From 864edc60c9fc85477298a4f6e178fec57025f066 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 4 Jul 2020 13:09:41 +0800 Subject: [PATCH 001/171] Update GlobalVar.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新论坛指路 --- Dice/GlobalVar.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 2c0b5003..70384416 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -309,9 +309,10 @@ std::map GlobalMsg .helpȺ 鿴Ⱥָ .help趨 ȷ趨 .help 鿴Դĵ +ٷ̳:https://forum.kokona.tech/ ٷ(ˮ)Ⱥ: 624807593 941980833 882747577 -ShikiȺ1029435374 -˽Ⱥ863062599)" +ShikiȺ: 1029435374 +˽Ⱥ: 863062599)" } }; @@ -324,7 +325,14 @@ const std::map HelpDoc = { }, { "", - "鿴Դ:https://github.com/mystringEmpty/Dice\n:https://github.com/mystringEmpty/Dice/releases\nûֲ:http://shiki.stringempty.xyz/download/Shiki_User_Manual.pdf\nֲ:http://shiki.stringempty.xyz/download/Shiki_Master_Manual.pdf\nֲ:http://shiki.stringempty.xyz/download/DiceMaid_CookBook.html\n(ĵ)https://dice.c-j.dev/\nst│:http://shiki.stringempty.xyz/download/COC7_player_card_shiki.xlsx" + R"(ٷ̳:https://forum.kokona.tech/ +鿴Դ:https://github.com/mystringEmpty/Dice +:https://github.com/mystringEmpty/Dice/releases +ûֲ:http://shiki.stringempty.xyz/download/Shiki_User_Manual.pdf +ֲ:http://shiki.stringempty.xyz/download/Shiki_Master_Manual.pdf +ֲ:http://shiki.stringempty.xyz/download/DiceMaid_CookBook.html +(ĵ)https://dice.c-j.dev/ +stCOC7│:http://shiki.stringempty.xyz/download/COC7_player_card_shiki.xlsx)" }, { "趨", From 6434de63e873a958f65b150c7142910effcaef32 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 23 Jul 2020 01:37:05 +0800 Subject: [PATCH 002/171] Merge Update --- Dice/BlackListManager.cpp | 900 +++++++++++++++++--------------------- Dice/BlackListManager.h | 2 +- Dice/Dice.cpp | 137 +++--- Dice/Dice.vcxproj | 4 + Dice/Dice.vcxproj.filters | 12 + Dice/DiceCloud.cpp | 1 - Dice/DiceConsole.cpp | 227 ++++------ Dice/DiceConsole.h | 79 ++-- Dice/DiceEvent.cpp | 895 +++++++++++++++---------------------- Dice/DiceEvent.h | 103 ++--- Dice/DiceFile.cpp | 3 +- Dice/DiceFile.hpp | 90 ++-- Dice/DiceJob.cpp | 323 ++++++++++++++ Dice/DiceJob.h | 24 + Dice/DiceMod.cpp | 34 +- Dice/DiceMod.h | 62 +-- Dice/DiceMsgSend.cpp | 7 +- Dice/DiceNetwork.cpp | 12 +- Dice/DiceSchedule.cpp | 179 ++++++++ Dice/DiceSchedule.h | 83 ++++ Dice/DiceSession.cpp | 61 ++- Dice/GlobalVar.cpp | 209 ++++----- Dice/Jsonio.h | 33 +- Dice/ManagerSystem.cpp | 141 +++--- Dice/ManagerSystem.h | 42 +- Dice/MsgFormat.cpp | 12 + Dice/MsgFormat.h | 58 +-- Dice/MsgMonitor.cpp | 9 +- Dice/MsgMonitor.h | 7 +- Dice/RD.cpp | 4 + Dice/StrExtern.hpp | 23 +- Dice/strExtern.cpp | 39 +- 32 files changed, 2088 insertions(+), 1727 deletions(-) create mode 100644 Dice/DiceJob.cpp create mode 100644 Dice/DiceJob.h create mode 100644 Dice/DiceSchedule.cpp create mode 100644 Dice/DiceSchedule.h diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 3de43428..92a22be7 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -27,7 +27,7 @@ using Factory = DDBlackMarkFactory; int isReliable(long long QQID) { if (!QQID)return 0; - if (QQID == console.master())return 5; + if (QQID == console.master())return 255; if (trustedQQ(QQID) > 2)return trustedQQ(QQID) - 1; if (mDiceList.count(QQID)) { @@ -370,14 +370,11 @@ bool DDBlackMark::isSource(long long qq) const { return DiceMaid == qq || masterQQ == qq; } - -bool DDBlackMark::is_remit() const -{ - if (fromGroup.first && groupset(fromGroup.first, "") > 0)return true; - if (fromQQ.first && trustedQQ(fromQQ.first) > 1)return true; - if (inviterQQ.first && trustedQQ(inviterQQ.first) > 1)return true; - if (ownerQQ.first && trustedQQ(ownerQQ.first) > 1)return true; - return false; +void DDBlackMark::check_remit(){ + if (fromGroup.first && groupset(fromGroup.first, "") > 0)erase(); + if (fromQQ.first && trustedQQ(fromQQ.first) > 1)erase(); + if (inviterQQ.first && trustedQQ(inviterQQ.first) > 1)inviterQQ.second = 0; + if (ownerQQ.first && trustedQQ(ownerQQ.first) > 1)ownerQQ.second = 0; } void DDBlackMark::erase() @@ -408,7 +405,13 @@ int DDBlackMark::check_cloud() console.log("Ƽ¼ʧ" + temp, 0); return -2; } - if (temp[0] == '+') + if (temp[0] == '?') + { + console.log("ƥ䵽δȷƼ¼:wid" + temp, 0); + wid = stoi(temp.substr(1)); + return 1; + } + else if (temp[0] == '+') { console.log("ƥ䵽δעƼ¼:wid" + temp, 0); wid = stoi(temp.substr(1)); @@ -423,352 +426,291 @@ int DDBlackMark::check_cloud() return -1; } -int DDBlackManager::find(const DDBlackMark& mark) -{ - std::lock_guard lock_queue(blacklistMutex); - unordered_set sRange; - if (mark.wid && mCloud.count(mark.wid)) return mCloud[mark.wid]; - if (!mark.time.empty()) - { - unordered_set sTimeRange = sTimeEmpty; - for (auto& [key,id] : multi_range(mTimeIndex, mark.time)) - { - sTimeRange.insert(id); - } - if (sRange.empty()) - { - sRange.swap(sTimeRange); - } - else - { - unordered_set sInter; +int DDBlackManager::find(const DDBlackMark& mark) { + std::lock_guard lock_queue(blacklistMutex); + unordered_set sRange; + if (mark.wid && mCloud.count(mark.wid)) return mCloud[mark.wid]; + if (mark.wid){ + if (mCloud.count(mark.wid)) return mCloud[mark.wid]; + sRange = sIDEmpty; + } + if (mark.time.length() == 19) { + unordered_set sTimeRange = sTimeEmpty; + for (auto &[key,id] : multi_range(mTimeIndex, mark.time)) { + sTimeRange.insert(id); + } + if (sRange.empty()) { + sRange.swap(sTimeRange); + } + else { + unordered_set sInter; - for (const auto& ele : sRange) - { - if (sTimeRange.count(ele)) - { - sInter.insert(ele); + for (const auto& ele : sRange) + { + if (sTimeRange.count(ele)) + { + sInter.insert(ele); } } - //std::set_intersection(sRange.begin(), sRange.end(), sTimeRange.begin(), sTimeRange.end(), std::inserter(sInter, sInter.begin())); - if (sInter.empty())return -1; - sRange.swap(sInter); - } - } - if (mark.fromGroup.first) - { - unordered_set sGroupRange = sGroupEmpty; - for (auto& [key, id] : multi_range(mGroupIndex, mark.fromGroup.first)) - { - sGroupRange.insert(id); - } - if (sGroupRange.empty())return -1; - if (sRange.empty()) - { - sRange.swap(sGroupRange); - } - else - { - unordered_set sInter; - - // O(n) - for (const auto& ele : sRange) - { - if (sGroupRange.count(ele)) - { - sInter.insert(ele); + //std::set_intersection(sRange.begin(), sRange.end(), sTimeRange.begin(), sTimeRange.end(), std::inserter(sInter, sInter.begin())); + if (sInter.empty())return -1; + sRange.swap(sInter); + } + } + if (mark.fromGroup.first) { + unordered_set sGroupRange = sGroupEmpty; + for (auto& [key, id] : multi_range(mGroupIndex, mark.fromGroup.first)) { + sGroupRange.insert(id); + } + if (sGroupRange.empty())return -1; + if (sRange.empty()) { + sRange.swap(sGroupRange); + } + else { + unordered_set sInter; + + // O(n) + for (const auto& ele : sRange) + { + if (sGroupRange.count(ele)) + { + sInter.insert(ele); } } - //std::set_intersection(sRange.begin(), sRange.end(), sGroupRange.begin(), sGroupRange.end(), std::inserter(sInter, sInter.begin())); - if (sInter.empty())return -1; - sRange.swap(sInter); - } - } - if (mark.fromQQ.first) - { - unordered_set sQQRange = sQQEmpty; - for (auto& [key, id] : multi_range(mQQIndex, mark.fromQQ.first)) - { - if (Enabled)console.log("ƥû¼" + to_string(id), 0); - sQQRange.insert(id); - } - if (sQQRange.empty())return -1; - if (sRange.empty()) - { - sRange.swap(sQQRange); - } - else - { - unordered_set sInter; + //std::set_intersection(sRange.begin(), sRange.end(), sGroupRange.begin(), sGroupRange.end(), std::inserter(sInter, sInter.begin())); + if (sInter.empty())return -1; + sRange.swap(sInter); + } + } + if (mark.fromQQ.first) { + unordered_set sQQRange = sQQEmpty; + for (auto& [key, id] : multi_range(mQQIndex, mark.fromQQ.first)) { + if (Enabled)console.log("ƥû¼" + to_string(id), 0); + sQQRange.insert(id); + } + if (sQQRange.empty())return -1; + if (sRange.empty()) { + sRange.swap(sQQRange); + } + else { + unordered_set sInter; - for (const auto& ele : sRange) - { - if (sQQRange.count(ele)) - { - sInter.insert(ele); + for (const auto& ele : sRange) + { + if (sQQRange.count(ele)) + { + sInter.insert(ele); } } - //std::set_intersection(sRange.begin(), sRange.end(), sQQRange.begin(), sQQRange.end(), std::inserter(sInter, sInter.begin())); - if (sInter.empty())return -1; - sRange.swap(sInter); - } - } - for (auto i : sRange) - { - if (vBlackList[i].isSame(mark))return i; - } - return -1; + //std::set_intersection(sRange.begin(), sRange.end(), sQQRange.begin(), sQQRange.end(), std::inserter(sInter, sInter.begin())); + if (sInter.empty())return -1; + sRange.swap(sInter); + } + } + for (auto i : sRange) { + if (vBlackList[i].isSame(mark))return i; + } + return -1; } -DDBlackMark& DDBlackMark::operator<<(const DDBlackMark& mark) -{ - // int delta_danger = mark.danger - danger; - if (type == "null" && mark.type != "null")type = mark.type; - if (time.empty() && !mark.time.empty()) - { - time = mark.time; - } - if (note != mark.note && - (note.empty() || count_char(note, '?') > count_char(mark.note, '?') || note.length() < mark.note.length()) - ) - { - note = mark.note; - } - if (mark.fromGroup.first) - { - fromGroup = mark.fromGroup; - } - if (mark.fromQQ.first) - { - fromQQ = mark.fromQQ; - } - if (mark.inviterQQ.first) - { - inviterQQ = mark.inviterQQ; - } - if (mark.ownerQQ.first) - { - ownerQQ = mark.ownerQQ; - } - if (!DiceMaid && mark.DiceMaid)DiceMaid = mark.DiceMaid; - if (!masterQQ && mark.masterQQ)masterQQ = mark.masterQQ; - //save comment if the mark changed at this update - if (!mark.comment.empty()) - { - comment = mark.comment; - } - return *this; +DDBlackMark& DDBlackMark::operator<<(const DDBlackMark& mark) { + // int delta_danger = mark.danger - danger; + if (type == "null" && mark.type != "null")type = mark.type; + if (time.empty() && !mark.time.empty()) { + time = mark.time; + } + if (note != mark.note && + (note.empty() || count_char(note, '?') > count_char(mark.note, '?') || note.length() < mark.note.length()) + ) { + note = mark.note; + } + if (mark.fromGroup.first) { + fromGroup = mark.fromGroup; + } + if (mark.fromQQ.first) { + fromQQ = mark.fromQQ; + } + if (mark.inviterQQ.first) { + inviterQQ = mark.inviterQQ; + } + if (mark.ownerQQ.first) { + ownerQQ = mark.ownerQQ; + } + if (!DiceMaid && mark.DiceMaid)DiceMaid = mark.DiceMaid; + if (!masterQQ && mark.masterQQ)masterQQ = mark.masterQQ; + //save comment if the mark changed at this update + if (!mark.comment.empty()) { + comment = mark.comment; + } + return *this; } -void DDBlackManager::insert(DDBlackMark& ex_mark) -{ - std::lock_guard lock_queue(blacklistMutex); - unsigned id = vBlackList.size(); - vBlackList.push_back(ex_mark); - DDBlackMark& mark(vBlackList[id]); - if (mark.wid)mCloud[mark.wid] = id; - else sIDEmpty.insert(id); - if (mark.time.empty())sTimeEmpty.insert(id); - else mTimeIndex.emplace(mark.time, id); - if (mark.fromGroup.first) - { - mGroupIndex.emplace(mark.fromGroup.first, id); - if (mark.fromGroup.second) - { - up_group_danger(mark.fromGroup.first, mark); - } - } - else - { - sGroupEmpty.insert(id); - } - if (mark.fromQQ.first) - { - mQQIndex.emplace(mark.fromQQ.first, id); - if (mark.fromQQ.second) - { - up_qq_danger(mark.fromQQ.first, mark); - } - } - else - { - sQQEmpty.insert(id); - } - if (mark.inviterQQ.first) - { - mQQIndex.emplace(mark.inviterQQ.first, id); - if (mark.inviterQQ.second) - { - up_qq_danger(mark.inviterQQ.first, mark); - } - } - if (mark.ownerQQ.first) - { - mQQIndex.emplace(mark.ownerQQ.first, id); - if (mark.ownerQQ.second) - { - up_qq_danger(mark.ownerQQ.first, mark); - } - } - if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); +void DDBlackManager::insert(DDBlackMark& ex_mark) { + std::lock_guard lock_queue(blacklistMutex); + unsigned id = vBlackList.size(); + vBlackList.push_back(ex_mark); + DDBlackMark& mark(vBlackList[id]); + if (mark.wid)mCloud[mark.wid] = id; + else sIDEmpty.insert(id); + if (mark.time.length() == 19)mTimeIndex.emplace(mark.time, id); + else sTimeEmpty.insert(id); + if (mark.fromGroup.first) { + mGroupIndex.emplace(mark.fromGroup.first, id); + if (mark.fromGroup.second) { + up_group_danger(mark.fromGroup.first, mark); + } + } + else { + sGroupEmpty.insert(id); + } + if (mark.fromQQ.first) { + mQQIndex.emplace(mark.fromQQ.first, id); + if (mark.fromQQ.second) { + up_qq_danger(mark.fromQQ.first, mark); + } + } + else { + sQQEmpty.insert(id); + } + if (mark.inviterQQ.first) { + mQQIndex.emplace(mark.inviterQQ.first, id); + if (mark.inviterQQ.second) { + up_qq_danger(mark.inviterQQ.first, mark); + } + } + if (mark.ownerQQ.first) { + mQQIndex.emplace(mark.ownerQQ.first, id); + if (mark.ownerQQ.second) { + up_qq_danger(mark.ownerQQ.first, mark); + } + } + if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); } - -bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) -{ - std::lock_guard lock_queue(blacklistMutex); - DDBlackMark& old_mark = vBlackList[id]; - int delta_danger = mark.danger - old_mark.danger; - bool isUpdated = false; - if (delta_danger) - { - old_mark.danger = mark.danger; - isUpdated = true; - } - if (mark.wid && !old_mark.wid) - { - sIDEmpty.erase(id); - old_mark.wid = mark.wid; - mCloud.emplace(old_mark.wid, id); - isUpdated = true; - } - if (old_mark.type == "null" && mark.type != "null")old_mark.type = mark.type; - if (old_mark.time.length() < mark.time.length()) - { - old_mark.time = mark.time; - sTimeEmpty.erase(id); - mTimeIndex.emplace(old_mark.time, id); - } - if (old_mark.note != mark.note && - (old_mark.note.empty() || count_char(old_mark.note, '?') > count_char(mark.note, '?') || old_mark.note.length() - < mark.note.length()) - ) - { - old_mark.note = mark.note; - } - if (mark.fromGroup.first) - { - if (!old_mark.fromGroup.first) - { - sGroupEmpty.erase(id); - mGroupIndex.emplace(mark.fromGroup.first, id); - if (mark.fromGroup.second) - { - up_group_danger(mark.fromGroup.first, mark); - } - old_mark.fromGroup = mark.fromGroup; - isUpdated = true; - } - else if (mark.fromGroup.second != old_mark.fromGroup.second) - { - if (old_mark.fromGroup.second) - { - old_mark.fromGroup.second = false; - reset_group_danger(mark.fromGroup.first); - isUpdated = true; - } - else if (delta_danger > 0 || credit > 2) - { - old_mark.fromGroup.second = true; - up_group_danger(mark.fromGroup.first, mark); - isUpdated = true; - } - } - } - if (mark.fromQQ.first) - { - if (!old_mark.fromQQ.first) - { - sQQEmpty.erase(id); - mQQIndex.emplace(mark.fromQQ.first, id); - old_mark.fromQQ = mark.fromQQ; - if (mark.fromQQ.second) - { - up_qq_danger(mark.fromQQ.first, mark); - } - isUpdated = true; - } - else if (mark.fromQQ.second != old_mark.fromQQ.second) - { - if (old_mark.fromQQ.second) - { - old_mark.fromQQ.second = false; - reset_qq_danger(mark.fromQQ.first); - isUpdated = true; - } - else if (delta_danger > 0 || credit > 2) - { - old_mark.fromQQ.second = true; - up_qq_danger(mark.fromQQ.first, mark); - isUpdated = true; - } - } - } - if (mark.inviterQQ.first) - { - if (!old_mark.inviterQQ.first) - { - mQQIndex.emplace(mark.inviterQQ.first, id); - old_mark.inviterQQ = mark.inviterQQ; - if (mark.inviterQQ.second) - { - up_qq_danger(mark.inviterQQ.first, mark); - } - isUpdated = true; - } - else if (mark.inviterQQ.second != old_mark.inviterQQ.second) - { - if (old_mark.inviterQQ.second) - { - old_mark.inviterQQ.second = false; - reset_qq_danger(mark.inviterQQ.first); - isUpdated = true; - } - else if (delta_danger > 0 || credit > 2) - { - old_mark.inviterQQ.second = true; - up_qq_danger(mark.inviterQQ.first, mark); - isUpdated = true; - } - } - } - if (mark.ownerQQ.first) - { - if (!old_mark.ownerQQ.first) - { - sQQEmpty.erase(id); - mQQIndex.emplace(mark.ownerQQ.first, id); - old_mark.ownerQQ = mark.ownerQQ; - if (mark.ownerQQ.second) - { - up_qq_danger(mark.ownerQQ.first, mark); - } - isUpdated = true; - } - else if (mark.ownerQQ.second != old_mark.ownerQQ.second) - { - if (old_mark.ownerQQ.second) - { - old_mark.ownerQQ.second = false; - reset_qq_danger(mark.ownerQQ.first); - isUpdated = true; - } - else if (delta_danger > 0 || credit > 2) - { - old_mark.ownerQQ.second = true; - up_qq_danger(mark.ownerQQ.first, mark); - isUpdated = true; - } - } - } - if (!old_mark.DiceMaid && mark.DiceMaid)old_mark.DiceMaid = mark.DiceMaid; - if (!old_mark.masterQQ && mark.masterQQ)old_mark.masterQQ = mark.masterQQ; - //save comment if the mark changed at this update - if (isUpdated) - { - if (!mark.comment.empty())old_mark.comment = mark.comment; - if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); - } - return isUpdated; +bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) { + std::lock_guard lock_queue(blacklistMutex); + DDBlackMark& old_mark = vBlackList[id]; + int delta_danger = mark.danger - old_mark.danger; + bool isUpdated = false; + if (delta_danger) { + old_mark.danger = mark.danger; + isUpdated = true; + } + if (mark.wid && !old_mark.wid) { + sIDEmpty.erase(id); + old_mark.wid = mark.wid; + mCloud.emplace(old_mark.wid, id); + isUpdated = true; + } + if (old_mark.type == "null" && mark.type != "null")old_mark.type = mark.type; + if (old_mark.time.length() < mark.time.length() && mark.time.length() <= 19) { + old_mark.time = mark.time; + sTimeEmpty.erase(id); + if (mark.time.length() == 19)mTimeIndex.emplace(old_mark.time, id); + } + if (old_mark.note != mark.note && + (old_mark.note.empty() || count_char(old_mark.note, '?') > count_char(mark.note, '?') || old_mark.note.length() < mark.note.length()) + ) { + old_mark.note = mark.note; + } + if (mark.fromGroup.first) { + if (!old_mark.fromGroup.first) { + sGroupEmpty.erase(id); + mGroupIndex.emplace(mark.fromGroup.first, id); + if (mark.fromGroup.second) { + up_group_danger(mark.fromGroup.first, mark); + } + old_mark.fromGroup = mark.fromGroup; + isUpdated = true; + } + else if (mark.fromGroup.second != old_mark.fromGroup.second) { + if (old_mark.fromGroup.second) { + old_mark.fromGroup.second = false; + reset_group_danger(mark.fromGroup.first); + isUpdated = true; + } + else if (delta_danger > 0 || credit > 2){ + old_mark.fromGroup.second = true; + up_group_danger(mark.fromGroup.first, mark); + isUpdated = true; + } + } + } + if (mark.fromQQ.first) { + if (!old_mark.fromQQ.first) { + sQQEmpty.erase(id); + mQQIndex.emplace(mark.fromQQ.first, id); + old_mark.fromQQ = mark.fromQQ; + if (mark.fromQQ.second) { + up_qq_danger(mark.fromQQ.first, mark); + } + isUpdated = true; + } + else if (mark.fromQQ.second != old_mark.fromQQ.second) { + if (old_mark.fromQQ.second) { + old_mark.fromQQ.second = false; + reset_qq_danger(mark.fromQQ.first); + isUpdated = true; + } + else if (delta_danger > 0 || credit > 2) { + old_mark.fromQQ.second = true; + up_qq_danger(mark.fromQQ.first, mark); + isUpdated = true; + } + } + } + if (mark.inviterQQ.first) { + if (!old_mark.inviterQQ.first) { + mQQIndex.emplace(mark.inviterQQ.first, id); + old_mark.inviterQQ = mark.inviterQQ; + if (mark.inviterQQ.second) { + up_qq_danger(mark.inviterQQ.first, mark); + } + isUpdated = true; + } + else if (mark.inviterQQ.second != old_mark.inviterQQ.second) { + if (old_mark.inviterQQ.second) { + old_mark.inviterQQ.second = false; + reset_qq_danger(mark.inviterQQ.first); + isUpdated = true; + } + else if (delta_danger > 0 || credit > 2) { + old_mark.inviterQQ.second = true; + up_qq_danger(mark.inviterQQ.first, mark); + isUpdated = true; + } + } + } + if (mark.ownerQQ.first) { + if (!old_mark.ownerQQ.first) { + sQQEmpty.erase(id); + mQQIndex.emplace(mark.ownerQQ.first, id); + old_mark.ownerQQ = mark.ownerQQ; + if (mark.ownerQQ.second) { + up_qq_danger(mark.ownerQQ.first, mark); + } + isUpdated = true; + } + else if (mark.ownerQQ.second != old_mark.ownerQQ.second) { + if (old_mark.ownerQQ.second) { + old_mark.ownerQQ.second = false; + reset_qq_danger(mark.ownerQQ.first); + isUpdated = true; + } + else if (delta_danger > 0 || credit > 2) { + old_mark.ownerQQ.second = true; + up_qq_danger(mark.ownerQQ.first, mark); + isUpdated = true; + } + } + } + if (!old_mark.DiceMaid && mark.DiceMaid)old_mark.DiceMaid = mark.DiceMaid; + if (!old_mark.masterQQ && mark.masterQQ)old_mark.masterQQ = mark.masterQQ; + //save comment if the mark changed at this update + if (isUpdated) { + if (!mark.comment.empty())old_mark.comment = mark.comment; + if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + } + return isUpdated; } void DDBlackManager::reset_group_danger(long long llgroup) @@ -1060,161 +1002,129 @@ void DDBlackManager::add_black_qq(long long llqq, FromMsg* msg) insert(mark); msg->note("" + printQQ(llqq) + "ıغ¼"); } - -void DDBlackManager::verify(void* pJson, long long operateQQ) -{ - DDBlackMark mark{pJson}; - if (!mark.isValid)return; - int credit = isReliable(operateQQ); - //ݿǷм¼:-1=;0=ע;1=δע; - int is_cloud = -1; - if (console["CloudBlackShare"]) - { - if (mark.wid) - { - string strInfo; - if ((is_cloud = getCloudBlackMark(mark.wid, strInfo)) > -1) - { - try - { - json j = json::parse(strInfo); - if (j["isErased"].get())is_cloud = 0; - if (mark.fromQQ.first) - { - if (mark.fromQQ.first != j["fromQQ"].get())return; - } - else - { - if (!mark.isClear)mark.fromQQ = {j["fromQQ"].get(), true}; - } - if (mark.fromGroup.first) - { - if (mark.fromGroup.first != j["fromGroup"].get())return; - } - else - { - if (!mark.isClear)mark.fromGroup = {j["fromGroup"].get(), true}; - } - mark.DiceMaid = j["DiceMaid"].get(); - mark.masterQQ = j["masterQQ"].get(); - mark.type = j["type"].get(); - mark.time = j["time"].get(); - if (mark.note.empty()) - { - if (j.count("note") && !j["note"].get().empty())mark.note = UTF8toGBK( - j["note"].get()); - else mark.fill_note(); - } - if (credit < 3 || !mark.danger)mark.danger = 2; - } - catch (...) - { - console.log("ƶͬʧ:wid=" + to_string(mark.wid), 0); - //mark.wid = 0; - } - } - else - { - console.log("ƶ˺ʧ:wid=" + to_string(mark.wid), 0); - mark.wid = 0; - } - } - else - { - is_cloud = mark.check_cloud(); - } - } - else - { - mark.wid = 0; - } - if (credit < 3) - { - if ((mark.isType("kick") && !console["ListenGroupKick"]) || (mark.isType("ban") && !console["ListenGroupBan"]) - || (mark.isType("spam") && !console["ListenSpam"]))return; - if (mark.type == "local" || mark.type == "other") - { - if (credit > 0)console.log( - getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼(δ):\n!warning" + UTF8toGBK( - static_cast(pJson)->dump()), 1, printSTNow()); - return; - } - } - else if (credit < 5 && mark.danger > credit) mark.danger = credit; - if (!mark.danger)mark.danger = 2; - if (mark.is_remit())mark.erase(); - int index = find(mark); - //¼¼ - if (index < 0) - { - if (!mark.isType())return; - if (credit < 0)return; - if (is_cloud < 0) - { - if (mark.type == "ruler" || credit == 0)return; - } - else if (is_cloud == 0) - { - if (!mark.isClear && credit < 3)mark.erase(); - } - if (credit < 3) - { - if (is_cloud < 1 && mark.type == "extern")return; - if (mark.type != "ruler") - { - if (mark.danger > 2)mark.danger = 2; - } - else - { - if (mark.danger > 3)mark.danger = 3; - } - } - else - { - if (mark.type == "local" && credit < 4)return; - } - if (get_qq_danger(mark.DiceMaid) || get_qq_danger(mark.masterQQ))return; - if (mark.fromGroup.first && groupset(mark.fromGroup.first, "") > 0)return; - insert(mark); - console.log( - getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼" + to_string(vBlackList.size() - 1) + - ":\n!warning" + UTF8toGBK(static_cast(pJson)->dump()), 1, printSTNow()); - } - else - { - //м¼ - DDBlackMark& old_mark = vBlackList[index]; - bool isSource = operateQQ == old_mark.DiceMaid || operateQQ == old_mark.masterQQ; - if (credit < 1) - { - if (old_mark.danger != 2)return; - if (is_cloud < 0 && !isSource)return; - if (is_cloud && (old_mark.isClear || mark.isClear))return; - mark.inviterQQ = {0, false}; - mark.ownerQQ = {0, false}; - } - else if (credit < 3) - { - if (old_mark.danger > 2)return; - if (mark.danger > 2)mark.danger = 2; - } - if (mark.danger != old_mark.danger) - { - if (credit < 3)return; - if (credit < 5 && credit < old_mark.danger)return; - } - if (update(mark, index, credit))console.log( - getName(operateQQ) + "Ѹ" + GlobalMsg["strSelfName"] + "¼" + to_string(index) + ":\n!warning" + - UTF8toGBK(static_cast(pJson)->dump()), 1, printSTNow()); - } +void DDBlackManager::verify(void* pJson, long long operateQQ) { + DDBlackMark mark{ pJson }; + if (!mark.isValid)return; + int credit = isReliable(operateQQ); + //ݿǷм¼:-1=;0=ע;1=δȷ;2=ȷ; + int is_cloud = -1; + if (console["CloudBlackShare"]) { + if (mark.wid) { + string strInfo; + if ((is_cloud = getCloudBlackMark(mark.wid, strInfo)) > -1) { + try { + nlohmann::json j = nlohmann::json::parse(strInfo); + if (j["isErased"].get())is_cloud = 0; + else if (j["isCheck"].get())is_cloud = 2; + if (mark.fromQQ.first) { + if (mark.fromQQ.first != j["fromQQ"].get())return; + } + else { + if (!mark.isClear)mark.fromQQ = { j["fromQQ"].get() ,true }; + } + if (mark.fromGroup.first) { + if (mark.fromGroup.first != j["fromGroup"].get())return; + } + else { + if (!mark.isClear)mark.fromGroup = { j["fromGroup"].get() ,true }; + } + mark.DiceMaid = j["DiceMaid"].get(); + mark.masterQQ = j["masterQQ"].get(); + mark.type = j["type"].get(); + mark.time = j["time"].get(); + if (mark.note.empty()) { + if (j.count("note") && !j["note"].get().empty())mark.note = UTF8toGBK(j["note"].get()); + else mark.fill_note(); + } + if (credit < 3 || !mark.danger)mark.danger = 2; + } + catch (...) { + console.log("ƶͬʧ:wid=" + to_string(mark.wid), 0); + //mark.wid = 0; + } + } + else { + console.log("ƶ˺ʧ:wid=" + to_string(mark.wid), 0); + mark.wid = 0; + } + } + else { + is_cloud = mark.check_cloud(); + } + } + else{ + mark.wid = 0; + } + if (credit < 3) { + if ((mark.isType("kick") && !console["ListenGroupKick"]) || (mark.isType("ban") && !console["ListenGroupBan"]) || (mark.isType("spam") && !console["ListenSpam"]))return; + if (mark.type == "local" || mark.type == "other" || mark.isSource(console.DiceMaid)) { + if (credit > 0)console.log(getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼(δ):\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); + return; + } + } + else if (credit < 5 && mark.danger > credit) mark.danger = credit; + if (!mark.danger || credit < 3)mark.danger = (mark.type == "ruler" ? 3 : 2); + mark.check_remit(); + int index = find(mark); + //¼¼ + if (index < 0) { + //߻һк + if (credit < 0 || get_qq_danger(mark.DiceMaid) || get_qq_danger(mark.masterQQ))return; + if (!mark.isType())return; + //ƶȷϼ¼Ҳ + if (is_cloud < 0 || is_cloud == 1) { + if (mark.type == "ruler" || credit == 0)return; + } + //ƶעͬע + else if(is_cloud == 0){ + if (!mark.isClear && credit < 3)mark.erase(); + } + if (credit < 3) { + if (is_cloud < 1 && mark.type == "extern")return; + } + else { + if (mark.type == "local" && credit < 4)return; + } + if (mark.fromGroup.first && (groupset(mark.fromGroup.first, "") > 0 || groupset(mark.fromGroup.first, "ЭЧ") > 0 || ExceptGroups.count(mark.fromGroup.first)))return; + insert(mark); + console.log(getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼" + to_string(vBlackList.size() - 1) + ":\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); + } + else { //м¼ + DDBlackMark& old_mark = vBlackList[index]; + bool isSource = operateQQ == old_mark.DiceMaid || operateQQ == old_mark.masterQQ; + //ΣյȼȨ޸ + if (old_mark.danger > credit && credit < 255) { + if (old_mark.danger != 2)return; + //Ȩ + if (isSource) { + mark.fromQQ.second &= old_mark.fromQQ.second; + mark.fromGroup.second &= old_mark.fromGroup.second; + } + //ͬƶע + else if (!is_cloud) { + mark.erase(); + } + else return; + } + // + if (credit < 1) { + mark.inviterQQ = { 0,false }; + mark.ownerQQ = { 0,false }; + } + //ȨΣյȼ + if (mark.danger != old_mark.danger && credit < 3) { + mark.danger = old_mark.danger; + } + if(update(mark,index,credit))console.log(getName(operateQQ) + "Ѹ" + GlobalMsg["strSelfName"] + "¼" + to_string(index) + ":\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); + } } -void DDBlackManager::create(DDBlackMark& mark) +void DDBlackManager::create(DDBlackMark& mark) { - if (mark.is_remit())return; - if (console["CloudBlackShare"] && mark.isSource(console.DiceMaid))mark.upload(); - console.log(mark.warning(), 0b100000); - insert(mark); + if (mark.check_remit(); mark.isClear)return; + if (console["CloudBlackShare"] && mark.isSource(console.DiceMaid))mark.upload(); + console.log(mark.warning(), 0b100000); + insert(mark); } int DDBlackManager::loadJson(string strPath) diff --git a/Dice/BlackListManager.h b/Dice/BlackListManager.h index bc15314a..3cebd799 100644 --- a/Dice/BlackListManager.h +++ b/Dice/BlackListManager.h @@ -70,7 +70,7 @@ class DDBlackMark [[nodiscard]] bool isType(const string& strType) const; [[nodiscard]] bool isSame(const DDBlackMark&) const; [[nodiscard]] bool isSource(long long) const; - [[nodiscard]] bool is_remit() const; + void check_remit(); DDBlackMark& operator<<(const DDBlackMark&); //bool operator<(const DDBlackMark&)const; }; diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 11ff63c5..5ffcef91 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -81,9 +81,6 @@ void loadData() loadJMap(strFileLoc + "ExternDeck.json", CardDeck::mExternPublicDeck); } map_merge(CardDeck::mPublicDeck, CardDeck::mExternPublicDeck); - fmt->set_help("չƶ", listKey(CardDeck::mExternPublicDeck)); - fmt->set_help("ȫƶб", listKey(CardDeck::mPublicDeck)); - fmt->set_help("master", printQQ(console.master())); //ȡĵ fmt->load(strLog); if (int cnt; (cnt = loadJMap(DiceDir + "\\conf\\CustomHelp.json", CustomHelp)) < 0) @@ -144,6 +141,7 @@ void dataInit() console.log("ʼԹȹ¼" + to_string(gm->mSession.size()) + "", 1); gm->save(); } + today = make_unique(DiceDir + "/user/DiceToday.json"); } // @@ -228,8 +226,7 @@ EVE_Enable(eventEnable) { console.set(key, val); } - console.setClock({4, 4}, ClockEvent::save); - console.setClock({5, 5}, ClockEvent::clear); + console.setClock({ 5, 5 }, ClockEvent::clear); console.loadNotice(); console.save(); } @@ -256,7 +253,8 @@ EVE_Enable(eventEnable) string name; while (ifName >> GroupID >> QQ >> name) { - getUser(QQ).create(NEWYEAR).setNick(GroupID, base64_decode(name)); + name = base64_decode(name); + getUser(QQ).create(NEWYEAR).setNick(GroupID, name); } } } @@ -368,12 +366,10 @@ EVE_Enable(eventEnable) if (loadFile(DiceDir + "\\user\\ChatList.txt", ChatList) < 1) { map mLastMsgList; - for (auto it : mLastMsgList) + for (auto it : mLastMsgList) { if (it.first.second == msgtype::Private)getUser(it.first.first).create(it.second); - else chat(it.first.first).create(it.second).lastmsg(it.second).isGroup = 2 - static_cast(it - .first.second - ); + else chat(it.first.first).create(it.second).lastmsg(it.second).isGroup = 2 - int(it.first.second); } std::map mGroupInviter; if (loadFile(strFileLoc + "GroupInviter.RDconf", mGroupInviter) < 1) @@ -443,11 +439,11 @@ EVE_Enable(eventEnable) threads(ConsoleTimer); threads(warningHandler); threads(frqHandler); + sch.start(); // getDiceList(); - Cloud::update(); - console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, - printSTNow()); + getExceptGroup(); + console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, printSTNow()); llStartTime = clock(); return 0; } @@ -462,14 +458,19 @@ bool eve_GroupAdd(Chat& grp) else if (time(nullptr) - grp.tCreated > 1)return false; lock_queue.unlock(); } - if (!console["ListenGroupAdd"] || grp.isset(""))return false; + GroupInfo ginf(grp.ID); + grp.Name = ginf.strGroupName; + if (grp.boolConf.empty() && ginf.nGroupSize > 499) { + grp.set("ЭЧ"); + } + if (!console["ListenGroupAdd"] || grp.isset(""))return 0; long long fromGroup = grp.ID; string strNow = printSTNow(); string strMsg(GlobalMsg["strSelfName"]); - try + try { - strMsg += "¼" + GroupInfo(fromGroup).tostring(); - if (blacklist->get_group_danger(fromGroup)) + strMsg += "¼:" + ginf.tostring(); + if (blacklist->get_group_danger(fromGroup)) { grp.leave(blacklist->list_group_warning(fromGroup)); strMsg += "ΪȺȺ"; @@ -477,28 +478,34 @@ bool eve_GroupAdd(Chat& grp) return true; } if (grp.isset("ʹ"))strMsg += "ѻʹɣ"; - if (grp.inviter) - { + else if(grp.isset("ЭЧ"))strMsg += "ĬЭЧ"; + if (grp.inviter) { strMsg += "," + printQQ(chat(fromGroup).inviter); } console.log(strMsg, 0, strNow); int max_trust = 0; + float ave_trust(0); //int max_danger = 0; + long long ownerQQ = 0; ResList blacks; std::vector list = getGroupMemberList(fromGroup); if (list.empty()) { strMsg += "ȺԱδأ"; } - else + else { - long long ownerQQ = 0; - - for (auto& each : list) + int cntUser(0), cntMember(0); + for (auto& each : list) { if (each.QQID == console.DiceMaid)continue; - if (each.permissions > 1) + cntMember++; + if (UserList.count(each.QQID)) { + cntUser++; + ave_trust += getUser(each.QQID).nTrust; + } + if (each.permissions > 1) { max_trust |= (1 << trustedQQ(each.QQID)); if (blacklist->get_qq_danger(each.QQID) > 1) { @@ -519,14 +526,18 @@ bool eve_GroupAdd(Chat& grp) if (each.permissions == 3) { ownerQQ = each.QQID; + ave_trust += ginf.nGroupSize * trustedQQ(each.QQID); strMsg += "Ⱥ" + printQQ(each.QQID) + ""; } + else { + ave_trust += ginf.nGroupSize * trustedQQ(each.QQID) / 10; + } } else if (blacklist->get_qq_danger(each.QQID) > 1) { //max_trust |= 1; blacks << printQQ(each.QQID); - if (blacklist->get_qq_danger(each.QQID)) + if (blacklist->get_qq_danger(each.QQID)) { AddMsgToQueue(blacklist->list_self_qq_warning(each.QQID), fromGroup, msgtype::Group); } @@ -537,6 +548,15 @@ bool eve_GroupAdd(Chat& grp) chat(fromGroup).inviter = ownerQQ; strMsg += "" + printQQ(ownerQQ); } + if (!cntMember) + { + strMsg += "ȺԱδأ"; + } + else + { + ave_trust /= cntMember; + strMsg += "\nûŨ" + to_string(cntUser * 100 / cntMember) + "%, ζ" + toString(ave_trust); + } } if (!blacks.empty()) { @@ -544,16 +564,15 @@ bool eve_GroupAdd(Chat& grp) strMsg += strNote; } if (console["Private"] && !grp.isset("ʹ")) - { - //СȺƹûϰ - if (max_trust > 1) + { //СȺƹûϰ + if (max_trust > 1 || ave_trust > 0.5) { grp.set("ʹ"); - strMsg += "Զ׷ʹ"; + strMsg += "\nԶ׷ʹ"; } - else + else { - strMsg += "ʹãȺ"; + strMsg += "\nʹãȺ"; console.log(strMsg, 1, strNow); grp.leave(getMsg("strPreserve")); return true; @@ -567,16 +586,17 @@ bool eve_GroupAdd(Chat& grp) console.log(strMsg + "\nȺ" + to_string(fromGroup) + "Ϣȡʧܣ", 0b1, printSTNow()); return true; } + if (grp.isset("ЭЧ")) return 0; if (!GlobalMsg["strAddGroup"].empty()) { this_thread::sleep_for(2s); - AddMsgToQueue(getMsg("strAddGroup"), {fromGroup, msgtype::Group}); + AddMsgToQueue(getMsg("strAddGroup"), { fromGroup, msgtype::Group }); } if (console["CheckGroupLicense"] && !grp.isset("ʹ")) { grp.set("δ"); this_thread::sleep_for(2s); - AddMsgToQueue(getMsg("strAddGroupNoLicense"), {fromGroup, msgtype::Group}); + AddMsgToQueue(getMsg("strGroupLicenseDeny"), { fromGroup, msgtype::Group }); } return false; } @@ -622,7 +642,7 @@ EVE_DiscussMsg_EX(eventDiscussMsg) if (blacklist->get_qq_danger(eve.fromQQ) && console["AutoClearBlack"]) { const string strMsg = "ֺû" + printQQ(eve.fromQQ) + "ԶִȺ"; - console.log(printChat({eve.fromDiscuss, msgtype::Discuss}) + strMsg, 0b10, printSTNow()); + console.log(printChat({ eve.fromDiscuss, msgtype::Discuss }) + strMsg, 0b10, printSTNow()); grp.leave(strMsg); return; } @@ -703,15 +723,14 @@ EVE_System_GroupMemberDecrease(eventGroupMemberDecrease) if (beingOperateQQ == console.DiceMaid) { grp.set(""); - if (!console || trustedQQ(fromQQ) > 1 || grp.isset(""))return 0; + if (!console || grp.isset(""))return 0; string strNow = printSTime(stNow); - string strNote = printQQ(fromQQ) + "" + printQQ(beingOperateQQ) + "ƳȺ" + to_string(fromGroup); + string strNote = printQQ(fromQQ) + "" + printQQ(beingOperateQQ) + "Ƴ" + printChat(grp); console.log(strNote, 0b1000, strNow); - if (!console["ListenGroupKick"] || trustedQQ(fromQQ) > 1 || grp.isset(""))return 0; - DDBlackMarkFactory mark{fromQQ, fromGroup}; - mark.sign().type("kick").time(strNow).note(strNow + " " + strNote); - if (grp.inviter && trustedQQ(grp.inviter) < 2) - { + if (!console["ListenGroupKick"] || trustedQQ(fromQQ) > 1 || grp.isset("") || grp.isset("ЭЧ") || ExceptGroups.count(fromGroup)) return 0; + DDBlackMarkFactory mark{ fromQQ ,fromGroup }; + mark.sign().type("kick").time(strNow).note(strNow + " " + strNote).comment(getMsg("strSelfCall") + "ԭ¼"); + if (grp.inviter && trustedQQ(grp.inviter) < 2) { strNote += ";Ⱥߣ" + printQQ(grp.inviter); if (console["KickedBanInviter"])mark.inviterQQ(grp.inviter).note(strNow + " " + strNote); } @@ -722,12 +741,11 @@ EVE_System_GroupMemberDecrease(eventGroupMemberDecrease) { if (!console || grp.isset(""))return 0; string strNow = printSTime(stNow); - string strNote = printQQ(fromQQ) + "" + printQQ(beingOperateQQ) + "ƳȺ" + to_string(fromGroup); + string strNote = printQQ(fromQQ) + "" + printQQ(beingOperateQQ) + "Ƴ" + printChat(grp); console.log(strNote, 0b1000, strNow); - if (trustedQQ(fromQQ) > 1 || grp.isset(""))return 0; - DDBlackMarkFactory mark{fromQQ, fromGroup}; - mark.type("kick").time(strNow).note(strNow + " " + strNote).DiceMaid(beingOperateQQ).masterQQ( - mDiceList[beingOperateQQ]); + if (trustedQQ(fromQQ) > 1 || grp.isset("") || grp.isset("ЭЧ") || ExceptGroups.count(fromGroup)) return 0; + DDBlackMarkFactory mark{ fromQQ ,fromGroup }; + mark.type("kick").time(strNow).note(strNow + " " + strNote).DiceMaid(beingOperateQQ).masterQQ(mDiceList[beingOperateQQ]).comment(strNow + " " + printQQ(console.DiceMaid) + "Ŀ"); grp.reset("ʹ").reset(""); blacklist->create(mark.product()); } @@ -751,9 +769,8 @@ EVE_System_GroupBan(eventGroupBan) { string strNow = printSTNow(); long long llOwner = 0; - string strNote = "" + printGroup(fromGroup) + "," + printQQ(beingOperateQQ) + "" + printQQ(fromQQ) + "" + - to_string(duration) + ""; - if (trustedQQ(fromQQ) > 1 || grp.isset("")) + string strNote = "" + printGroup(fromGroup) + "," + printQQ(beingOperateQQ) + "" + printQQ(fromQQ) + "" + printDuringTime(duration); + if (!console["ListenGroupBan"] || trustedQQ(fromQQ) > 1 || grp.isset("") || grp.isset("ЭЧ") || ExceptGroups.count(fromGroup)) { console.log(strNote, 0b10, strNow); return 1; @@ -764,11 +781,10 @@ EVE_System_GroupBan(eventGroupBan) if (beingOperateQQ == console.DiceMaid) { if (!console)return 0; - mark.sign(); + mark.sign().comment(getMsg("strSelfCall") + "ԭ¼"); } - else - { - mark.DiceMaid(beingOperateQQ).masterQQ(mDiceList[beingOperateQQ]); + else { + mark.DiceMaid(beingOperateQQ).masterQQ(mDiceList[beingOperateQQ]).comment(strNow + " " + printQQ(console.DiceMaid) + "Ŀ"); } //ͳȺڹ int intAuthCnt = 0; @@ -808,10 +824,9 @@ EVE_Request_AddGroup(eventGroupInvited) if (subType == 2 && groupset(fromGroup, "") < 1) { this_thread::sleep_for(3s); - const string strNow = printSTNow(); - string strMsg = "Ⱥԣ" + getStrangerInfo(fromQQ).nick + "(" + to_string(fromQQ) + "),Ⱥ:" + - to_string(fromGroup) + ""; - if (blacklist->get_group_danger(fromGroup)) + string strNow = printSTNow(); + string strMsg = "Ⱥԣ" + printQQ(fromQQ) + ",Ⱥ:" + to_string(fromGroup) + ""; + if (blacklist->get_group_danger(fromGroup)) { strMsg += "\nѾܾȺںУ"; console.log(strMsg, 0b10, strNow); @@ -823,11 +838,10 @@ EVE_Request_AddGroup(eventGroupInvited) console.log(strMsg, 0b10, strNow); setGroupAddRequest(responseFlag, 2, 2, ""); } - else if (Chat& grp = chat(fromGroup).group(); grp.isset("ʹ")) - { - grp.set("ʹ").set("δ"); + else if (Chat& grp = chat(fromGroup).group(); grp.isset("ʹ")) { + grp.set("δ"); grp.inviter = fromQQ; - strMsg += "\nͬ⣨Ⱥʹã" + grp.listBoolConf(); + strMsg += "\nͬ⣨Ⱥʹã"; console.log(strMsg, 1, strNow); setGroupAddRequest(responseFlag, 2, 1, ""); } @@ -887,6 +901,7 @@ EVE_Disable(eventDisable) Enabled = false; threads = {}; dataBackUp(); + sch.end(); fmt.reset(); gm.reset(); PList.clear(); diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index 90bedd84..f4121b6b 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -252,10 +252,12 @@ + + @@ -301,10 +303,12 @@ + + diff --git a/Dice/Dice.vcxproj.filters b/Dice/Dice.vcxproj.filters index 66a4e30a..d3cd2b0c 100644 --- a/Dice/Dice.vcxproj.filters +++ b/Dice/Dice.vcxproj.filters @@ -117,6 +117,12 @@ 源文件 + + 源文件 + + + 源文件 + 源文件 @@ -266,6 +272,12 @@ 头文件 + + 头文件 + + + 头文件 + 头文件 diff --git a/Dice/DiceCloud.cpp b/Dice/DiceCloud.cpp index 30d0dbb0..df47c062 100644 --- a/Dice/DiceCloud.cpp +++ b/Dice/DiceCloud.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "json.hpp" #include "DiceCloud.h" #include "GlobalVar.h" diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 131a36e9..1b4ae43b 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -7,7 +7,8 @@ * |_______/ |________| |________| |________| |__| * * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2019 w4123 + * Copyright (C) 2018-2020 w4123 + * Copyright (C) 2019-2020 String.Empty * * This program is free software: you can redistribute it and/or modify it under the terms * of the GNU Affero General Public License as published by the Free Software Foundation, @@ -35,19 +36,22 @@ using namespace std; using namespace CQ; -const std::map Console::intDefault{ - {"DisabledGlobal", 0}, {"DisabledBlock", 0}, {"DisabledListenAt", 1}, - {"DisabledMe", 1}, {"DisabledJrrp", 0}, {"DisabledDeck", 1}, {"DisabledDraw", 0}, {"DisabledSend", 0}, - {"Private", 0}, {"CheckGroupLicense", 0}, {"LeaveDiscuss", 0}, - {"ListenGroupRequest", 1}, {"ListenGroupAdd", 1}, - {"ListenFriendRequest", 1}, {"ListenFriendAdd", 1}, {"AllowStranger", 1}, - {"AutoClearBlack", 1}, {"LeaveBlackQQ", 0}, - {"ListenGroupKick", 1}, {"ListenGroupBan", 1}, {"ListenSpam", 1}, - {"BannedLeave", 0}, {"BannedBanInviter", 0}, - {"KickedBanInviter", 0}, - {"CloudBlackShare", 1}, {"BelieveDiceList", 0}, {"CloudVisible", 1}, - {"SystemAlarmCPU", 90}, {"SystemAlarmRAM", 90}, - {"SendIntervalIdle", 500}, {"SendIntervalBusy", 100} +const std::mapConsole::intDefault{ +{"DisabledGlobal",0},{"DisabledBlock",0},{"DisabledListenAt",1}, +{"DisabledMe",1},{"DisabledJrrp",0},{"DisabledDeck",1},{"DisabledDraw",0},{"DisabledSend",0}, +{"Private",0},{"CheckGroupLicense",0},{"LeaveDiscuss",0}, +{"ListenGroupRequest",1},{"ListenGroupAdd",1}, +{"ListenFriendRequest",1},{"ListenFriendAdd",1},{"AllowStranger",1}, +{"AutoClearBlack",1},{"LeaveBlackQQ",0}, +{"ListenGroupKick",1},{"ListenGroupBan",1},{"ListenSpam",1}, +{"BannedLeave",0},{"BannedBanInviter",0}, +{"KickedBanInviter",0}, +{"GroupClearLimit",20}, +{"CloudBlackShare",1},{"BelieveDiceList",0},{"CloudVisible",1}, +{"SystemAlarmCPU",90},{"SystemAlarmRAM",90},{"SystemAlarmDisk",90}, +{"SendIntervalIdle",500},{"SendIntervalBusy",100}, +//Զ¼[min],ԶͼƬ[h] +{"AutoSaveInterval",10},{"AutoClearImage",0} }; const enumap Console::mClockEvent{"off", "on", "save", "clear"}; @@ -157,15 +161,14 @@ int Console::log(const std::string& strMsg, int note_lv, const string& strTime) if (!Cnt)sendPrivateMsg(DiceMaid, note); } return Cnt; -} - +} void Console::newMaster(long long qq) { - masterQQ = qq; - getUser(qq).trust(5); - setNotice({qq, msgtype::Private}, 0b111111); - save(); - AddMsgToQueue(getMsg("strNewMaster"), qq); + masterQQ = qq; + getUser(qq).trust(5); + setNotice({ qq,CQ::msgtype::Private }, 0b111111); + save(); + AddMsgToQueue(getMsg("strNewMaster"), qq); } void Console::reset() @@ -212,7 +215,8 @@ void Console::saveNotice() const Console console; //DiceModManager modules{}; - +//Ⱥб +std::set ExceptGroups; //б std::map mDiceList; @@ -264,58 +268,50 @@ std::string printSTime(const SYSTEMTIME st) st.wMinute < 10 ? "0" : "") + to_string(st.wMinute) + ":" + (st.wSecond < 10 ? "0" : "") + to_string(st.wSecond); } - -//ӡûdzQQ -string printQQ(long long llqq) -{ - string nick = getStrangerInfo(llqq).nick; - string::size_type i; - while ((i = nick.find(' ')) != string::npos) + //ӡûdzQQ + string printQQ(long long llqq) { - nick.erase(nick.begin() + i); + string nick = getStrangerInfo(llqq).nick; + if (nick.empty())nick = getFriendList()[llqq].nick; + if(nick.empty())return "û(" + to_string(llqq) + ")"; + return nick + "(" + to_string(llqq) + ")"; } - return nick + "(" + to_string(llqq) + ")"; -} - -//ӡQQȺ -string printGroup(long long llgroup) -{ - if (!llgroup)return "˽"; - const auto GroupList = getGroupList(); - if (GroupList.count(llgroup)) + //ӡQQȺ + string printGroup(long long llgroup) { - return GroupList.at(llgroup) + "(" + to_string(llgroup) + ")"; + if (!llgroup)return"˽"; + if (ChatList.count(llgroup))return printChat(ChatList[llgroup]); + if (getGroupList().count(llgroup))return "[" + getGroupList()[llgroup] + "](" + to_string(llgroup) + ")"; + return "Ⱥ(" + to_string(llgroup) + ")"; } - return "Ⱥ(" + to_string(llgroup) + ")"; -} - -//ӡ촰 -string printChat(chatType ct) -{ - switch (ct.second) + //ӡ촰 + string printChat(chatType ct) { - case msgtype::Private: - return printQQ(ct.first); - case msgtype::Group: - return printGroup(ct.first); - case msgtype::Discuss: - return "(" + to_string(ct.first) + ")"; - default: - break; + switch (ct.second) + { + case msgtype::Private: + return printQQ(ct.first); + case msgtype::Group: + return printGroup(ct.first); + case msgtype::Discuss: + return "(" + to_string(ct.first) + ")"; + default: + break; + } + return ""; } - return ""; -} - //ȡб void getDiceList() { std::string list; - if (!Network::GET("shiki.stringempty.xyz", "/DiceList/", 80, list) && mDiceList.empty()) - { - console.log("ȡбʱ: \n" + list, 1, printSTNow()); - return; - } - readJson(list, mDiceList); + if (Network::GET("shiki.stringempty.xyz", "/DiceList/", 80, list)) + readJson(list, mDiceList); +} +//ȡб +void getExceptGroup() { + std::string list; + if (Network::GET("shiki.stringempty.xyz", "/DiceCloud/except_group.json", 80, list)) + json::parse(list, nullptr, false).get_to(ExceptGroups); } @@ -330,82 +326,43 @@ bool operator<(const Console::Clock clock, const SYSTEMTIME& st) } //׼ʱ -void ConsoleTimer() -{ - Console::Clock clockNow{stNow.wHour, stNow.wMinute}; - long long perLastCPU = 0; - long long perLastRAM = 0; - while (Enabled) - { - GetLocalTime(&stNow); - //ʱ䶯 - if (stTmp.wMinute != stNow.wMinute) - { - stTmp = stNow; - clockNow = {stNow.wHour, stNow.wMinute}; - for (const auto& [clock,eve_type] : multi_range(console.mWorkClock, clockNow)) - { - switch (eve_type) - { - case ClockEvent::on: - if (console["DisabledGlobal"]) - { - console.set("DisabledGlobal", 0); - console.log(getMsg("strClockToWork"), 0b10000, ""); - } - break; - case ClockEvent::off: - if (!console["DisabledGlobal"]) - { - console.set("DisabledGlobal", 1); - console.log(getMsg("strClockOffWork"), 0b10000, ""); + void ConsoleTimer() { + Console::Clock clockNow{ stNow.wHour,stNow.wMinute }; + while (Enabled) { + GetLocalTime(&stNow); + //ʱ䶯 + if (stTmp.wMinute != stNow.wMinute) { + stTmp = stNow; + clockNow = { stNow.wHour,stNow.wMinute }; + for (auto &[clock,eve_type] : multi_range(console.mWorkClock, clockNow)) { + switch (eve_type) { + case ClockEvent::on: + if (console["DisabledGlobal"]) { + console.set("DisabledGlobal", 0); + console.log(getMsg("strClockToWork"), 0b10000, ""); + } + break; + case ClockEvent::off: + if (!console["DisabledGlobal"]) { + console.set("DisabledGlobal", 1); + console.log(getMsg("strClockOffWork"), 0b10000, ""); + } + break; + case ClockEvent::save: + dataBackUp(); + console.log(GlobalMsg["strSelfName"] + "ʱɡ", 1, printSTime(stTmp)); + break; + case ClockEvent::clear: + if (clearGroup("black")) + console.log(GlobalMsg["strSelfName"] + "ʱȺɡ", 1, printSTNow()); + break; + default:break; } - break; - case ClockEvent::save: - dataBackUp(); - console.log(GlobalMsg["strSelfName"] + "ʱɡ", 1, printSTime(stTmp)); - break; - case ClockEvent::clear: - if (console && console["AutoClearBlack"] && clearGroup("black")) - console.log(GlobalMsg["strSelfName"] + "ʱȺɡ", 1, printSTNow()); - break; - default: break; - } - } - //¼ - if (stNow.wMinute % 10 == 0) - { - Cloud::update(); - } - if (stNow.wMinute % 30 == 0) - { - if (console["SystemAlarmCPU"]) - { - const long long perCPU = getWinCpuUsage(); - if (perCPU > console["SystemAlarmCPU"] && perCPU > perLastCPU)console.log( - "棺" + GlobalMsg["strSelfName"] + "ϵͳCPUռô" + to_string(perCPU) + "%", 0b1001, - printSTime(stNow)); - else if (perLastCPU > console["SystemAlarmCPU"] && perCPU < console["SystemAlarmCPU"])console.log( - "ѣ" + GlobalMsg["strSelfName"] + "ϵͳCPUռý" + to_string(perCPU) + "%", 0b11, - printSTime(stNow)); - perLastCPU = perCPU; - } - if (console["SystemAlarmRAM"]) - { - const long long perRAM = getRamPort(); - if (perRAM > console["SystemAlarmRAM"] && perRAM > perLastRAM)console.log( - "棺" + GlobalMsg["strSelfName"] + "ϵͳڴռô" + to_string(perRAM) + "%", 0b1001, - printSTime(stNow)); - else if (perLastRAM > console["SystemAlarmRAM"] && perRAM < console["SystemAlarmRAM"])console.log( - "ѣ" + GlobalMsg["strSelfName"] + "ϵͳڴռý" + to_string(perRAM) + "%", 0b11, - printSTime(stNow)); - perLastRAM = perRAM; } } + this_thread::sleep_for(100ms); } - this_thread::sleep_for(1000ms); } -} //һ int clearGroup(string strPara, long long fromQQ) diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index e2a00753..d57836b9 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -32,6 +32,7 @@ class Console long long DiceMaid = 0; friend void ConsoleTimer(); friend class FromMsg; + friend class DiceJob; //DiceSens DSens; using Clock = std::pair; static const enumap mClockEvent; @@ -119,10 +120,10 @@ class Console loadNotice(); return true; } - - void save() + void save() { - DDOM xml("console", ""); + mkDir("DiceData\\conf"); + DDOM xml("console",""); xml.push(DDOM("mode", to_string(isMasterMode))); xml.push(DDOM("master", to_string(masterQQ))); if (!mWorkClock.empty()) @@ -155,45 +156,41 @@ class Console std::multimap mWorkClock{}; std::map NoticeList{}; }; - -extern Console console; -//extern DiceModManager modules; -//б -extern std::map mDiceList; -//ȡб -void getDiceList(); - -struct fromMsg -{ - std::string strMsg; - long long fromQQ = 0; - long long fromGroup = 0; - fromMsg() = default; - - fromMsg(std::string msg, long long QQ, long long Group) : strMsg(std::move(msg)), fromQQ(QQ), fromGroup(Group) - { + extern Console console; + //extern DiceModManager modules; + +extern std::set ExceptGroups; +void getExceptGroup(); + //б + extern std::map mDiceList; + //ȡб + void getDiceList(); + struct fromMsg { + std::string strMsg; + long long fromQQ = 0; + long long fromGroup = 0; + fromMsg() = default; + fromMsg(std::string msg, long long QQ, long long Group) :strMsg(msg), fromQQ(QQ), fromGroup(Group) {}; }; -}; - -//֪ͨ -//һ -extern int clearGroup(std::string strPara = "unpower", long long fromQQ = 0); -//ӵ촰 -extern std::map mLinkedList; -//תб -extern std::multimap mFwdList; -//ʱ -extern long long llStartTime; -//ǰʱ -extern SYSTEMTIME stNow; -std::string printClock(std::pair clock); -std::string printSTime(SYSTEMTIME st); -std::string printSTNow(); -std::string printDate(); -std::string printDate(time_t tt); -std::string printQQ(long long); -std::string printGroup(long long); -std::string printChat(chatType); + //֪ͨ + //һ + extern int clearGroup(std::string strPara = "unpower", long long fromQQ = 0); + //ӵ촰 + extern std::map mLinkedList; + //תб + extern std::multimap mFwdList; + //ʱ + extern long long llStartTime; + //ǰʱ + extern SYSTEMTIME stNow; + std::string printClock(std::pair clock); + std::string printSTime(SYSTEMTIME st); + std::string printSTNow(); + std::string printDate(); + std::string printDate(time_t tt); + std::string printQQ(long long); + std::string printGroup(long long); + std::string printChat(chatType); void ConsoleTimer(); class ThreadFactory diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index f11d70ae..870106ae 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1,6 +1,4 @@ -#include -#include -#include +#include #include "DiceEvent.h" #include "Jsonio.h" #include "MsgFormat.h" @@ -11,10 +9,7 @@ #include "DiceSession.h" #include "GetRule.h" #include "CQAPI.h" -#include "DiceNetwork.h" -#include "DiceCloud.h" - -//#pragma warning(disable:28159) +#include using namespace std; using namespace CQ; @@ -54,14 +49,14 @@ int FromMsg::AdminEvent(const string& strOption) if (console["DisabledJrrp"])res << "ȫֽ.jrrp"; if (console["DisabledDraw"])res << "ȫֽ.draw"; if (console["DisabledSend"])res << "ȫֽ.send"; - if (trusted > 3) - res << "Ⱥ" + to_string(getGroupList().size()) - << "Ⱥ¼" + to_string(ChatList.size()) - << "" + to_string(getFriendList().size()) - << "û¼" + to_string(UserList.size()) - << (!PList.empty() ? "ɫ¼" + to_string(PList.size()) : "޽ɫ¼") - << "û" + to_string(blacklist->mQQDanger.size()) - << "Ⱥ" + to_string(blacklist->mGroupDanger.size()); + if (trusted > 3) res << "Ⱥ" + to_string(getGroupList().size()) + << "Ⱥ¼" + to_string(ChatList.size()) + << ""+to_string(getFriendList().size()) + << "û¼" + to_string(UserList.size()) + << "û" + to_string(today->cnt()) + << (PList.size() ? "ɫ¼" + to_string(PList.size()) : "") + << "û" + to_string(blacklist->mQQDanger.size()) + << "Ⱥ" + to_string(blacklist->mGroupDanger.size()); reply(GlobalMsg["strSelfName"] + "ĵǰ" + res.show()); return 1; } @@ -92,7 +87,7 @@ int FromMsg::AdminEvent(const string& strOption) { note("ѾԱȨޡ", 0b100); getUser(fromQQ).trust(3); - console.NoticeList.erase({fromQQ, msgtype::Private}); + console.NoticeList.erase({ fromQQ,msgtype::Private }); return 1; } if (strOption == "on") @@ -416,8 +411,7 @@ int FromMsg::AdminEvent(const string& strOption) else { chat(llGroup).set("ʹ").reset("δ"); - if (!chat(llGroup).isset("") && !chat(llGroup).isset("δ"))AddMsgToQueue( - getMsg("strAuthorized"), llGroup, msgtype::Group); + if (!chat(llGroup).isset("") && !chat(llGroup).isset("δ"))AddMsgToQueue(getMsg("strAuthorized"), llGroup, Group); note("" + printGroup(llGroup) + "" + GlobalMsg["strSelfName"] + "ʹ"); } } @@ -444,168 +438,130 @@ int FromMsg::AdminEvent(const string& strOption) reply("ǰָƵ" + to_string(FrqMonitor::getFrqTotal())); return 1; } - bool boolErase = false; - strVar["note"] = readPara(); - if (strMsg[intMsgCnt] == '-') - { - boolErase = true; - intMsgCnt++; - } - if (strMsg[intMsgCnt] == '+') { intMsgCnt++; } - long long llTargetID = readID(); - if (strOption == "dismiss") - { - if (ChatList.count(llTargetID)) - { - note("" + GlobalMsg["strSelfName"] + "˳" + printChat(chat(llTargetID)), 0b10); - chat(llTargetID).reset("").leave(); - } - else - { - reply(GlobalMsg["strGroupGetErr"]); + else { + bool boolErase = false; + strVar["note"] = readPara(); + if (strMsg[intMsgCnt] == '-') { + boolErase = true; + intMsgCnt++; } - return 1; - } - if (strOption == "boton") - { - if (getGroupList().count(llTargetID)) - { - if (groupset(llTargetID, "ָͣ") > 0) - { - chat(llTargetID).reset("ָͣ"); - note("" + GlobalMsg["strSelfName"] + "" + printGroup(llTargetID) + "ָ"); + if (strMsg[intMsgCnt] == '+') { intMsgCnt++; } + long long llTargetID = readID(); + if (strOption == "dismiss") { + if (ChatList.count(llTargetID)) { + note("" + GlobalMsg["strSelfName"] + "˳" + printChat(chat(llTargetID)), 0b10); + chat(llTargetID).reset("").leave(); } - else reply(GlobalMsg["strSelfName"] + "ڸȺָ!"); - } - else - { - reply(GlobalMsg["strGroupGetErr"]); - } - } - else if (strOption == "botoff") - { - if (groupset(llTargetID, "ָͣ") < 1) - { - chat(llTargetID).set("ָͣ"); - note("" + GlobalMsg["strSelfName"] + "" + printGroup(llTargetID) + "ָͣ", 0b1); - } - else reply(GlobalMsg["strSelfName"] + "ڸȺָͣ!"); - return 1; - } - else if (strOption == "blackgroup") - { - if (llTargetID == 0) - { - strReply = "ǰȺб"; - for (auto [each, danger] : blacklist->mGroupDanger) - { - strReply += "\n" + to_string(each); + else { + reply(GlobalMsg["strGroupGetErr"]); } - reply(); return 1; } - strVar["time"] = printSTNow(); - do - { - if (boolErase) - { - blacklist->rm_black_group(llTargetID, this); + else if (strOption == "boton") { + if (getGroupList().count(llTargetID)) { + if (groupset(llTargetID, "ָͣ") > 0) { + chat(llTargetID).reset("ָͣ"); + note("" + GlobalMsg["strSelfName"] + "" + printGroup(llTargetID) + "ָ"); + } + else reply(GlobalMsg["strSelfName"] + "ڸȺָ!"); } - else - { - blacklist->add_black_group(llTargetID, this); + else { + reply(GlobalMsg["strGroupGetErr"]); } } - while ((llTargetID = readID())); - return 1; - } - else if (strOption == "whiteqq") - { - if (llTargetID == 0) - { - strReply = "ǰûб"; - for (auto& [qq, user] : UserList) - { - if (user.nTrust)strReply += "\n" + printQQ(qq) + ":" + to_string(user.nTrust); + else if (strOption == "botoff") { + if (groupset(llTargetID, "ָͣ") < 1) { + chat(llTargetID).set("ָͣ"); + note("" + GlobalMsg["strSelfName"] + "" + printGroup(llTargetID) + "ָͣ", 0b1); } - reply(); + else reply(GlobalMsg["strSelfName"] + "ڸȺָͣ!"); return 1; } - do - { - if (boolErase) - { - if (trustedQQ(llTargetID)) - { - if (trusted <= trustedQQ(llTargetID)) - { - reply(GlobalMsg["strUserTrustDenied"]); - } - else - { - getUser(llTargetID).trust(0); - note("ջ" + GlobalMsg["strSelfName"] + "" + printQQ(llTargetID) + "Ρ", 0b1); - } - } - else - { - reply(printQQ(llTargetID) + "" + GlobalMsg["strSelfName"] + "İ"); + else if (strOption == "blackgroup") { + if (llTargetID == 0) { + strReply = "ǰȺб"; + for (auto [each, danger] : blacklist->mGroupDanger) { + strReply += "\n" + to_string(each); } + reply(); + return 1; } - else - { - if (trustedQQ(llTargetID)) - { - reply(printQQ(llTargetID) + "Ѽ" + GlobalMsg["strSelfName"] + "İ!"); + strVar["time"] = printSTNow(); + do { + if (boolErase) { + blacklist->rm_black_group(llTargetID, this); } - else - { - getUser(llTargetID).trust(1); - note("" + GlobalMsg["strSelfName"] + "" + printQQ(llTargetID) + "Ρ", 0b1); - strVar["user_nick"] = getName(llTargetID); - AddMsgToQueue(format(GlobalMsg["strWhiteQQAddNotice"], GlobalMsg, strVar), llTargetID); + else { + blacklist->add_black_group(llTargetID, this); } - } + } while ((llTargetID = readID())); + return 1; } - while ((llTargetID = readID())); - return 1; - } - else if (strOption == "blackqq") - { - if (llTargetID == 0) - { - strReply = "ǰûб"; - for (auto [each, danger] : blacklist->mQQDanger) - { - strReply += "\n" + printQQ(each); + else if (strOption == "whiteqq") { + if (llTargetID == 0) { + strReply = "ǰûб"; + for (auto& [qq, user] : UserList) { + if (user.nTrust)strReply += "\n" + printQQ(qq) + ":" + to_string(user.nTrust); + } + reply(); + return 1; } - reply(); + do { + if (boolErase) { + if (trustedQQ(llTargetID)) { + if (trusted <= trustedQQ(llTargetID)) { + reply(GlobalMsg["strUserTrustDenied"]); + } + else { + getUser(llTargetID).trust(0); + note("ջ" + GlobalMsg["strSelfName"] + "" + printQQ(llTargetID) + "Ρ", 0b1); + } + } + else { + reply(printQQ(llTargetID) + "" + GlobalMsg["strSelfName"] + "İ"); + } + } + else { + if (trustedQQ(llTargetID)) { + reply(printQQ(llTargetID) + "Ѽ" + GlobalMsg["strSelfName"] + "İ!"); + } + else { + getUser(llTargetID).trust(1); + note("" + GlobalMsg["strSelfName"] + "" + printQQ(llTargetID) + "Ρ", 0b1); + strVar["user_nick"] = getName(llTargetID); + AddMsgToQueue(format(GlobalMsg["strWhiteQQAddNotice"], GlobalMsg, strVar), llTargetID); + } + } + } while ((llTargetID = readID())); return 1; } - strVar["time"] = printSTNow(); - do - { - if (boolErase) - { - blacklist->rm_black_qq(llTargetID, this); - } - else - { - blacklist->add_black_qq(llTargetID, this); + else if (strOption == "blackqq") { + if (llTargetID == 0) { + strReply = "ǰûб"; + for (auto [each, danger] : blacklist->mQQDanger) { + strReply += "\n" + printQQ(each); + } + reply(); + return 1; } + strVar["time"] = printSTNow(); + do { + if (boolErase) { + blacklist->rm_black_qq(llTargetID, this); + } + else { + blacklist->add_black_qq(llTargetID, this); + } + } while ((llTargetID = readID())); + return 1; } - while ((llTargetID = readID())); - return 1; + else reply(GlobalMsg["strAdminOptionEmpty"]); + return 0; } - else reply(GlobalMsg["strAdminOptionEmpty"]); - return 0; } - -int FromMsg::MasterSet() -{ - const std::string strOption = readPara(); - if (strOption.empty()) - { +int FromMsg::MasterSet() { + std::string strOption = readPara(); + if (strOption.empty()) { reply(GlobalMsg["strAdminOptionEmpty"]); return -1; } @@ -626,17 +582,14 @@ int FromMsg::MasterSet() console.killMaster(); return 1; } - if (strOption == "reset") - { - if (console.master() != fromQQ) - { + else if (strOption == "reset") { + if (console.master() != fromQQ) { reply(GlobalMsg["strNotMaster"]); return 1; } - const string strMaster = readDigit(); - if (strMaster.empty() || stoll(strMaster) == console.master()) - { - reply("MasterҪDz!"); + string strMaster = readDigit(); + if (strMaster.empty() || stoll(strMaster) == console.master()) { + reply("MasterҪDz{strSelfCall}!"); } else { @@ -663,7 +616,7 @@ int FromMsg::MasterSet() if (trustedQQ(llAdmin) > 3) { note("ջ" + printQQ(llAdmin) + "" + GlobalMsg["strSelfName"] + "ĹȨޡ", 0b100); - console.rmNotice({llAdmin, msgtype::Private}); + console.rmNotice({ llAdmin,msgtype::Private }); getUser(llAdmin).trust(0); } else @@ -680,7 +633,7 @@ int FromMsg::MasterSet() else { getUser(llAdmin).trust(4); - console.addNotice({llAdmin, msgtype::Private}, 0b1110); + console.addNotice({ llAdmin, msgtype::Private }, 0b1110); note("" + printQQ(llAdmin) + "" + GlobalMsg["strSelfName"] + "ĹȨޡ", 0b100); } } @@ -701,13 +654,13 @@ int FromMsg::DiceReply() { if (strMsg[0] != '.')return 0; intMsgCnt++; - int intT = static_cast(fromType); + int intT = (int)fromChat.second; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; strVar["nick"] = getName(fromQQ, fromGroup); strVar["pc"] = getPCName(fromQQ, fromGroup); strVar["at"] = intT ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; - isAuth = trusted > 3 || fromType != msgtype::Group || getGroupMemberInfo(fromGroup, fromQQ).permissions > 1; + isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions > 1; strLowerMessage = strMsg; std::transform(strLowerMessage.begin(), strLowerMessage.end(), strLowerMessage.begin(), [](unsigned char c) { return tolower(c); }); @@ -730,9 +683,8 @@ int FromMsg::DiceReply() } } if (pGrp->isset("ʹ") && !pGrp->isset("δ"))return 0; - if (trusted > 0) - { - pGrp->set("ʹ").reset("δ"); + if (trusted > 0) { + pGrp->set("ʹ").reset("δ").reset("ЭЧ"); note("Ȩ" + printGroup(pGrp->ID) + "ʹ", 1); AddMsgToQueue(getMsg("strGroupAuthorized", strVar), pGrp->ID, msgtype::Group); } @@ -748,8 +700,8 @@ int FromMsg::DiceReply() } if (strLowerMessage.substr(intMsgCnt, 7) == "dismiss") { - if (!intT) - { + intMsgCnt += 7; + if (!intT) { string QQNum = readDigit(); if (QQNum.empty()) { @@ -766,32 +718,33 @@ int FromMsg::DiceReply() if (grp.isset("") || grp.isset("δ")) { reply(GlobalMsg["strGroupAway"]); - return 1; } - if (trustedQQ(fromQQ) > 2 || getGroupMemberInfo(llGroup, fromQQ).permissions > 1) - { - grp.leave(GlobalMsg["strDismiss"]); + if (trustedQQ(fromQQ) > 2) { + grp.leave(GlobalMsg["strAdminDismiss"]); reply(GlobalMsg["strGroupExit"]); - return 1; } - reply(GlobalMsg["strPermissionDeniedErr"]); + else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1){ + reply(GlobalMsg["strDismiss"]); + } + else { + reply(GlobalMsg["strPermissionDeniedErr"]); + } return 1; } - intMsgCnt += 7; - while (isspace(static_cast(strLowerMessage[intMsgCnt]))) - intMsgCnt++; string QQNum = readDigit(); - if (QQNum.empty() || QQNum == to_string(console.DiceMaid) || (QQNum.length() == 4 && stoll(QQNum) == - getLoginQQ() % 10000)) - { - if (!isAuth && trusted < 3) - { - if (pGrp->isset("ָͣ") && GroupInfo(fromGroup).nGroupSize > 200)AddMsgToQueue( - getMsg("strPermissionDeniedErr", strVar), fromQQ); + if (QQNum.empty() || QQNum == to_string(console.DiceMaid) || (QQNum.length() == 4 && stoll(QQNum) == getLoginQQ() % 10000)){ + if (trusted > 2) { + pGrp->leave(GlobalMsg["strAdminDismiss"]); + } + if (pGrp->isset("ЭЧ"))return 0; + if (isAuth) { + pGrp->leave(GlobalMsg["strDismiss"]); + } + else { + if (!isCalled && (pGrp->isset("ָͣ") || GroupInfo(fromGroup).nGroupSize > 200))AddMsgToQueue(getMsg("strPermissionDeniedErr", strVar), fromQQ); else reply(GlobalMsg["strPermissionDeniedErr"]); - return -1; } - chat(fromGroup).leave(GlobalMsg["strDismiss"]); + return 1; } return 1; } @@ -853,8 +806,10 @@ int FromMsg::DiceReply() } return 1; } - if (blacklist->get_qq_danger(fromQQ) || (intT != PrivateT && blacklist->get_group_danger(fromGroup))) - { + else if (intT != PrivateT && pGrp->isset("ЭЧ")) { + return 0; + } + else if (blacklist->get_qq_danger(fromQQ) || (intT != PrivateT && blacklist->get_group_danger(fromGroup))) { return 0; } if (strLowerMessage.substr(intMsgCnt, 3) == "bot") @@ -868,14 +823,10 @@ int FromMsg::DiceReply() if (Command == "on") { if (console["DisabledGlobal"])reply(GlobalMsg["strGlobalOff"]); - else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console[ - "CheckGroupLicense"] == 2 && !pGrp->isset("ʹ"))))reply(GlobalMsg["strGroupLicenseDeny"]); - else if (intT) - { - if (isAuth) - { - if (groupset(fromGroup, "ָͣ") > 0) - { + else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ"))))reply(GlobalMsg["strGroupLicenseDeny"]); + else if (intT) { + if (isAuth){ + if (groupset(fromGroup, "ָͣ") > 0) { chat(fromGroup).reset("ָͣ"); reply(GlobalMsg["strBotOn"]); } @@ -892,33 +843,23 @@ int FromMsg::DiceReply() } } } - else if (Command == "off") - { - if (fromType == msgtype(intT)) - { - if (isAuth) - { - if (groupset(fromGroup, "ָͣ")) - { - if (!isCalled && QQNum.empty() && pGrp->isGroup && GroupInfo(fromGroup).nGroupSize > 200) - AddMsgToQueue(getMsg("strBotOffAlready", strVar), fromQQ); - else reply(GlobalMsg["strBotOffAlready"]); - } - else - { - chat(fromGroup).set("ָͣ"); - reply(GlobalMsg["strBotOff"]); - } + else if (Command == "off"){ + if (isAuth){ + if (groupset(fromGroup, "ָͣ")) { + if (!isCalled && QQNum.empty() && pGrp->isGroup && GroupInfo(fromGroup).nGroupSize > 200)AddMsgToQueue(getMsg("strBotOffAlready", strVar), fromQQ); + else reply(GlobalMsg["strBotOffAlready"]); } - else - { - if (groupset(fromGroup, "ָͣ"))AddMsgToQueue(getMsg("strPermissionDeniedErr", strVar), fromQQ); - else reply(GlobalMsg["strPermissionDeniedErr"]); + else { + chat(fromGroup).set("ָͣ"); + reply(GlobalMsg["strBotOff"]); } } + else { + if (groupset(fromGroup, "ָͣ"))AddMsgToQueue(getMsg("strPermissionDeniedErr", strVar), fromQQ); + else reply(GlobalMsg["strPermissionDeniedErr"]); + } } - else if (!Command.empty() && !isCalled && pGrp->isset("ָͣ")) - { + else if (!Command.empty() && !isCalled && pGrp->isset("ָͣ")) { return 0; } else if (intT == GroupT && pGrp->isset("ָͣ") && GroupInfo(fromGroup).nGroupSize >= 500 && !isCalled) @@ -1039,9 +980,7 @@ int FromMsg::DiceReply() } return true; } - if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && - !pGrp->isset("ʹ")))) - { + else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ")))) { return 0; } if (strLowerMessage.substr(intMsgCnt, 7) == "welcome") @@ -1158,39 +1097,17 @@ int FromMsg::DiceReply() if (strOption == "state") { GetLocalTime(&stNow); - strReply = "ʱ䣺" + printSTime(stNow) + "\n"; - strReply += "ڴռã" + to_string(getRamPort()) + "%\n"; - strReply += "CPUռã" + to_string(getWinCpuUsage()) + "%\n"; - //strReply += "CPUռã" + to_string(getWinCpuUsage()) + "% / ռã" + to_string(getProcessCpu() / 100.0) + "%\n"; - //strReply += "ʱ䣺" + std::to_string(clock()) + " ʱ䣺" + std::to_string(llStartTime) + "\n"; - strReply += "ʱ"; - long long llDuration = (clock() - llStartTime) / 1000; - if (llDuration < 0) - { - strReply += "N/A"; - } - else if (llDuration < 60 * 2) - { - strReply += std::to_string(llDuration) + ""; - } - else if (llDuration < 60 * 60 * 2) - { - strReply += std::to_string(llDuration / 60) + "" + std::to_string(llDuration % 60) + ""; - } - else if (llDuration < 60 * 60 * 24 * 2) - { - strReply += std::to_string(llDuration / 60 / 60) + "Сʱ" + std::to_string(llDuration / 60 % 60) + ""; - } - else if (llDuration < 60 * 60 * 24 * 10) - { - strReply += std::to_string(llDuration / 60 / 60 / 24) + "" + std::to_string(llDuration / 60 / 60 % 24) - + "Сʱ"; - } - else - { - strReply += std::to_string(llDuration / 60 / 60 / 24) + ""; - } - reply(); + double mbFreeBytes = 0, mbTotalBytes = 0; + long long milDisk(getDiskUsage(mbFreeBytes, mbTotalBytes)); + ResList res; + res << "ʱ:" + printSTime(stNow) + << "ڴռ:" + to_string(getRamPort()) + "%" + << "CPUռ:" + toString(getWinCpuUsage() / 10.0) + "%" + << "Ӳռ:" + toString(milDisk / 10.0) + "%(:" + toString(mbFreeBytes) + "GB/ " + toString(mbTotalBytes) + "GB)" + << "ʱ:" + printDuringTime((clock() - llStartTime) / 1000) + << "ָ:" + to_string(today->get("frq")) + << "ָ:" + to_string(FrqMonitor::sumFrqTotal); + reply(res.show()); return 1; } if (strOption == "clrimg") @@ -1205,78 +1122,34 @@ int FromMsg::DiceReply() reply(GlobalMsg["strNotMaster"]); return -1; } - int Cnt = clearImage(); - note("imageļ" + to_string(Cnt) + "", 0b1); + cmd_key = "clrimage"; + sch.push_job(*this); return 1; } - if (strOption == "reload") + else if (strOption == "reload") { if (Mirai) { reply("Miraiִ֧˹"); return -1; } - if (trusted < 5) + + if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; } - - string strSelfName; - int pid = _getpid(); - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(pe32); - HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hProcessSnap == INVALID_HANDLE_VALUE) - { - note("ʧܣ̿մʧܣ", 1); - return false; - } - BOOL bResult = Process32First(hProcessSnap, &pe32); - int ppid(0); - while (bResult) - { - if (pe32.th32ProcessID == pid) - { - ppid = pe32.th32ParentProcessID; - reply("ȷϽ" + strModulePath + "\nid:" + to_string(pe32.th32ProcessID) + "\nid:" + to_string( - pe32.th32ParentProcessID)); - strSelfName = pe32.szExeFile; - break; - } - bResult = Process32Next(hProcessSnap, &pe32); - } - if (!ppid) - { - note("ʧܣδҵ̣", 1); + cmd_key = "reload"; + sch.push_job(*this); + return 1; + } + else if (strOption == "die") { + if (trusted < 5 && fromQQ != console.master()) { + reply(GlobalMsg["strNotMaster"]); return -1; } - string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + - to_string(getLoginQQ()); - //string command = "taskkill /f /pid " + to_string(ppid) + "\ntaskkill /f /pid " + to_string(pid) + "\nstart " + strSelfPath + " /account " + to_string(getLoginQQ()) + "\ntimeout /t 60\ndel %0"; - ofstream fout("reload.bat"); - fout << command << std::endl; - fout.close(); - note(command, 0); - this_thread::sleep_for(5s); - Enabled = false; - dataBackUp(); - switch (UINT res = -1;res = WinExec(".\\reload.bat", SW_SHOW)) - { - case 0: - note("ʧܣڴԴѺľ", 1); - break; - case ERROR_FILE_NOT_FOUND: - note("ʧܣָļδҵ", 1); - break; - case ERROR_PATH_NOT_FOUND: - note("ʧܣָ·δҵ", 1); - break; - default: - if (res > 31)note("ɹ" + to_string(res), 0); - else note("ʧܣδ֪" + to_string(res), 0); - break; - } + cmd_key = "die"; + sch.push_job(*this); return 1; } if (strOption == "rexplorer") @@ -1319,29 +1192,14 @@ int FromMsg::DiceReply() } if (strOpt == "update") { - string strPara = readPara(); - if (strPara.empty()) + strVar["ver"] = readPara(); + if (strVar["ver"].empty()) { Cloud::checkUpdate(this); } - else if (strPara == "dev" || strPara == "release") - { - string strAppPath(strModulePath); - strAppPath = strAppPath.substr(0, strAppPath.find_last_of('\\')) + "\\app\\com.w4123.dice.cpk"; - - switch (Cloud::DownloadFile( - ("http://shiki.stringempty.xyz/DiceVer/" + strPara + "?" + to_string(fromTime)).c_str(), - strAppPath.c_str())) - { - case -1: - reply("ʧ:" + strAppPath); - break; - case -2: - reply("ļδҵ:" + strAppPath); - break; - case 0: - note("¿ɹ\nreloadӦø"); - } + else if (strVar["ver"] == "dev" || strVar["ver"] == "release") { + cmd_key = "update"; + sch.push_job(*this); } return 1; } @@ -1482,6 +1340,7 @@ int FromMsg::DiceReply() const int intTMonth = 30 * 24 * 60 * 60; set sInact; set sBlackQQ; + int cntUser(0); if (isInGroup) for (const auto& each : getGroupMemberList(llGroup)) { @@ -1493,6 +1352,7 @@ int FromMsg::DiceReply() { sBlackQQ.insert(each.GroupNick + "(" + to_string(each.QQID) + ")"); } + if (UserList.count(each.QQID))cntUser++; } ResList res; strVar["group"] = grpinfo.llGroup ? grpinfo.tostring() : printGroup(llGroup); @@ -1505,12 +1365,11 @@ int FromMsg::DiceReply() res << "¼" + printDate(grp.tCreated); res << "¼" + printDate(grp.tUpdated); if (grp.inviter)res << "ߣ" + printQQ(grp.inviter); - if (isInGroup) - { - res << string("Ⱥӭ") + (grp.isset("Ⱥӭ") ? "" : "δ"); - res << (!sInact.empty() ? "\n30첻ԾȺԱ" + to_string(sInact.size()) : ""); - if (!sBlackQQ.empty()) - { + if (isInGroup) { + res << string("Ⱥӭ") + (grp.isset("Ⱥӭ") ? "" : "δ") + << "ûռȣ" + to_string(cntUser * 100 / (grpinfo.nGroupSize - 1)) + "%"; + res << (sInact.size() ? "\n30첻ԾȺԱ" + to_string(sInact.size()) : ""); + if (sBlackQQ.size()) { if (sBlackQQ.size() > 8) res << GlobalMsg["strSelfName"] + "ĺԱ" + to_string(sBlackQQ.size()) + ""; else @@ -1583,179 +1442,142 @@ int FromMsg::DiceReply() else reply(GlobalMsg["strGroupWholeBan"]); return 1; } - else - { - if (Command == "restart") - { - if (intPms < 2 && trusted < 4) - { - reply(GlobalMsg["strPermissionDeniedErr"]); - return 1; - } - if (!setGroupWholeBan(llGroup, 0))reply(GlobalMsg["strGroupWholeUnban"]); - return 1; - } - if (Command == "card") - { - if (long long llqq = readID()) - { - if (trusted < 4 && intPms < 2 && llqq != fromQQ) - { - reply(GlobalMsg["strPermissionDeniedErr"]); - return 1; - } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) - { - reply(GlobalMsg["strSelfPermissionErr"]); - return 1; - } - while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length()) - intMsgCnt++; - while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; - strVar["card"] = readRest(); - strVar["target"] = getName(llqq, llGroup); - if (setGroupCard(llGroup, llqq, strVar["card"])) - { - reply(GlobalMsg["strGroupCardSetErr"]); - } - else - { - reply(GlobalMsg["strGroupCardSet"]); - } - } - else - { - reply(GlobalMsg["strQQIDEmpty"]); - } - return 1; - } - if ((intPms < 2 && (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3 || trusted < 5))) - { + else if (Command == "restart") { + if (intPms < 2 && trusted < 4) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - if (Command == "ban") - { - if (trusted < 4) - { - reply(GlobalMsg["strNotAdmin"]); - return -1; + if(!setGroupWholeBan(llGroup, 0))reply(GlobalMsg["strGroupWholeUnban"]); + return 1; + } + else if (Command == "card") { + if (long long llqq = readID()) { + if (trusted < 4 && intPms < 2 && llqq != fromQQ) { + reply(GlobalMsg["strPermissionDeniedErr"]); + return 1; } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) - { + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } - string QQNum = readDigit(); - if (QQNum.empty()) - { - reply(GlobalMsg["strQQIDEmpty"]); - return -1; + while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length())intMsgCnt++; + while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; + strVar["card"] = readRest(); + strVar["target"] = getName(llqq, llGroup); + if (setGroupCard(llGroup, llqq, strVar["card"])) { + reply(GlobalMsg["strGroupCardSetErr"]); } - long long llMemberQQ = stoll(QQNum); - GroupMemberInfo Member = getGroupMemberInfo(llGroup, llMemberQQ); - if (Member.QQID == llMemberQQ) - { - strVar["member"] = getName(Member.QQID, llGroup); - if (Member.permissions > 1) - { - reply(GlobalMsg["strSelfPermissionErr"]); - return 1; - } - string strMainDice = readDice(); - if (strMainDice.empty()) - { - reply(GlobalMsg["strValueErr"]); - return -1; - } - const int intDefaultDice = get(getUser(fromQQ).intConf, string("Ĭ"), 100); - RD rdMainDice(strMainDice, intDefaultDice); - rdMainDice.Roll(); - long long intDuration = rdMainDice.intTotal; - strVar["res"] = rdMainDice.FormCompleteString(); - if (setGroupBan(llGroup, llMemberQQ, intDuration * 60) == 0) - if (intDuration <= 0) - reply(GlobalMsg["strGroupUnban"]); - else reply(GlobalMsg["strGroupBan"]); - else reply(GlobalMsg["strGroupBanErr"]); + else { + reply(GlobalMsg["strGroupCardSet"]); } - else reply("{self}޴ȺԱ"); } - else if (Command == "kick") - { - if (trusted < 4) - { - reply(GlobalMsg["strNotAdmin"]); - return -1; - } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) - { + else { + reply(GlobalMsg["strQQIDEmpty"]); + } + return 1; + } + else if ((intPms < 2 && (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3 || trusted < 5))) { + reply(GlobalMsg["strPermissionDeniedErr"]); + return 1; + } + else if (Command == "ban") { + if (trusted < 4) { + reply(GlobalMsg["strNotAdmin"]); + return -1; + } + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) { + reply(GlobalMsg["strSelfPermissionErr"]); + return 1; + } + string QQNum = readDigit(); + if (QQNum.empty()) { + reply(GlobalMsg["strQQIDEmpty"]); + return -1; + } + long long llMemberQQ = stoll(QQNum); + GroupMemberInfo Member = getGroupMemberInfo(llGroup, llMemberQQ); + if (Member.QQID == llMemberQQ){ + strVar["member"] = getName(Member.QQID, llGroup); + if (Member.permissions > 1) { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } - long long llMemberQQ = readID(); - if (!llMemberQQ) - { - reply(GlobalMsg["strQQIDEmpty"]); + string strMainDice = readDice(); + if (strMainDice.empty()) { + reply(GlobalMsg["strValueErr"]); return -1; } - ResList resKicked, resDenied, resNotFound; - GroupMemberInfo Member; - do - { - Member = getGroupMemberInfo(llGroup, llMemberQQ); - if (Member.QQID == llMemberQQ) - { - if (Member.permissions > 1) - { - resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; - continue; - } - if (setGroupKick(llGroup, llMemberQQ, false) == 0) - { - resKicked << Member.Nick + "(" + to_string(Member.QQID) + ")"; - } - else resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; - } - else resNotFound << to_string(llMemberQQ); - } - while ((llMemberQQ = readID())); - strReply = GlobalMsg["strSelfName"]; - if (!resKicked.empty())strReply += "ƳȺԱ" + resKicked.show() + "\n"; - if (!resDenied.empty())strReply += "Ƴʧܣ" + resDenied.show() + "\n"; - if (!resNotFound.empty())strReply += "Ҳ" + resNotFound.show(); - reply(); + const int intDefaultDice = get(getUser(fromQQ).intConf, string("Ĭ"), 100); + RD rdMainDice(strMainDice, intDefaultDice); + rdMainDice.Roll(); + long long intDuration = rdMainDice.intTotal; + strVar["res"] = rdMainDice.FormCompleteString(); + if (setGroupBan(llGroup, llMemberQQ, intDuration * 60) == 0) + if (intDuration <= 0) + reply(GlobalMsg["strGroupUnban"]); + else reply(GlobalMsg["strGroupBan"]); + else reply(GlobalMsg["strGroupBanErr"]); + } + else reply("{self}޴ȺԱ"); + } + else if (Command == "kick") { + if (trusted < 4) { + reply(GlobalMsg["strNotAdmin"]); + return -1; + } + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) { + reply(GlobalMsg["strSelfPermissionErr"]); return 1; } - else if (Command == "title") - { - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3) - { - reply(GlobalMsg["strSelfPermissionErr"]); - return 1; - } - if (long long llqq = readID()) - { - while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length()) - intMsgCnt++; - while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; - strVar["title"] = readRest(); - if (setGroupSpecialTitle(llGroup, llqq, strVar["title"])) - { - reply(GlobalMsg["strGroupTitleSetErr"]); + long long llMemberQQ = readID(); + if (!llMemberQQ) { + reply(GlobalMsg["strQQIDEmpty"]); + return -1; + } + ResList resKicked, resDenied, resNotFound; + GroupMemberInfo Member; + do { + Member = getGroupMemberInfo(llGroup, llMemberQQ); + if (Member.QQID == llMemberQQ) { + if (Member.permissions > 1) { + resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; + continue; } - else - { - strVar["target"] = getName(llqq, llGroup); - reply(GlobalMsg["strGroupTitleSet"]); + if (setGroupKick(llGroup, llMemberQQ, false) == 0) { + resKicked << Member.Nick + "(" + to_string(Member.QQID) + ")"; } + else resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; } - else - { - reply(GlobalMsg["strQQIDEmpty"]); - } + else resNotFound << to_string(llMemberQQ); + } while ((llMemberQQ = readID())); + strReply = GlobalMsg["strSelfName"]; + if (!resKicked.empty())strReply += "ƳȺԱ" + resKicked.show() + "\n"; + if (!resDenied.empty())strReply += "Ƴʧܣ" + resDenied.show() + "\n"; + if (!resNotFound.empty())strReply += "Ҳ" + resNotFound.show(); + reply(); + return 1; + } + else if (Command == "title") { + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3) { + reply(GlobalMsg["strSelfPermissionErr"]); return 1; } + if (long long llqq = readID()) { + while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length())intMsgCnt++; + while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; + strVar["title"] = readRest(); + if (setGroupSpecialTitle(llGroup, llqq, strVar["title"])) { + reply(GlobalMsg["strGroupTitleSetErr"]); + } + else { + strVar["target"] = getName(llqq, llGroup); + reply(GlobalMsg["strGroupTitleSet"]); + } + } + else { + reply(GlobalMsg["strQQIDEmpty"]); + } + return 1; } return 1; } @@ -3007,10 +2829,10 @@ int FromMsg::DiceReply() reply(GlobalMsg["strPcTempInvalid"]); break; case -4: - reply(GlobalMsg["strPCNameExist"]); + reply(GlobalMsg["strPcNameExist"]); break; case -6: - reply(GlobalMsg["strPCNameInvalid"]); + reply(GlobalMsg["strPcNameInvalid"]); break; default: reply(GlobalMsg["strUnknownErr"]); @@ -3144,6 +2966,8 @@ int FromMsg::DiceReply() reply(GlobalMsg["strPcClr"]); return 1; } + reply(fmt->get_help("pc")); + return 1; } else if (strLowerMessage.substr(intMsgCnt, 2) == "ra" || strLowerMessage.substr(intMsgCnt, 2) == "rc") { @@ -3158,9 +2982,7 @@ int FromMsg::DiceReply() string strTurnCnt = strMsg.substr(intMsgCnt, strMsg.find('#') - intMsgCnt); //#ܷʶЧ if (strTurnCnt.empty())intMsgCnt++; - else if ((strTurnCnt.length() == 1 && isdigit(static_cast(strTurnCnt[0]))) || strTurnCnt == - "10") - { + else if ((strTurnCnt.length() == 1 && isdigit(static_cast(strTurnCnt[0]))) || strTurnCnt == "10") { intMsgCnt += strTurnCnt.length() + 1; intTurnCnt = stoi(strTurnCnt); } @@ -4251,16 +4073,16 @@ bool FromMsg::DiceFilter() } if (isOtherCalled && !isCalled)return false; init2(strMsg); - if (fromType == msgtype::Private) isCalled = true; + if (fromChat.second == msgtype::Private) isCalled = true; trusted = trustedQQ(fromQQ); - isBotOff = (console["DisabledGlobal"] && (trusted < 4 || !isCalled)) || (!(isCalled && console["DisabledListenAt"]) - && (groupset(fromGroup, "ָͣ") > 0)); - if (DiceReply()) - { - AddFrq(fromQQ, fromTime, fromChat); - if (isAns)getUser(fromQQ).update(fromTime); - if (fromType != msgtype::Private)chat(fromGroup).update(fromTime); - return true; + isBotOff = (console["DisabledGlobal"] && (trusted < 4 || !isCalled)) || (!(isCalled && console["DisabledListenAt"]) && (groupset(fromGroup, "ָͣ") > 0)); + if (DiceReply()) { + if (isAns) { + AddFrq(fromQQ, fromTime, fromChat); + getUser(fromQQ).update(fromTime); + } + if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); + return 1; } if (groupset(fromGroup, "ûظ") < 1 && CustomReply())return true; if (isBotOff)return console["DisabledBlock"]; @@ -4270,16 +4092,13 @@ bool FromMsg::DiceFilter() int FromMsg::readNum(int& num) { string strNum; - while (intMsgCnt < strMsg.length() && !isdigit(static_cast(strMsg[intMsgCnt])) && strMsg[intMsgCnt] - != '-')intMsgCnt++; - if (strMsg[intMsgCnt] == '-') - { + while (intMsgCnt < strMsg.length() && !isdigit(static_cast(strMsg[intMsgCnt])) && strMsg[intMsgCnt] != '-')intMsgCnt++; + if (strMsg[intMsgCnt] == '-') { strNum += '-'; intMsgCnt++; } if (intMsgCnt >= strMsg.length())return -1; - while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) - { + while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) { strNum += strMsg[intMsgCnt]; intMsgCnt++; } @@ -4296,32 +4115,30 @@ int FromMsg::readChat(chatType& ct, bool isReroll) ct = {fromQQ, msgtype::Private}; return 0; } + else if (strT == "this") + { + ct = fromChat; + return 0; + } + else if (strT == "qq") + { + ct.second = CQ::msgtype::Private; + } + else if (strT == "group") + { + ct.second = CQ::msgtype::Group; + } + + else if (strT == "discuss") + { + ct.second = CQ::msgtype::Discuss; + } else { - if (strT == "this") - { - ct = fromChat; - return 0; - } - if (strT == "qq") - { - ct.second = msgtype::Private; - } - else if (strT == "group") - { - ct.second = msgtype::Group; - } - else if (strT == "discuss") - { - ct.second = msgtype::Discuss; - } - else - { - if (isReroll)intMsgCnt = intFormor; - return -1; - } + if (isReroll)intMsgCnt = intFormor; + return -1; } - if (const long long llID = readID(); llID) + if (long long llID = readID(); llID) { ct.first = llID; return 0; diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 71724f49..0807495c 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -9,43 +9,19 @@ #include #include "CQAPI_EX.h" #include "MsgMonitor.h" -#include "DiceMsgSend.h" -#include "GlobalVar.h" +#include "DiceSchedule.h" using std::string; //Ϣ -class FromMsg -{ +class FromMsg :public DiceJobDetail { public: - std::string strMsg; string strLowerMessage; - long long fromID = 0; - CQ::msgtype fromType = CQ::msgtype::Private; - long long fromQQ = 0; long long fromGroup = 0; Chat* pGrp = nullptr; - chatType fromChat; - time_t fromTime = time(nullptr); string strReply; - //ʱ - map strVar = {}; - - FromMsg(std::string message, long long fromNum) : strMsg(std::move(message)), fromID(fromNum), fromQQ(fromNum) - { - fromChat = {fromID, CQ::msgtype::Private}; - } - - FromMsg(std::string message, long long fromGroup, CQ::msgtype msgType, long long fromNum) : strMsg(std::move(message)), - fromID(fromGroup), - fromType(msgType), - fromQQ(fromNum), - fromGroup(fromGroup), - fromChat({ - fromGroup, - fromType - }) - { + FromMsg(std::string message, long long qq) :DiceJobDetail(qq, { qq,CQ::msgtype::Private }, message){} + FromMsg(std::string message, long long fromGroup, CQ::msgtype msgType, long long qq) :DiceJobDetail(qq, { fromGroup,msgType }, message), fromGroup(fromGroup) { pGrp = &chat(fromGroup); } @@ -55,17 +31,18 @@ class FromMsg { isAns = true; if (isFormat) - AddMsgToQueue(format(strReply, GlobalMsg, strVar), fromID, fromType); - else AddMsgToQueue(strReply, fromID, fromType); + AddMsgToQueue(format(strReply, GlobalMsg, strVar), fromChat); + else AddMsgToQueue(strReply, fromChat); } void reply(const std::string& strReply, const std::initializer_list replace_str = {}, bool isFormat = true) { isAns = true; - if (!isFormat) - { - AddMsgToQueue(strReply, fromID, fromType); + while (isspace(static_cast(strReply[0]))) + strReply.erase(strReply.begin()); + if (!isFormat) { + AddMsgToQueue(strReply, fromChat); return; } int index = 0; @@ -73,7 +50,7 @@ class FromMsg { strVar[to_string(index++)] = s; } - AddMsgToQueue(format(strReply, GlobalMsg, strVar), fromID, fromType); + AddMsgToQueue(format(strReply, GlobalMsg, strVar), fromChat); } void reply() @@ -90,9 +67,8 @@ class FromMsg fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); reply(strMsg); - const string note = getName(fromQQ) + strMsg; - for (const auto& [ct,level] : console.NoticeList) - { + string note = getName(fromQQ) + strMsg; + for (const auto &[ct,level] : console.NoticeList) { if (!(level & note_lv) || pair(fromQQ, CQ::msgtype::Private) == ct || ct == fromChat)continue; AddMsgToQueue(note, ct); } @@ -102,8 +78,8 @@ class FromMsg std::string printFrom() { std::string strFwd; - if (fromType == CQ::msgtype::Group)strFwd += "[Ⱥ:" + to_string(fromGroup) + "]"; - if (fromType == CQ::msgtype::Discuss)strFwd += "[:" + to_string(fromGroup) + "]"; + if (fromChat.second == CQ::msgtype::Group)strFwd += "[Ⱥ:" + to_string(fromGroup) + "]"; + if (fromChat.second == CQ::msgtype::Discuss)strFwd += "[:" + to_string(fromGroup) + "]"; strFwd += getName(fromQQ, fromGroup) + "(" + to_string(fromQQ) + "):"; return strFwd; } @@ -140,10 +116,8 @@ class FromMsg } //ո - void readSkipSpace() - { - while (intMsgCnt < strMsg.length() && isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt - ++; + void readSkipSpace() { + while (intMsgCnt < strMsg.length() && isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt++; } void readSkipColon() @@ -155,9 +129,8 @@ class FromMsg string readUntilSpace() { string strPara; - readSkipSpace(); - while (intMsgCnt < strMsg.length() && !isspace(static_cast(strLowerMessage[intMsgCnt]))) - { + readSkipSpace(); + while (intMsgCnt < strMsg.length() && !isspace(static_cast(strLowerMessage[intMsgCnt]))) { strPara += strMsg[intMsgCnt]; intMsgCnt++; } @@ -165,10 +138,9 @@ class FromMsg } //ȡǿոհ׷ - string readUntilTab() - { + string readUntilTab() { while (intMsgCnt < strMsg.length() && isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; - const int intBegin = intMsgCnt; + int intBegin = intMsgCnt; int intEnd = intBegin; const unsigned int len = strMsg.length(); while (intMsgCnt < len && (!isspace(static_cast(strMsg[intMsgCnt])) || strMsg[intMsgCnt] == ' ')) @@ -191,15 +163,12 @@ class FromMsg string readPara() { string strPara; - while (intMsgCnt < strMsg.length() && isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt - ++; - while (intMsgCnt < strMsg.length() && !isspace(static_cast(strLowerMessage[intMsgCnt])) && ! - isdigit(static_cast(strLowerMessage[intMsgCnt])) - && (strLowerMessage[intMsgCnt] != '-') && (strLowerMessage[intMsgCnt] != '+') && (strLowerMessage[intMsgCnt] - != '[') && (strLowerMessage[intMsgCnt] != ']') && (strLowerMessage[intMsgCnt] != '=') && ( - strLowerMessage[intMsgCnt] != ':') - && intMsgCnt != strLowerMessage.length()) - { + while (intMsgCnt < strMsg.length() && isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt++; + while (intMsgCnt < strMsg.length() && !isspace(static_cast(strLowerMessage[intMsgCnt])) && !isdigit(static_cast(strLowerMessage[intMsgCnt])) + && (strLowerMessage[intMsgCnt] != '-') && (strLowerMessage[intMsgCnt] != '+') + && (strLowerMessage[intMsgCnt] != '[') && (strLowerMessage[intMsgCnt] != ']') + && (strLowerMessage[intMsgCnt] != '=') && (strLowerMessage[intMsgCnt] != ':') + && intMsgCnt != strLowerMessage.length()) { strPara += strLowerMessage[intMsgCnt]; intMsgCnt++; } @@ -210,15 +179,12 @@ class FromMsg string readDigit(bool isForce = true) { string strMum; - if (isForce) - while (intMsgCnt < strMsg.length() && !isdigit(static_cast(strMsg[intMsgCnt]))) - { - if (strMsg[intMsgCnt] < 0)intMsgCnt++; - intMsgCnt++; - } - else while (intMsgCnt < strMsg.length() && isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; - while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) - { + if (isForce)while (intMsgCnt < strMsg.length() && !isdigit(static_cast(strMsg[intMsgCnt]))) { + if (strMsg[intMsgCnt] < 0)intMsgCnt++; + intMsgCnt++; + } + else while(intMsgCnt < strMsg.length() && isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; + while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) { strMum += strMsg[intMsgCnt]; intMsgCnt++; } @@ -368,8 +334,7 @@ class FromMsg if (strMsg[intMsgCnt] == ':' || strMsg[intMsgCnt] == '.')intMsgCnt++; if (strMsg.substr(intMsgCnt, 2) == "")intMsgCnt += 2; readSkipSpace(); - if (intMsgCnt >= strMsg.length() || !isdigit(static_cast(strMsg[intMsgCnt]))) - { + if (intMsgCnt >= strMsg.length() || !isdigit(static_cast(strMsg[intMsgCnt]))) { cc.second = 0; return 0; } diff --git a/Dice/DiceFile.cpp b/Dice/DiceFile.cpp index 1ea800f4..6de8d81b 100644 --- a/Dice/DiceFile.cpp +++ b/Dice/DiceFile.cpp @@ -18,8 +18,7 @@ int mkDir(const std::string& dir) return -2;*/ } -int clrDir(const std::string& dir, const std::set& exceptList) -{ +int clrDir(std::string dir, const std::unordered_set& exceptList) { int nCnt = 0; std::error_code err; for (const auto& p : std::filesystem::directory_iterator(dir, err)) diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index 11153afc..7bf6257c 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include "DiceXMLTree.h" @@ -32,7 +32,7 @@ using std::unordered_map; int mkDir(const std::string& dir); -int clrDir(const std::string& dir, const std::set& exceptList); +int clrDir(std::string dir, const unordered_set& exceptList); template void map_merge(map& m1, const map& m2) @@ -214,9 +214,26 @@ int loadFile(std::string strPath, std::unordered_set& setTmp) return -1; } -template -int loadFile(std::string strPath, std::map& mapTmp) -{ +template +int loadFile(std::string strPath, std::unordered_set& setTmp) { + std::ifstream fin(strPath); + if (fin) + { + int Cnt = 0; + T item; + while (fscan(fin, item)) + { + setTmp.insert(item); + Cnt++; + } + return Cnt; + } + fin.close(); + return -1; +} + +template +int loadFile(std::string strPath, std::map&mapTmp) { std::ifstream fin(strPath); if (fin) { @@ -233,9 +250,8 @@ int loadFile(std::string strPath, std::map& mapTmp) return -1; } -template -int loadFile(std::string strPath, std::unordered_map& mapTmp) -{ +template +int loadFile(std::string strPath, std::unordered_map& mapTmp) { std::ifstream fin(strPath); if (fin) { @@ -252,9 +268,8 @@ int loadFile(std::string strPath, std::unordered_map& mapTmp) return -1; } -template -void loadFile(std::string strPath, std::multimap& mapTmp) -{ +template +void loadFile(std::string strPath, std::multimap&mapTmp) { std::ifstream fin(strPath); if (fin) { @@ -304,6 +319,21 @@ int loadBFile(std::string strPath, std::unordered_map& m) return Cnt; } +template +int loadBFile(std::string strPath, std::unordered_map& m) { + std::ifstream fin(strPath, std::ios::in | std::ios::binary); + if (!fin)return -1; + int len = fread(fin); + int Cnt = 0; + T key; + C val; + while (fin.peek() != EOF && len > Cnt++) { + key = fread(fin); + m[key].readb(fin); + } + fin.close(); + return Cnt; +} //ȡαini template int loadINI(std::string strPath, std::map& m) @@ -335,7 +365,7 @@ int loadXML(const std::string& strPath, std::map& m) } //ļ -int listDir(const string& dir, vector& files, bool isSub = false); +int listDir(const string& dir, vector& files, bool isSub = false) noexcept; template int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2& tmp, int& intFile, int& intFailure, @@ -348,9 +378,8 @@ int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2 { intFile++; string path = convert_w2a(p.path().filename().wstring().c_str()); - const int Cnt = load(strDir + path, tmp); - if (Cnt < 0) - { + int Cnt = load(strDir + path, tmp); + if (Cnt < 0) { files.push_back(path); intFailure++; } @@ -512,14 +541,27 @@ void saveFile(std::string strPath, const unordered_map& mTmp) fout.close(); } -template -void saveBFile(std::string strPath, std::map& m) -{ + +template +void saveFile(std::string strPath, const unordered_map& mTmp) { + if (clrEmpty(strPath, mTmp))return; + std::ofstream fout(strPath); + for (const auto& [key, val] : mTmp) + { + fout << key << "\t"; + fprint(fout, val); + fout << std::endl; + } + fout.close(); +} + +template +void saveBFile(std::string strPath, std::map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); const int len = m.size(); fwrite(fout, len); - for (auto& [key,val] : m) + for (auto& [key, val] : m) { fwrite(fout, key); fwrite(fout, val); @@ -527,15 +569,13 @@ void saveBFile(std::string strPath, std::map& m) fout.close(); } -template -void saveBFile(std::string strPath, std::unordered_map& m) -{ +template +void saveBFile(std::string strPath, std::unordered_map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); - const int len = m.size(); + int len = m.size(); fwrite(fout, len); - for (auto& [key, val] : m) - { + for (auto& [key, val] : m) { fwrite(fout, key); fwrite(fout, val); } diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp new file mode 100644 index 00000000..e0e70951 --- /dev/null +++ b/Dice/DiceJob.cpp @@ -0,0 +1,323 @@ +#pragma once +#include "DiceJob.h" +#include "DiceConsole.h" +#include +#include +#include "StrExtern.hpp" +#include "ManagerSystem.h" +#include "DiceCloud.h" +#include "BlackListManager.h" +#pragma warning(disable:28159) + +using namespace std; +using namespace CQ; + +int sendSelf(const string msg) { + static long long selfQQ = CQ::getLoginQQ(); + return CQ::sendPrivateMsg(selfQQ, msg); +} + +void cq_exit(DiceJob& job) { + int pid = _getpid(); + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(pe32); + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) { + job.note("ʧܣ̿մʧܣ", 1); + } + BOOL bResult = Process32First(hProcessSnap, &pe32); + int ppid(0); + while (bResult) { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + break; + } + bResult = Process32Next(hProcessSnap, &pe32); + } + if (!ppid) { + job.note("ʧܣδҵ̣", 1); + } + string strCMD("taskkill /f /pid " + to_string(ppid)); + job.note("" + getMsg("self") + "5ɱ", 1); + std::this_thread::sleep_for(5s); + job.echo(strCMD); + Enabled = false; + dataBackUp(); + system(strCMD.c_str()); +} +void cq_restart(DiceJob& job) { + char** path = new char* (); + _get_pgmptr(path); + string strSelfPath(*path); + delete path; + string strSelfName; + int pid = _getpid(); + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(pe32); + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) { + job.note("ʧܣ̿մʧܣ", 1); + } + BOOL bResult = Process32First(hProcessSnap, &pe32); + int ppid(0); + while (bResult) { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + job.echo("ȷϽ" + strSelfPath + "\nid:" + to_string(pe32.th32ProcessID) + "\nid:" + to_string(pe32.th32ParentProcessID)); + strSelfName = pe32.szExeFile; + break; + } + bResult = Process32Next(hProcessSnap, &pe32); + } + if (!ppid) { + job.note("ʧܣδҵ̣", 1); + } + string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + to_string(console.DiceMaid); + ofstream fout("reload.bat"); + fout << command << std::endl; + fout.close(); + job.note(command, 0); + std::this_thread::sleep_for(3s); + Enabled = false; + dataBackUp(); + switch (UINT res = -1; res = WinExec(".\\reload.bat", SW_SHOW)) { + case 0: + job.note("ʧܣڴԴѺľ", 1); + break; + case ERROR_FILE_NOT_FOUND: + job.note("ʧܣָļδҵ", 1); + break; + case ERROR_PATH_NOT_FOUND: + job.note("ʧܣָ·δҵ", 1); + break; + default: + if (res > 31)job.note("ɹ", 0); + else job.note("ʧܣδ֪" + to_string(res), 0); + break; + } +} + +void auto_save(DiceJob& job) { + if (sch.is_job_cold("autosave"))return; + dataBackUp(); + console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); + if (console["AutoSaveInterval"] > 0) { + sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"]); + sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); + } +} + +void check_system(DiceJob& job) { + static int perRAM(0), perLastRAM(0); + static double perLastCPU(0), perLastDisk(0), + perCPU(0), perDisk(0); + static bool isAlarmRAM(false), isAlarmCPU(false), isAlarmDisk(false); + static double mbFreeBytes = 0, mbTotalBytes = 0; + //ڴ + if (console["SystemAlarmRAM"] > 0) { + perRAM = getRamPort(); + if (perRAM > console["SystemAlarmRAM"] && perRAM > perLastRAM) { + console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳڴռô" + to_string(perRAM) + "%", 0b1000, printSTime(stNow)); + perLastRAM = perRAM; + isAlarmRAM = true; + } + else if (perLastRAM > console["SystemAlarmRAM"] && perRAM < console["SystemAlarmRAM"]) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳڴռý" + to_string(perRAM) + "%", 0b10, printSTime(stNow)); + perLastRAM = perRAM; + isAlarmRAM = false; + } + } + //CPU + if (console["SystemAlarmCPU"] > 0) { + perCPU = getWinCpuUsage() / 10.0; + if (perCPU > console["SystemAlarmCPU"] && (!isAlarmCPU || perCPU > perLastCPU + 1)) { + console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳCPUռô" + toString(perCPU) + "%", 0b1000, printSTime(stNow)); + perLastCPU = perCPU; + isAlarmCPU = true; + } + else if (perLastCPU > console["SystemAlarmCPU"] && perCPU < console["SystemAlarmCPU"]) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳCPUռý" + toString(perCPU) + "%", 0b10, printSTime(stNow)); + perLastCPU = perCPU; + isAlarmCPU = false; + } + } + //Ӳ̼ + if (console["SystemAlarmRAM"] > 0) { + perDisk = getDiskUsage(mbFreeBytes, mbTotalBytes) / 10.0; + if (perDisk > console["SystemAlarmDisk"] && (!isAlarmDisk || perDisk > perLastDisk + 1)) { + console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳӲռô" + toString(perDisk) + "%", 0b1000, printSTime(stNow)); + perLastDisk = perDisk; + isAlarmDisk = true; + } + else if (perLastDisk > console["SystemAlarmDisk"] && perDisk < console["SystemAlarmDisk"]) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳӲռý" + toString(perDisk) + "%", 0b10, printSTime(stNow)); + perLastDisk = perDisk; + isAlarmDisk = false; + } + } + if (isAlarmRAM || isAlarmCPU || isAlarmDisk) { + sch.add_job_for(5 * 60, job); + } + else { + sch.add_job_for(30 * 60, job); + } +} + +//õͼƬб +void clear_image(DiceJob& job) { + if (!job.fromQQ) { + if (sch.is_job_cold("clrimage"))return; + if (console["AutoClearImage"] <= 0) { + sch.add_job_for(60 * 60, job); + return; + } + } + scanImage(GlobalMsg, sReferencedImage); + scanImage(HelpDoc, sReferencedImage); + scanImage(CardDeck::mPublicDeck, sReferencedImage); + scanImage(CardDeck::mReplyDeck, sReferencedImage); + scanImage(CardDeck::mGroupDeck, sReferencedImage); + scanImage(CardDeck::mPrivateDeck, sReferencedImage); + for (auto it : ChatList) { + scanImage(it.second.strConf, sReferencedImage); + } + job.note("" + GlobalMsg["strSelfName"] + "ͼƬ" + to_string(sReferencedImage.size()) + "", 0b0); + int cnt = clrDir("data\\image\\", sReferencedImage); + job.note("imageļ"+ to_string(cnt) + "", 1); + if (console["AutoClearImage"] > 0) { + sch.refresh_cold("clrimage", time(NULL) + console["AutoClearImage"]); + sch.add_job_for(console["AutoClearImage"] * 60 * 60, "clrimage"); + } +} + +void clear_group(DiceJob& job) { + int intCnt = 0; + ResList res; + std::mapstrVar; + if (job.strVar["clear_mode"] == "unpower") { + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; + if (grp.isGroup && getGroupMemberInfo(id, console.DiceMaid).permissions == 1) { + res << printGroup(id); + grp.leave(getMsg("strLeaveNoPower")); + intCnt++; + this_thread::sleep_for(3s); + } + } + console.log(GlobalMsg["strSelfName"] + "ɸȺȨȺ" + to_string(intCnt) + ":" + res.show(), 0b10, printSTNow()); + } + else if (isdigit(static_cast(job.strVar["clear_mode"][0]))) { + int intDayLim = stoi(job.strVar["clear_mode"]); + string strDayLim = to_string(intDayLim); + time_t tNow = time(NULL); + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; + time_t tLast = grp.tLastMsg; + if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0)tLast = tLMT; + if (!tLast)continue; + int intDay = (int)(tNow - tLast) / 86400; + if (intDay > intDayLim) { + strVar["day"] = to_string(intDay); + res << printGroup(id) + ":" + to_string(intDay) + "\n"; + grp.leave(getMsg("strLeaveUnused", strVar)); + intCnt++; + this_thread::sleep_for(2s); + } + } + console.log(GlobalMsg["strSelfName"] + "ɸDZˮ" + strDayLim + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10, printSTNow()); + } + else if (job.strVar["clear_mode"] == "black") { + try { + for (auto& [id, grp_name] : getGroupList()) { + Chat& grp = chat(id).group().name(grp_name); + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset(""))continue; + if (blacklist->get_group_danger(id)) { + res << printGroup(id) + "" + "Ⱥ"; + if (console["LeaveBlackGroup"])grp.leave(getMsg("strBlackGroup")); + } + vector MemberList = getGroupMemberList(id); + for (auto eachQQ : MemberList) { + if (blacklist->get_qq_danger(eachQQ.QQID) > 1) { + if (eachQQ.permissions < getGroupMemberInfo(id, getLoginQQ()).permissions) { + continue; + } + else if (eachQQ.permissions > getGroupMemberInfo(id, getLoginQQ()).permissions) { + res << printChat(grp) + "" + printQQ(eachQQ.QQID) + "ԷȺȨ޽ϸ"; + grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); + intCnt++; + break; + } + else if (console["LeaveBlackQQ"]) { + res << printChat(grp) + "" + printQQ(eachQQ.QQID); + grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); + intCnt++; + break; + } + } + } + } + } + catch (...) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "Ⱥʱ", 0b10, printSTNow()); + } + if (intCnt) { + job.note("Ѱ" + getMsg("strSelfName") + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10); + } + else if (job.fromQQ) { + job.echo(getMsg("strSelfName") + "δִȺ"); + } + } + else if (job["clear_mode"] == "preserve") { + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("ʹ") || grp.isset(""))continue; + if (grp.isGroup && getGroupMemberInfo(id, console.master()).permissions) { + grp.set("ʹ"); + continue; + } + res << printChat(grp); + grp.leave(getMsg("strPreserve")); + intCnt++; + this_thread::sleep_for(3s); + } + console.log(GlobalMsg["strSelfName"] + "ɸȺ" + to_string(intCnt) + "" + res.show(), 1, printSTNow()); + } + else + job.echo("޷ʶɸѡ"); +} + +// +void cloud_beat(DiceJob& job) { + Cloud::update(); + sch.add_job_for(5 * 60, job); +} + +void dice_update(DiceJob& job) { + job.note("ʼDice\n汾:" + job.strVar["ver"], 1); + char** path = new char* (); + _get_pgmptr(path); + string strAppPath(*path); + strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; + delete path; + string strURL("http://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); + switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { + case -1: + job.echo("ʧ:" + strURL); + break; + case -2: + job.echo("ʧ!ļδҵ:" + strAppPath); + break; + case 0: + job.note("Dice!" + job.strVar["ver"] + "ɹ\n.system reload Ӧø", 1); + } +} + +string print_master() { + return printQQ(console.master()); +} + +string list_deck() { + return listKey(CardDeck::mPublicDeck); +} +string list_extern_deck() { + return listKey(CardDeck::mExternPublicDeck); +} \ No newline at end of file diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h new file mode 100644 index 00000000..14f5fca3 --- /dev/null +++ b/Dice/DiceJob.h @@ -0,0 +1,24 @@ +#pragma once +#include "DiceSchedule.h" + +inline time_t tNow = time(NULL); + +int sendSelf(const string msg); + +void cq_exit(DiceJob& job); +void cq_restart(DiceJob& job); + +void auto_save(DiceJob& job); +void check_system(DiceJob& job); + +void clear_image(DiceJob& job); + +void clear_group(DiceJob& job); + +void cloud_beat(DiceJob& job); +void dice_update(DiceJob& job); + +string print_master(); + +string list_deck(); +string list_extern_deck(); \ No newline at end of file diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index e2ed2518..78c1bc07 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -32,14 +32,17 @@ string DiceModManager::format(string s, const map& dict s.replace(l - 1, 1, ""); continue; } - string key = s.substr(l + 1, r - l - 1); + string key = s.substr(l + 1, r - l - 1), val; auto it = dict.find(key); - if (it != dict.end()) - { - s.replace(l, r - l + 1, format(it->second, dict, mod_name)); - r += s.length() - len + 1; - len = s.length(); + if (it != dict.end()) { + val = format(it->second, dict, mod_name); } + else if (auto func = strFuncs.find(key); func != strFuncs.end()) { + val = func->second(); + } + else continue; + s.replace(l, r - l + 1, val); + r = l + val.length(); //ñmod } return s; @@ -64,18 +67,15 @@ void DiceModManager::rm_help(const string& key) helpdoc.erase(key); } -int DiceModManager::load(string& strLog) -{ +int DiceModManager::load(string& strLog) { vector sFile; vector sFileErr; - const int cntFile = listDir(DiceDir + "\\mod\\", sFile, true); - int cntItem{0}; + int cntFile = listDir(DiceDir + "\\mod\\", sFile, true); + int cntItem{ 0 }; if (cntFile <= 0)return cntFile; - for (auto& filename : sFile) - { + for (auto& filename : sFile) { nlohmann::json j = freadJson(filename); - if (j.is_null()) - { + if (j.is_null()) { sFileErr.push_back(filename.filename().string()); continue; } @@ -83,10 +83,12 @@ int DiceModManager::load(string& strLog) { cntItem += readJMap(j["helpdoc"], helpdoc); } + if (j.count("global_char")) { + cntItem += readJMap(j["global_char"], GlobalChar); + } } strLog += "ȡ" + DiceDir + "\\mod\\е" + std::to_string(cntFile) + "ļ, " + std::to_string(cntItem) + "Ŀ\n"; - if (!sFileErr.empty()) - { + if (!sFileErr.empty()) { strLog += "ȡʧ" + std::to_string(sFileErr.size()) + ":\n"; for (auto& it : sFileErr) { diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index e118a88e..060604fe 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -13,14 +13,13 @@ using std::string; using std::vector; using std::map; -class DiceGenerator -{ - //ȴʱ - //int cold_time; - //γȡ - //int draw_limit = 1; - string expression; - //string cold_msg = "ȴʱС"; +class DiceGenerator { + //ȴʱ + //int cold_time; + //γȡ + //int draw_limit = 1; + string expression; + //string cold_msg = "ȴʱС"; public: string getExp() { return expression; } }; @@ -31,24 +30,37 @@ class BaseDeck vector cards; }; -class DiceMod -{ - string mod_name; - map m_helpdoc; - /*map> m_private_deck; - map> m_public_deck; - map m_generator;*/ +class DiceMod{ + string mod_name; + string auther; + string ver; + unsigned int build; + unsigned int Dice_build; + mapm_helpdoc; + map> m_public_deck; + /*map m_generator;*/ public: - - DiceMod(string name, - map helpdoc /*, - map> private_deck, - map> public_deck, - map generator*/ - ) : mod_name(std::move(name)), m_helpdoc(std::move(helpdoc)) - /*,m_private_deck(private_deck),m_public_deck(public_deck),m_generator(generator)*/ - { - } + DiceMod() = default; + DiceMod(string name, + maphelpdoc/*, + map> private_deck, + map> public_deck, + map generator*/ + ):mod_name(name), m_helpdoc(helpdoc) /*,m_private_deck(private_deck),m_public_deck(public_deck),m_generator(generator)*/ { + } + friend class DiceModFactory; +}; +class DiceModFactory :public DiceMod { + string mod_name; + string auther; + string ver; + unsigned int build; + unsigned int Dice_build; + mapm_helpdoc; + map> m_public_deck; + /*map m_generator;*/ +public: + DiceModFactory& name(string strMod){} }; class DiceModManager diff --git a/Dice/DiceMsgSend.cpp b/Dice/DiceMsgSend.cpp index c95e058c..d51273bc 100644 --- a/Dice/DiceMsgSend.cpp +++ b/Dice/DiceMsgSend.cpp @@ -78,8 +78,11 @@ void SendMsg() msgQueue.pop(); } } - if (!msg.msg.empty()) - { + if (!msg.msg.empty()){ + if (int pos = msg.msg.find('\f'); pos != string::npos) { + AddMsgToQueue(msg.msg.substr(pos + 1), msg.target_id, msg.msg_type); + msg.msg = msg.msg.substr(0, pos); + } if (msg.msg_type == msgtype::Private) { sendPrivateMsg(msg.target_id, msg.msg); diff --git a/Dice/DiceNetwork.cpp b/Dice/DiceNetwork.cpp index 56d3ac43..fb31d74b 100644 --- a/Dice/DiceNetwork.cpp +++ b/Dice/DiceNetwork.cpp @@ -93,10 +93,8 @@ namespace Network const char* header = "Content-Type: application/x-www-form-urlencoded"; const HINTERNET hInternet = InternetOpenA(DiceRequestHeader, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); - const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, - INTERNET_SERVICE_HTTP, 0, 0); - const HINTERNET hRequest = HttpOpenRequestA(hConnect, "POST", objectName, "HTTP/1.1", nullptr, acceptTypes, 0, - 0); + const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, INTERNET_SERVICE_HTTP, 0, 0); + const HINTERNET hRequest = HttpOpenRequestA(hConnect, "POST", objectName, "HTTP/1.1", nullptr, acceptTypes, 0, 0); const BOOL res = HttpSendRequestA(hRequest, header, strlen(header), frmdata, strlen(frmdata)); @@ -195,10 +193,8 @@ namespace Network const char* acceptTypes[] = {"*/*", nullptr}; const HINTERNET hInternet = InternetOpenA(DiceRequestHeader, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); - const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, - INTERNET_SERVICE_HTTP, 0, 0); - const HINTERNET hRequest = HttpOpenRequestA(hConnect, "GET", objectName, "HTTP/1.1", nullptr, acceptTypes, - INTERNET_FLAG_NO_CACHE_WRITE, 0); + const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, INTERNET_SERVICE_HTTP, 0, 0); + const HINTERNET hRequest = HttpOpenRequestA(hConnect, "GET", objectName, "HTTP/1.1", nullptr, acceptTypes, INTERNET_FLAG_NO_CACHE_WRITE, 0); const BOOL res = HttpSendRequestA(hRequest, nullptr, 0, nullptr, 0); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp new file mode 100644 index 00000000..d97c5381 --- /dev/null +++ b/Dice/DiceSchedule.cpp @@ -0,0 +1,179 @@ +#include +#include +#include +#include "DiceJob.h" +#include "ManagerSystem.h" +#include "Jsonio.h" + +unordered_map mCommand = { + {"syscheck",check_system}, + {"autosave",auto_save}, + {"clrimage",clear_image}, + {"reload",cq_restart}, + {"die",cq_exit}, + {"heartbeat",cloud_beat}, + {"update",dice_update} +}; + + +void DiceJob::exec() { + if (auto it = mCommand.find(cmd_key); it != mCommand.end()) { + it->second(*this); + } + else return; +} +void DiceJob::echo(const std::string& msg) { + switch (fromChat.second) { + case CQ::msgtype::Private: + CQ::sendPrivateMsg(fromQQ, msg); + break; + case CQ::msgtype::Group: + CQ::sendGroupMsg(fromChat.first, msg); + break; + case CQ::msgtype::Discuss: + CQ::sendDiscussMsg(fromChat.first, msg); + break; + } +} +void DiceJob::note(const std::string& strMsg, int note_lv = 0b1) { + ofstream fout(string("DiceData\\audit\\log") + to_string(console.DiceMaid) + "_" + printDate() + ".txt", ios::out | ios::app); + fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; + fout.close(); + echo(strMsg); + string note = fromQQ ? getName(fromQQ) + strMsg : strMsg; + for (const auto& [ct, level] : console.NoticeList) { + if (!(level & note_lv) || pair(fromQQ, CQ::msgtype::Private) == ct || ct == fromChat)continue; + AddMsgToQueue(note, ct); + } +} + +// +std::queue queueJob; +std::mutex mtQueueJob; +std::condition_variable cvJob; +std::condition_variable cvJobWaited; +//ʱ +using waited_job = pair; +std::priority_queue> queueJobWaited; +std::mutex mtJobWaited; + +void jobHandle() { + while (Enabled) { + //ҵ + if (std::unique_lock lock_queue(mtQueueJob); !queueJob.empty()) { + DiceJob job(queueJob.front()); + queueJob.pop(); + lock_queue.unlock(); + job.exec(); + cvJobWaited.notify_one(); + } + else{ + cvJob.wait(lock_queue, []() {return !queueJob.empty(); }); + } + } +} +void jobWait() { + while (Enabled) { + //鶨ʱҵ + if (std::unique_lock lock_queue(mtJobWaited); !queueJobWaited.empty() && queueJobWaited.top().first <= time(NULL)) { + sch.push_job(queueJobWaited.top().second); + queueJobWaited.pop(); + } + else { + cvJobWaited.wait_for(lock_queue, 1s); + } + today->daily_clear(); + } +} +//ִж +void DiceScheduler::push_job(const DiceJobDetail& job) { + if (!Enabled)return; + { + std::unique_lock lock_queue(mtQueueJob); + queueJob.push(job); + } + cvJob.notify_one(); +} +void DiceScheduler::push_job(const char* job_name) { + if (!Enabled)return; + { + std::unique_lock lock_queue(mtQueueJob); + queueJob.emplace(job_name); + } + cvJob.notify_one(); +} +//ȴ +void DiceScheduler::add_job_for(unsigned int waited, const DiceJobDetail& job) { + if (!Enabled)return; + std::unique_lock lock_queue(mtJobWaited); + queueJobWaited.emplace(time(NULL) + waited, job); +} +void DiceScheduler::add_job_for(unsigned int waited, const char* job_name) { + if (!Enabled)return; + std::unique_lock lock_queue(mtJobWaited); + queueJobWaited.emplace(time(NULL) + waited, job_name); +} + +void DiceScheduler::add_job_until(time_t cloc, const DiceJobDetail& job) { + if (!Enabled)return; + std::unique_lock lock_queue(mtJobWaited); + queueJobWaited.emplace(cloc, job); +} +void DiceScheduler::add_job_until(time_t cloc, const char* job_name) { + if (!Enabled)return; + std::unique_lock lock_queue(mtJobWaited); + queueJobWaited.emplace(cloc, job_name); +} +std::unique_ptr threadJobs; + +bool DiceScheduler::is_job_cold(const char* cmd) { + return untilJobs[cmd] > time(NULL); +} +void DiceScheduler::refresh_cold(const char* cmd, time_t until) { + untilJobs[cmd] = until; +} + +void DiceScheduler::start() { + threadJobs = std::make_unique(jobHandle); + threadJobs->detach(); + std::thread thWaited(jobWait); + thWaited.detach(); + push_job("heartbeat"); + push_job("syscheck"); + if (console["AutoSaveInterval"] > 0)add_job_for(console["AutoSaveInterval"] * 60, "autosave"); + if (console["AutoClearImage"] > 0)add_job_for(console["AutoClearImage"] * 60 * 60, "clrimage"); + else add_job_for(60 * 60, "clrimage"); +} +void DiceScheduler::end() { + threadJobs.reset(); +} + +void DiceToday::daily_clear() { + GetLocalTime(&stNow); + if (stToday.wDay != stNow.wDay) { + stToday = stNow; + cntGlobal.clear(); + } +} + +void DiceToday::save() { + json jFile; + jFile["date"] = { stToday.wYear,stToday.wMonth,stToday.wDay }; + jFile["global"] = cntGlobal; + jFile["user_cnt"] = cntUser; + fwriteJson(pathFile, jFile); +} +void DiceToday::load() { + json jFile = freadJson(pathFile); + if (jFile.is_null()) { + GetLocalTime(&stToday); + return; + } + if (jFile.count("date")) { + jFile["date"][0].get_to(stToday.wYear); + jFile["date"][1].get_to(stToday.wMonth); + jFile["date"][2].get_to(stToday.wDay); + } + if (jFile.count("global")) { jFile["global"].get_to(cntGlobal); } + if (jFile.count("user_cnt")) { jFile["user_cnt"].get_to(cntUser); } +} \ No newline at end of file diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h new file mode 100644 index 00000000..a4bce12d --- /dev/null +++ b/Dice/DiceSchedule.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019-2020 String.Empty + * ʱ¼ + * ܼʱɵָ + */ +#pragma once +#include +#include +#include "DiceConsole.h" +using std::string; +using std::map; + +struct DiceJobDetail { + long long fromQQ = 0; + chatType fromChat; + string cmd_key; + string strMsg; + time_t fromTime = time(NULL); + //ʱ + map strVar = {}; + DiceJobDetail(const char* cmd, bool isFromSelf = false):cmd_key(cmd){ + if(isFromSelf)fromQQ = console.DiceMaid; + } + DiceJobDetail(long long qq, chatType ct, std::string msg = "", const char* cmd = "") + :fromQQ(qq), fromChat(ct), strMsg(msg),cmd_key(cmd) { + } + string operator[](const char* key){ + return strVar[key]; + } + bool operator<(const DiceJobDetail& other)const { + return cmd_key < other.cmd_key; + } +}; + +class DiceJob : public DiceJobDetail { + enum class Renum { NIL, Retry_For, Retry_Until }; +public: + DiceJob(DiceJobDetail detail) :DiceJobDetail(detail) {} + Renum ren = Renum::NIL; + void exec(); + void echo(const std::string&); + void note(const std::string&, int); +}; + +class DiceScheduler { + //¼ȴ + unordered_map untilJobs; +public: + void start(); + void end(); + void push_job(const DiceJobDetail&); + void push_job(const char*); + void add_job_for(unsigned int, const DiceJobDetail&); + void add_job_for(unsigned int, const char*); + void add_job_until(time_t, const DiceJobDetail&); + void add_job_until(time_t, const char*); + bool is_job_cold(const char*); + void refresh_cold(const char*, time_t); +}; +inline DiceScheduler sch; + +typedef void (*cmd)(DiceJob&); + +//ռ¼ +class DiceToday { + SYSTEMTIME stToday; + string pathFile; + unordered_mapcntGlobal; + unordered_map>cntUser; +public: + DiceToday(const string& path) :pathFile(path) { + load(); + } + void load(); + void save(); + void inc(const string& key) { cntGlobal[key]++; save(); } + void inc(long long qq, const string& key, int cnt = 1) { cntUser[qq][key] += cnt; save(); } + int& get(const string& key) { return cntGlobal[key]; } + int& get(long long qq, const string& key) { return cntUser[qq][key]; } + size_t cnt(const string& key = "") { return cntUser.size(); } + void daily_clear(); +}; +inline std::unique_ptr today; \ No newline at end of file diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index cd432fb8..aaf04cd6 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -148,38 +148,31 @@ void DiceTableMaster::save() } } -int DiceTableMaster::load() const -{ - // string strLog; - std::unique_lock lock(sessionMutex); - vector sFile; - int cnt = listDir(DiceDir + R"(\user\session\)", sFile); - if (cnt <= 0)return cnt; - for (auto& filename : sFile) - { - nlohmann::json j = freadJson(filename); - if (j.is_null()) - { - cnt--; - continue; - } - if (j["type"] == "simple") - { - auto pSession(std::make_shared(j["room"])); - pSession->create(j["create_time"]).update(j["update_time"]); - if (j.count("observer")) pSession->sOB = j["observer"].get>(); - if (j.count("tables")) - { - for (nlohmann::json::iterator itTable = j["tables"].begin(); itTable != j["tables"].end(); ++itTable) - { - string strTable = UTF8toGBK(itTable.key()); - for (nlohmann::json::iterator itItem = itTable.value().begin(); itItem != j.end(); ++itItem) - { - pSession->mTable[strTable].emplace(UTF8toGBK(itItem.key()), itItem.value()); - } - } - } - } - } - return cnt; +int DiceTableMaster::load() { + string strLog; + std::unique_lock lock(sessionMutex); + vector sFile; + int cnt = listDir(DiceDir + "\\user\\session\\", sFile); + if (cnt <= 0)return cnt; + for (auto& filename : sFile) { + nlohmann::json j = freadJson(filename); + if (j.is_null()) { + cnt--; + continue; + } + if (j["type"] == "simple") { + auto pSession(std::make_shared(j["room"])); + pSession->create(j["create_time"]).update(j["update_time"]); + if (j.count("observer")) pSession->sOB = j["observer"].get>(); + if (j.count("tables")) { + for (nlohmann::json::iterator itTable = j["tables"].begin(); itTable != j["tables"].end(); ++itTable){ + string strTable = UTF8toGBK(itTable.key()); + for (nlohmann::json::iterator itItem = itTable.value().begin(); itItem != j.end(); ++itItem) { + pSession->mTable[strTable].emplace(UTF8toGBK(itItem.key()), itItem.value()); + } + } + } + } + } + return cnt; } diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 70384416..1cc237ed 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -32,6 +32,7 @@ bool Enabled = false; bool Mirai = false; std::string Dice_Full_Ver_For = Dice_Full_Ver + " For CoolQ]"; +std::string strModulePath; std::string strModulePath; @@ -55,8 +56,7 @@ std::map GlobalMsg {"strUserTrustIllegal","ĿȨ޸Ϊ{trust}ǷǷġ"}, {"strUserNotFound","{self}{user}û¼"}, {"strGroupAuthorized","A roll to the table turns to a dice fumble!\nDice Roller {strSelfName}\nȺȨɣ뾡ʹñ\nЭʹãʹ.dismissͳ!" }, - {"strAddGroupNoLicense","Ⱥδ{self}ʹãԶȺھĬ\n.helpЭ ĶͬЭӪʹã\nԱʹ!dismissͳ{self}\nɰ¸ʽд:\n!authorize ;:[] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[ָ]úʹ[ָ]ͳȺ" }, - {"strGroupLicenseDeny","Ⱥδ{self}ʹãȷЭ鲢ɡ\nԱʹ!dismissͳ{self}" }, + {"strGroupLicenseDeny","Ⱥδ{self}ʹãԶȺھĬ\n.helpЭ ĶͬЭӪʹã\nԱʹ!dismissͳ{self}\nɰ¸ʽд:\n!authorize ;:[*д*] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[*дָ*]úʹ[*дָ*]ͳȺ" }, {"strGroupLicenseApply","ȺδͨȨ\nѷ͡" }, {"strGroupSetOn","ѿ{self}ڴȺġ{option}ѡ"}, //Ⱥڿغңؿͨôı {"strGroupSetOnAlready","{self}ڴȺ{option}"}, @@ -96,10 +96,10 @@ std::map GlobalMsg {"strPcCardFull","{nick}{self}ĽɫѴޣɫ"}, {"strPcTempInvalid","{self}޷ʶĽɫģ"}, {"strPcNameEmpty","ƲΪա"}, - {"strPcNameExist","Ѵͬ"}, - {"strPcNameNotExist","Ʋڡ"}, - {"strPcNameInvalid","Ƿ│ðţ"}, - {"strPcInitDelErr","ʼɾ"}, + {"strPcNameExist","{nick}Ѵͬ"}, + {"strPcNameNotExist","{nick}޸ƽɫ"}, + {"strPcNameInvalid","ǷĽɫðţ"}, + {"strPcInitDelErr","{nick}ijʼɾ"}, {"strPcNoteTooLong","עȲܳ255"}, {"strPcTextTooLong","ıȲܳ48"}, {"strSensNote","ָддʣ{self}Ѽ¼ϱ"}, @@ -110,9 +110,9 @@ std::map GlobalMsg {"strReplyDel","{self}Թؼ{key}Ļظ"}, {"strStModify","{self}Ѽ¼{pc}Ա仯:"}, //ڼֵ仯ʱʹôı {"strStDetail","{self}{pc}ԣ"}, //ʱʹôı(ʱ) - {"strStValEmpty","{self}δ¼{attr}ԭֵ"}, //{0}Ϊ - {"strBlackQQAddNotice","{user_nick}ѱ{self}ϵMaster"}, - {"strBlackQQAddNoticeReason","{user_nick}{reason}ѱ{self}߽ϵԱ"}, + {"strStValEmpty","{self}δ¼{attr}ԭֵ"}, + {"strBlackQQAddNotice","{user_nick}ѱ{self}ϵMaster:{master_QQ}"}, + {"strBlackQQAddNoticeReason","{user_nick}{reason}ѱ{self}߽ϵԱMaster:{master_QQ}"}, {"strBlackQQDelNotice","{user_nick}ѱ{self}ƳڿԼʹ"}, {"strWhiteQQAddNotice","{user_nick}ѻ{self}Σ뾡ʹ{self}"}, {"strWhiteQQDenied","㲻{self}εû"}, @@ -141,27 +141,28 @@ std::map GlobalMsg {"strEnDefaultName","Ի"},//Ĭı {"strEnValEmpty", "δ{self}趨ɳֵ.st {attr} ֵ 鿴.help en"}, {"strEnValInvalid", "{attr}ֵ벻ȷ,1-99Χڵ!"}, - {"strSendMsg", "{self}ѽϢͳ"}, //Master͵Ļִ - {"strSendMasterMsg", "Ϣ{self}ѷ͸Master"}, //Master͵Ļִ - {"strSendMsgEmpty", "ϢΪա"}, - {"strSendMsgInvalid", "{self}ûпԷ͵Ķ"}, //ûMaster - {"strDefaultCOCClr", "Ĭϼ춨"}, - {"strDefaultCOCNotFound", "Ĭϼ춨治ڡ"}, - {"strDefaultCOCSet", "Ĭϼ춨:"}, - {"strLinkLoss", "{self}ʱѶϿ"}, - {"strLinked", "{self}Ѵʱš"}, - {"strLinkWarning", "Դʱţ֤ܷͨ"}, - {"strLinkNotFound", "ʱҪͨ򲻿״ĵطˡ"}, - {"strNotMaster", "㲻{self}masterʲô"}, - {"strNotAdmin", "㲻{self}ĹԱ"}, - {"strDismiss", ""}, //.dismissȺǰĻִ - {"strHlpSet", "Ϊ{key}ô"}, - {"strHlpReset", "{key}Ĵ"}, - {"strHlpNameEmpty", "MasterҪԶʲôѽ"}, - {"strHlpNotFound", "{self}δҵָİϢ"}, - {"strClockToWork", "{self}Ѱʱá"}, - {"strClockOffWork", "{self}Ѱʱرա"}, - {"strNameGenerator", "{pc}ƣ{res}"}, + {"strSendMsg","{self}ѽϢͳ"},//Master͵Ļִ + {"strSendMasterMsg","Ϣ{self}ѷ͸Master"},//Master͵Ļִ + {"strSendMsgEmpty","ϢΪա"}, + {"strSendMsgInvalid","{self}ûпԷ͵Ķ"},//ûMaster + {"strDefaultCOCClr","Ĭϼ춨"}, + {"strDefaultCOCNotFound","Ĭϼ춨治ڡ"}, + {"strDefaultCOCSet","Ĭϼ춨:"}, + {"strLinkLoss","{self}ʱѶϿ"}, + {"strLinked","{self}Ѵʱš"}, + {"strLinkWarning","Դʱţ֤ܷͨ"}, + {"strLinkNotFound","ʱҪͨ򲻿״ĵطˡ"}, + {"strNotMaster","㲻{self}masterʲô"}, + {"strNotAdmin","㲻{self}ĹԱ"}, + { "strAdminDismiss", "{strDismiss}" }, //ԱָȺĻִ + {"strDismiss", ""}, //.dismissȺǰĻִ + {"strHlpSet","Ϊ{key}ô"}, + {"strHlpReset","{key}Ĵ"}, + {"strHlpNameEmpty","MasterҪԶʲôѽ"}, + {"strHlpNotFound","{self}δҵָİϢ"}, + {"strClockToWork","{self}Ѱʱá"}, + {"strClockOffWork","{self}Ѱʱرա"}, + {"strNameGenerator","{pc}ƣ{res}"}, {"strDrawCard", "{pc}鵽ʲô{res}"}, {"strMeOn", "ɹ{self}.me"}, {"strMeOff", "ɹ{self}.me"}, @@ -318,30 +319,25 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { - {"", R"(V2.4.0 ๦ܸ Dice! GUI )"}, - { - "Э", - "0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС" - }, - { - "", - R"(ٷ̳:https://forum.kokona.tech/ -鿴Դ:https://github.com/mystringEmpty/Dice -:https://github.com/mystringEmpty/Dice/releases -ûֲ:http://shiki.stringempty.xyz/download/Shiki_User_Manual.pdf -ֲ:http://shiki.stringempty.xyz/download/Shiki_Master_Manual.pdf -ֲ:http://shiki.stringempty.xyz/download/DiceMaid_CookBook.html -(ĵ)https://dice.c-j.dev/ -stCOC7│:http://shiki.stringempty.xyz/download/COC7_player_card_shiki.xlsx)" - }, - { - "趨", - "Master{master}\n.meʹãֹ\n.jrrpʹã\n봦ƣǽ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n\nȺ:δã\nٷ(ˮ)Ⱥ: 624807593 941980833 882747577\n˽Ⱥ863062599\nShikiȺ1029435374" - }, - {"", "Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty\nӦAGPLv3Ȩ"}, - { - "ָ", - R"(atָָﵥӦat.bot off +{"",R"( +560:֧Mirai +558:ÿͳ +557:ʱҵϵͳ +556:ϵͳ +555:û¼/Ⱥϵͳ +554:ɫ +553:nameܵ +552:̨ϵͳ +551:ļȡƶ +550:ּ춨 +549:ˢ +547:ָ +537:.send)"}, +{"Э","0.ЭShiki(The StarDeathJudgementThe World)ķЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, +{"","鿴Դ:https://github.com/mystringEmpty/Dice\n:https://github.com/mystringEmpty/Dice/releases\nûֲ:http://shiki.stringempty.xyz/download/Shiki_User_Manual.pdf\nֲ:http://shiki.stringempty.xyz/download/Shiki_Master_Manual.pdf\nֲ:http://shiki.stringempty.xyz/download/DiceMaid_CookBook.html\n(ĵ)https://dice.c-j.dev/\nst│:http://shiki.stringempty.xyz/download/COC7_player_card_shiki.xlsx"}, +{"趨","Master{master_QQ}\n.meʹãֹ\n.jrrpʹã\n봦ƣǽ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n\nȺ:δã\nٷ(ˮ)Ⱥ: 624807593 941980833 882747577\n˽Ⱥ863062599\nȺ1029435374"}, +{"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, +{"ָ",R"(atָָﵥӦat.bot off ָҪӲ.helpӦָ ȡϸϢ ָ: .dismiss Ⱥ @@ -370,72 +366,45 @@ const std::map HelpDoc = { .send Ϣ .group Ⱥ .draw -Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)" - }, - { - "deck", - "ָĬƶѣʹ.drawָƶʱʹôƶѡƶѲŻֱһźϴơ\n.deck set ƶ Ĭƶ\n.deck set 1-100 ָȵ\n.deck show 鿴ʣ࿨\n.deck reset ʣ࿨\n.deck new Զƶѣÿո|ָ޶\n.deck new е|޵|޵|޵|޵|޵\nshowȺڲҪȨ" - }, - {"Ⱥ", "&dismiss"}, - {"Ⱥָ", "&dismiss"}, - {"dismiss", "ָҪȺԱȨޣʹú˳Ⱥ\n!dismiss [ĿQQ(ĩλ)]ָȺ\n!dismissú;Ĭ״ֻ̬ҪЧ"}, - {"Ȩ", "&authoize"}, - {"authorize", "Ȩ(ûʹʱתΪ)\n!authorize (+[Ⱥ]) ([])\nȺԭطͿʡȺţ޷ԶȨʱͬɷ"}, - {"", "&bot"}, - {"bot", ".bot on/off/ĬӣȺ\n.botӾĬ״ֻ̬ҪҲںЧ"}, - {"ٲ", "&rules"}, - {"", "&rules"}, - { - "rules ", - "ٲ飺.rules ([]):[] .rules set []\n.rules Ծ //ͬʱһ\n.rules COCʧ //cocĬѰcoc7Ĵ,dndĬѰ3r\n.rules dnd\n.rules set dnd //úȲѯdndͬ޲" - }, - {"", "&r"}, - {"rd", "&r"}, - { - "r", - ".r [ʽ] ([ԭ]) [ʽ]([]#)[Ӹ]d(p[ͷ])(k[ȡ])ʱΪһĬ\nϷҪ1-101-9Χ1-100Χ1-1000\n.r3#d\t//3\n.rhѧ \n.rs1D10+1D6+3 ɳӥ˺\t//ʡԵӵĵֱӸ\nְ汾ͷrod棬Ⱥ.obᱻʶΪԹָ" - }, - {"", "Ⱥ޶ָhΪ˽˺Ⱥobû\nΪ˱֤ͳɹ"}, - {"Թ", "&ob"}, - {"Թģʽ", "&ob"}, - { - "ob", - "Թģʽ.ob (exit/list/clr/on/off)\n.ob\t//ԹۿԿ˰\n.ob exit\t//˳Թģʽ\n.ob list\t//鿴ȺԹ\n.ob clr\t//Թ\n.ob on\t//ȫȺԹģʽ\n.ob off\t//Թģʽ\nԹ۽ȺЧ" - }, - {"Ĭ", "&set"}, - {"set", "ʽСD֮ûнʱΪͶĬ\n.set20 ĬΪ20\n.set ΪĬΪĬϵ100\nùж2D6Ƽʹ.st &=2D6"}, - {"λ", "λʮ棬Ϊ0~9ʮ֣ĽΪʮλλ֮ͣ00+0ʱΪ100"}, - {"ʮλ", "ʮλʮ棬Ϊ00~90ʮ֣ĽΪʮλλ֮ͣ00+0ʱΪ100"}, - {"", "&/ͷ"}, - {"ͷ", "&/ͷ"}, - {"", "&/ͷ"}, - {"/ͷ", "COCн/ͷǶͶʮλսѡõ/ߵĽִʧܵµȼڸС/ʮλ\n.rb2 2\nrcp 1ͷ"}, - {"", "&name"}, - { - "name", - ".name (cn/jp/en)([])\n.name 10\t//Ĭ\n.name en\t//cn/jp/en޶//Ӣ\nɸΧ1-10̫׷ĺ" - }, - {"dz", "&nn"}, - {"dz", "&nn"}, - { - "nn", - "dzƣ.nn [dz] / .nn / .nnn(cn/jp/en) \n.nn kp\t//dzǰ./ȷŻᱻԶ\n.nn\t//Ϊɾdz\n.nnn\t//Ϊdz\n.nnn jp\t/޶dz\n˽.nnΪȫdz\nȼȺdz>ȫdz>ȺƬ>QQdz" - }, - {"", "ð汾֧COC7(.coc.drawԱ/Ӣ츳)COC6(.coc6.drawú)DND(.dnd)AMGC(.draw AMGC)"}, - { - "coc", - "³ĺ(COC)ɣ.coc([7/6])(d)([])\n.coc 10\t//Ĭ7\n.coc6d\t//dΪϸɣһֻһ\n㷨ɣӦñ򣬲ο.rulesԱѡ" - }, - {"dnd", "³(DND)ɣ.dnd([])\n.dnd 5\t//οӦñ"}, - {"Լ¼", "&st"}, - { - "st", - "Լ¼.st (del/clr/show) ([]:[ֵ])\nûĬȺʹͬһſpl࿪ʹ.pcָп\n.st:50 :55 :65 :45 ò:70 :75 ־:35 :65 :75\n.st hp-1 +/-ʱΪԭֵϱ仯\n.st san+1d6 ޸ʱʹʽ\n.st del kpþ\t//ɾѱ\n.st clr\t//յǰ\n.st show \t//鿴ָ\n.st show\t//޲ʱ鿴ԣʹֻstӵܵİԶ│\nCOCԻᱻΪͬʣ/С/san/" - }, - {"ɫ", "&pc"}, - { - "pc", - R"(ɫ.pc +Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)"}, +{"deck","ָĬƶѣʹ.drawָƶʱʹôƶѡƶѲŻֱһźϴơ\n.deck set ƶ Ĭƶ\n.deck set 1-100 ָȵ\n.deck show 鿴ʣ࿨\n.deck reset ʣ࿨\n.deck new Զƶѣÿո|ָ޶\n.deck new е|޵|޵|޵|޵|޵\nshowȺڲҪȨ"}, +{"Ⱥ","&dismiss"}, +{"Ⱥָ","&dismiss"}, +{"dismiss","ָҪȺԱȨޣʹú˳Ⱥ\n!dismiss [ĿQQ(ĩλ)]ָȺ\n!dismissú;Ĭ״ֻ̬ҪЧ"}, +{"Ȩ","&authoize"}, +{"authorize","Ȩ(ûʹʱתΪ)\n!authorize (+[Ⱥ]) ([])\nȺԭطͿʡȺţ޷ԶȨʱͬɷ"}, +{"","&bot"}, +{"bot",".bot on/off/ĬӣȺ\n.botӾĬ״ֻ̬ҪҲںЧ"}, +{"ٲ","&rules"}, +{"","&rules"}, +{"rules","ٲ飺.rules ([]):[] .rules set []\n.rules Ծ //ͬʱһ\n.rules COCʧ //cocĬѰcoc7Ĵ,dndĬѰ3r\n.rules dnd\n.rules set dnd //úȲѯdndͬ޲"}, +{"","&r"}, +{"rd","&r"}, +{"r",".r [ʽ] ([ԭ]) [ʽ]([]#)[Ӹ]d(p[ͷ])(k[ȡ])ʱΪһĬ\nϷҪ1-101-9Χ1-100Χ1-1000\n.r3#d\t//3\n.rhѧ \n.rs1D10+1D6+3 ɳӥ˺\t//ʡԵӵĵֱӸ\nְ汾ͷrod棬Ⱥ.obᱻʶΪԹָ"}, +{"","Ⱥ޶ָhΪ˽˺Ⱥobû\nΪ˱֤ͳɹ"}, +{"Թ","&ob"}, +{"Թģʽ","&ob"}, +{"ob","Թģʽ.ob (exit/list/clr/on/off)\n.ob\t//ԹۿԿ˰\n.ob exit\t//˳Թģʽ\n.ob list\t//鿴ȺԹ\n.ob clr\t//Թ\n.ob on\t//ȫȺԹģʽ\n.ob off\t//Թģʽ\nԹ۽ȺЧ"}, +{"Ĭ","&set"}, +{"set","ʽСD֮ûнʱΪͶĬ\n.set20 ĬΪ20\n.set ΪĬΪĬϵ100\nùж2D6Ƽʹ.st &=2D6"}, +{"λ","λʮ棬Ϊ0~9ʮ֣ĽΪʮλλ֮ͣ00+0ʱΪ100"}, +{"ʮλ","ʮλʮ棬Ϊ00~90ʮ֣ĽΪʮλλ֮ͣ00+0ʱΪ100"}, +{"","&/ͷ"}, +{"ͷ","&/ͷ"}, +{"","&/ͷ"}, +{"/ͷ","COCн/ͷǶͶʮλսѡõ/ߵĽִʧܵµȼڸС/ʮλ\n.rb2 2\nrcp 1ͷ"}, +{"","&name"}, +{"name",".name (cn/jp/en)([])\n.name 10\t//Ĭ\n.name en\t//cn/jp/en޶//Ӣ\nɸΧ1-10̫׷ĺ"}, +{"dz","&nn"}, +{"dz","&nn"}, +{"nn","dzƣ.nn [dz] / .nn / .nnn(cn/jp/en) \n.nn kp\t//dzǰ./ȷŻᱻԶ\n.nn\t//Ϊɾdz\n.nnn\t//Ϊdz\n.nnn jp\t/޶dz\n˽.nnΪȫdz\nȼȺdz>ȫdz>ȺƬ>QQdz"}, +{"","ð汾֧COC7(.coc.drawԱ/Ӣ츳)COC6(.coc6.drawú)DND(.dnd)AMGC(.draw AMGC)"}, +{"coc","³ĺ(COC)ɣ.coc([7/6])(d)([])\n.coc 10\t//Ĭ7\n.coc6d\t//dΪϸɣһֻһ\n㷨ɣӦñ򣬲ο.rulesԱѡ"}, +{"dnd","³(DND)ɣ.dnd([])\n.dnd 5\t//οӦñ"}, +{"Լ¼","&st"}, +{"st","Լ¼.st (del/clr/show) ([]:[ֵ])\nûĬȺʹͬһſpl࿪ʹ.pcָп\n.st:50 :55 :65 :45 ò:70 :75 ־:35 :65 :75\n.st hp-1 +/-ʱΪԭֵϱ仯\n.st san+1d6 ޸ʱʹʽ\n.st del kpþ\t//ɾѱ\n.st clr\t//յǰ\n.st show \t//鿴ָ\n.st show\t//޲ʱ鿴ԣʹֻstӵܵİԶ│\nCOCԻᱻΪͬʣ/С/san/"}, +{"ɫ","&pc"}, +{"pc",R"(ɫ.pc ÿûͬʱ16Žɫ .pc new ([ģ]:([ɲ]:))([]) ȫʡԲһCOC7ģ diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index 0e4e99d1..891714c3 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -7,6 +7,8 @@ #include "json.hpp" #include "EncodingConvert.h" +using nlohmann::json; + class JsonList { std::vector vRes; @@ -54,8 +56,9 @@ std::enable_if_t, T> readJKey(const std::string& strJson return stoll(strJson); } -nlohmann::json freadJson(const std::string& strPath); +nlohmann::json freadJson(std::string strPath); nlohmann::json freadJson(const std::filesystem::path& path); +void fwriteJson(std::string strPath, const json& j); template int readJMap(const nlohmann::json& j, std::map& mapTmp) @@ -87,26 +90,16 @@ int readJson(const std::string& strJson, std::map& mapTmp) } } -template -int loadJMap(const std::string& strLoc, std::map& mapTmp) -{ - std::ifstream fin(strLoc); - if (fin) - { - try - { - nlohmann::json j; - fin >> j; - fin.close(); - return readJMap(j, mapTmp); - } - catch (...) - { - fin.close(); - return -1; - } +template +int loadJMap(const std::string& strLoc, std::map &mapTmp) { + nlohmann::json j = freadJson(strLoc); + if (j.is_null())return -2; + try { + return readJMap(j, mapTmp); + } + catch (...) { + return -1; } - return -2; } template diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 7e615e33..b1f2040f 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -11,26 +11,26 @@ #include "GlobalVar.h" string DiceDir = "DiceData"; -//õͼƬб -set sReferencedImage; - -const map mChatConf{ - //0-ȺԱ2-23-34-Ա5-ϵͳ - {"", 4}, - {"Ϣ", 0}, - {"ָͣ", 0}, - {"ûظ", 0}, - {"jrrp", 0}, - {"draw", 0}, - {"me", 0}, - {"help", 0}, - {"ob", 0}, - {"ʹ", 1}, - {"δ", 1}, - {"", 2}, - {"", 4}, - {"δ", 5}, - {"", 5} + //õͼƬб +unordered_set sReferencedImage; + +const map mChatConf{//0-ȺԱ2-23-34-Ա5-ϵͳ + {"",4}, + {"Ϣ",0}, + {"ָͣ",0}, + {"ûظ",0}, + {"jrrp",0}, + {"draw",0}, + {"me",0}, + {"help",0}, + {"ob",0}, + {"ʹ",1}, + {"δ",1}, + {"",2}, + {"",4}, + {"ЭЧ",4}, + {"δ",5}, + {"",5} }; User& getUser(long long qq) @@ -62,8 +62,8 @@ int clearUser() return QQDelete.size(); } -string getName(long long QQ, long long GroupID) -{ +string getName(long long QQ, long long GroupID){ + if (QQ == console.DiceMaid)return getMsg("strSelfCall"); string nick; if (UserList.count(QQ) && getUser(QQ).getNick(nick, GroupID))return nick; if (GroupID && !(nick = strip(CQ::getGroupMemberInfo(GroupID, QQ).GroupNick)).empty())return nick; @@ -111,23 +111,31 @@ Chat& chat(long long id) if (!ChatList.count(id))ChatList[id].id(id); return ChatList[id]; } - -int groupset(long long id, string st) -{ +Chat& Chat::id(long long grp) { + ID = grp; + if (CQ::getGroupList().count(grp)) { + CQ::GroupInfo ginfo(grp); + Name = ginfo.strGroupName; + isGroup = true; + if (ExceptGroups.count(grp) || ginfo.nGroupSize > 499) { + boolConf.insert("ЭЧ"); + } + } + return *this; +} +int groupset(long long id, string st) { if (!ChatList.count(id))return -1; return ChatList[id].isset(std::move(st)); } - -string printChat(Chat& grp) -{ - if (CQ::getGroupList().count(grp.ID))return CQ::getGroupList()[grp.ID] + "(" + to_string(grp.ID) + ")"; - if (grp.isset("Ⱥ"))return grp.strConf["Ⱥ"] + "(" + to_string(grp.ID) + ")"; - if (grp.isGroup) return "Ⱥ" + to_string(grp.ID) + ""; - return "" + to_string(grp.ID) + ""; +string printChat(Chat& grp) { + if (CQ::getGroupList().count(grp.ID))return "[" + CQ::getGroupList()[grp.ID] + "](" + to_string(grp.ID) + ")"; + if (!grp.Name.empty())return "[" + grp.Name + "](" + to_string(grp.ID) + ")"; + if (grp.isset("Ⱥ"))return "[" + grp.strConf["Ⱥ"] + "](" + to_string(grp.ID) + ")"; + if (grp.isGroup) return "Ⱥ(" + to_string(grp.ID) + ")"; + return "(" + to_string(grp.ID) + ")"; } -void scanImage(const string& s, set& list) -{ +void scanImage(string s, unordered_set& list) { int l = 0, r = 0; while ((l = s.find('[', r)) != string::npos && (r = s.find(']', l)) != string::npos) { @@ -138,32 +146,12 @@ void scanImage(const string& s, set& list) } } -void scanImage(const vector& v, set& list) -{ - for (const auto& it : v) - { +void scanImage(const vector& v, unordered_set& list) { + for (auto it : v) { scanImage(it, sReferencedImage); } } - -int clearImage() -{ - scanImage(GlobalMsg, sReferencedImage); - scanImage(HelpDoc, sReferencedImage); - scanImage(CardDeck::mPublicDeck, sReferencedImage); - scanImage(CardDeck::mReplyDeck, sReferencedImage); - scanImage(CardDeck::mGroupDeck, sReferencedImage); - scanImage(CardDeck::mPrivateDeck, sReferencedImage); - for (const auto& it : ChatList) - { - scanImage(it.second.strConf, sReferencedImage); - } - const string strLog = "" + GlobalMsg["strSelfName"] + "ͼƬ" + to_string(sReferencedImage.size()) + ""; - console.log(strLog, 0b0, printSTNow()); - return clrDir("data\\image\\", sReferencedImage); -} - DWORD getRamPort() { MEMORYSTATUSEX memory_status; @@ -181,8 +169,12 @@ __int64 compareFileTime(const FILETIME& ft1, const FILETIME& ft2) return t1 - t2; } -__int64 getWinCpuUsage() -{ +long long getWinCpuUsage() { + HANDLE hEvent; + BOOL res; + FILETIME preidleTime; + FILETIME prekernelTime; + FILETIME preuserTime; FILETIME idleTime; FILETIME kernelTime; FILETIME userTime; @@ -199,11 +191,10 @@ __int64 getWinCpuUsage() const __int64 kernel = compareFileTime(kernelTime, prekernelTime); const __int64 user = compareFileTime(userTime, preuserTime); - const __int64 cpu = (kernel + user - idle) * 100 / (kernel + user); - return cpu; + return (kernel + user - idle) * 1000 / (kernel + user); } -int getProcessCpu() +long long getProcessCpu() { const HANDLE hProcess = GetCurrentProcess(); //if (INVALID_HANDLE_VALUE == hProcess){return -1;} @@ -225,6 +216,30 @@ int getProcessCpu() const __int64 ullKernelTime = compareFileTime(ftKernelTime, ftPreKernelTime); const __int64 ullUserTime = compareFileTime(ftUserTime, ftPreUserTime); log << ullKernelTime << "\n" << ullUserTime << "\n" << iCpuNum; - const __int64 dCpu = (ullKernelTime + ullUserTime) / (iCpuNum * 100); - return static_cast(dCpu); + return (ullKernelTime + ullUserTime) / (iCpuNum * 10); } + +//ȡӲ(ǧֱ) +long long getDiskUsage(double& mbFreeBytes, double& mbTotalBytes){ + /*int sizStr = GetLogicalDriveStrings(0, NULL);//ñ̷Drive + char* chsDrive = new char[sizStr];//ʼԴ洢̷Ϣ + GetLogicalDriveStrings(sizStr, chsDrive); + int DType; + int si = 0;*/ + BOOL fResult; + unsigned _int64 i64FreeBytesToCaller; + unsigned _int64 i64TotalBytes; + unsigned _int64 i64FreeBytes; + fResult = GetDiskFreeSpaceEx( + NULL, + (PULARGE_INTEGER)&i64FreeBytesToCaller, + (PULARGE_INTEGER)&i64TotalBytes, + (PULARGE_INTEGER)&i64FreeBytes); + //GetDiskFreeSpaceExԻȡ̵Ŀռ״̬,صǸBOOL + if (fResult) { + mbTotalBytes = i64TotalBytes * 1000 / 1024 / 1024 / 1024 / 1000.0;// + mbFreeBytes = i64FreeBytesToCaller * 1000 / 1024 / 1024 / 1024 / 1000.0;//ʣռ + return 1000 - 1000 * i64FreeBytesToCaller / i64TotalBytes; + } + return 0; +} \ No newline at end of file diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index e5f33a5c..15ff3bbf 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -358,44 +358,32 @@ string printChat(Chat& grp); ifstream& operator>>(ifstream& fin, Chat& grp); ofstream& operator<<(ofstream& fout, const Chat& grp); -//õͼƬб -extern set sReferencedImage; +extern unordered_setsReferencedImage; +void scanImage(string s, unordered_set& list); -void scanImage(const string& s, set& list); +void scanImage(const vector& v, unordered_set& list); -void scanImage(const vector& v, set& list); - -template -void scanImage(const map& m, set& list) -{ - for (const auto& it : m) - { +template +void scanImage(const map& m, unordered_set& list) { + for (auto it : m) { scanImage(it.first, sReferencedImage); scanImage(it.second, sReferencedImage); } } - -template -void scanImage(const map& m, set& list) -{ - for (const auto& it : m) - { +template +void scanImage(const map& m, unordered_set& list) { + for (auto it : m) { scanImage(it.first, sReferencedImage); scanImage(it.second, sReferencedImage); } } - -template -void scanImage(const map& m, set& list) -{ - for (const auto& it : m) - { +template +void scanImage(const map& m, unordered_set& list) { + for (auto it : m) { scanImage(it.second, sReferencedImage); } } -int clearImage(); - DWORD getRamPort(); /*static DWORD getRamPort() { @@ -410,6 +398,8 @@ DWORD getRamPort(); __int64 compareFileTime(const FILETIME& ft1, const FILETIME& ft2); //WIN CPUʹ -__int64 getWinCpuUsage(); +long long getWinCpuUsage(); + +long long getProcessCpu(); -int getProcessCpu(); +long long getDiskUsage(double&, double&); \ No newline at end of file diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 45852d07..2cf490bb 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -23,8 +23,20 @@ #include "MsgFormat.h" #include #include // +#include "DiceJob.h" +#include "EncodingConvert.h" using std::string; +std::map GlobalChar{ + {"FormFeed","\f"}, +}; + +std::map strFuncs{ + {"master_QQ",print_master}, + {"չƶ",list_extern_deck}, + {"ȫƶб",list_deck}, +}; + std::string format(std::string str, const std::initializer_list& replace_str) { auto counter = 0; diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index af352142..55f28e1d 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -28,6 +28,11 @@ #include #include using std::string; + +extern std::map GlobalChar; +typedef string(*GobalTex)(); +extern std::map strFuncs; + std::string format(std::string str, const std::initializer_list& replace_str); template @@ -48,29 +53,31 @@ std::string format(std::string s, const std::map } } int l = 0, r = 0; - int len = s.length(); - while ((l = s.find('{', r)) != string::npos && (r = s.find('}', l)) != string::npos) - { - if (l - 1 >= 0 && s[l - 1] == 0x5c) - { + while ((l = s.find('{', r)) != string::npos && (r = s.find('}', l)) != string::npos) { + //ǰӡ\ʾݲת + if (l - 1 >= 0 && s[l - 1] == 0x5c) { s.replace(l - 1, 1, ""); continue; } string key = s.substr(l + 1, r - l - 1); + string val; auto it = replace_str.find(key); - if (it != replace_str.end()) - { - s.replace(l, r - l + 1, format(it->second, replace_str, str_tmp)); - r += s.length() - len + 1; - len = s.length(); + if (it != replace_str.end()) { + val = format(it->second, replace_str, str_tmp); } - else if ((it = str_tmp.find(key)) != str_tmp.end()) - { - if (key == "res")s.replace(l, r - l + 1, format(it->second, replace_str, str_tmp)); - else s.replace(l, r - l + 1, it->second); - r += s.length() - len + 1; - len = s.length(); + else if ((it = GlobalChar.find(key)) != GlobalChar.end()) { + val = it->second; + } + else if ((it = str_tmp.find(key)) != str_tmp.end()) { + if (key == "res")val = format(it->second, replace_str, str_tmp); + else val = it->second; + } + else if (auto func = strFuncs.find(key); func != strFuncs.end()) { + val = func->second(); } + else continue; + s.replace(l, r - l + 1, val); + r = l + val.length(); } return s; } @@ -104,21 +111,14 @@ class ResList std::string show() { std::string s; - if (intMaxLen > intLineLen || isLineBreak) - { - for (auto it : vRes) - { - for (auto it2 = vRes.begin(); it2 != vRes.end(); ++it2) - { - if (it2 == vRes.begin())s = "\n" + *it2; - else s += strLongSepa + *it2; - } + if (intMaxLen > intLineLen || isLineBreak) { + for (auto it = vRes.begin(); it != vRes.end(); it++) { + if (it == vRes.begin())s = "\n" + *it; + else s += strLongSepa + *it; } } - else - { - for (auto it = vRes.begin(); it != vRes.end(); ++it) - { + else { + for (auto it = vRes.begin(); it != vRes.end(); it++) { if (it == vRes.begin())s = *it; else s += sDot + *it; } diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index b002d75e..c19177c0 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -6,7 +6,9 @@ #include #include #include "MsgMonitor.h" +#include "DiceSchedule.h" +std::atomic FrqMonitor::sumFrqTotal = 0; std::map FrqMonitor::mFrequence = {}; std::map FrqMonitor::mWarnLevel = {}; @@ -24,6 +26,9 @@ void AddFrq(long long QQ, time_t TT, chatType CT) setFrq.insert(QQ); auto* newFrq = new FrqMonitor(QQ, TT, CT); EarlyMsgQueue.push(newFrq); + FrqMonitor::sumFrqTotal++; + today->inc("frq"); + today->inc(QQ, "frq"); } void frqHandler() @@ -119,11 +124,11 @@ EVE_Status_EX(statusFrq) { eve.data = std::to_string(intFrq); eve.dataf = "/min"; - if (intFrq < 60) + if (intFrq < 10) { eve.color_green(); } - else if (intFrq < 120) + else if (intFrq < 20) { eve.color_orange(); } diff --git a/Dice/MsgMonitor.h b/Dice/MsgMonitor.h index 6ed2b88d..5c38e44a 100644 --- a/Dice/MsgMonitor.h +++ b/Dice/MsgMonitor.h @@ -5,6 +5,7 @@ #pragma once #include #include +#include #include "DiceConsole.h" #include "ManagerSystem.h" #include "GlobalVar.h" @@ -18,8 +19,10 @@ class FrqMonitor static const long long earlierTime = 60; static const long long earliestTime = 300; //Ƶʼ¼ - static std::map mFrequence; - static std::map mWarnLevel; + static std::mapmFrequence; + static std::mapmWarnLevel; + static std::mapmDailyFrq; + static std::atomic sumFrqTotal; static int getFrqTotal(); long long fromQQ = 0; time_t fromTime = 0; diff --git a/Dice/RD.cpp b/Dice/RD.cpp index 8f0df4ec..9ef88642 100644 --- a/Dice/RD.cpp +++ b/Dice/RD.cpp @@ -47,6 +47,10 @@ void init2(string& msg) } if (msg[0] == '!') msg[0] = '.'; + int posFF(0); + while ((posFF = msg.find('\f'))!= string::npos) { + msg[posFF] = '\v'; + } } void COC7D(string& strMAns) diff --git a/Dice/StrExtern.hpp b/Dice/StrExtern.hpp index 7080f474..2c1fd75b 100644 --- a/Dice/StrExtern.hpp +++ b/Dice/StrExtern.hpp @@ -14,11 +14,24 @@ using std::to_string; string toString(int num, unsigned short size = 0); -template -string to_signed_string(Dig num) +template +typename std::enable_if::value, string>::type +toString(F num, unsigned short scale = 0, bool align = false) { - if (num > 0)return "+" + to_string(num); - return to_string(num); + string strNum{ to_string(num) }; + size_t dot(strNum.find('.') + scale + 1); + if (align)return strNum.substr(0, dot); + size_t last(strNum.find_last_not_of('0') + 1); + if (strNum[last - 1] == '.')last--; + if (last > dot)last = dot; + return strNum.substr(0, last); +} + +template +string to_signed_string(Dig num) +{ + if (num > 0)return "+" + to_string(num); + return to_string(num); } int count_char(const string& s, char ch); @@ -26,3 +39,5 @@ int count_char(const string& s, char ch); string convert_w2a(const wchar_t* wch); wstring convert_a2w(const char* ch); + +string printDuringTime(long long); diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index f3374106..68f219b2 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -1,4 +1,5 @@ #include +#include #include #define WIN32_LEAN_AND_MEAN #include @@ -6,6 +7,7 @@ using std::string; using std::wstring; +using std::string_view; using std::to_string; #define CP_GB18030 (54936) @@ -39,12 +41,35 @@ string convert_w2a(const wchar_t* wch) return str; } -wstring convert_a2w(const char* ch) +wstring convert_a2w(const char* ch) { - const int len = MultiByteToWideChar(CP_GB18030, 0, ch, -1, nullptr, 0); - auto* m_char = new wchar_t[len]; - MultiByteToWideChar(CP_GB18030, 0, ch, -1, m_char, len); - std::wstring wstr(m_char); - delete[] m_char; - return wstr; + int len = MultiByteToWideChar(CP_GB18030, 0, ch, -1, nullptr, 0); + wchar_t* m_char = new wchar_t[len]; + MultiByteToWideChar(CP_GB18030, 0, ch, -1, m_char, len); + std::wstring wstr(m_char); + delete[] m_char; + return wstr; } + +string printDuringTime(long long seconds) +{ + if (seconds < 0) { + return "N/A"; + } + else if (seconds < 60) { + return std::to_string(seconds) + ""; + } + int mins = seconds / 60; + seconds = seconds % 60; + if (mins < 60) { + return std::to_string(mins) + "" + std::to_string(seconds) + ""; + } + int hours = mins / 60; + mins = mins % 60; + if (hours < 24) { + return std::to_string(hours) + "Сʱ" + std::to_string(mins) + "" + std::to_string(seconds) + ""; + } + int days = hours / 24; + hours = hours % 24; + return std::to_string(days) + "" + std::to_string(hours) + "Сʱ" + std::to_string(mins) + "" + std::to_string(seconds) + ""; +} \ No newline at end of file From 5c270bc16c2dc9b77dbb232952b82d19eabc0abb Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 23 Jul 2020 01:37:16 +0800 Subject: [PATCH 003/171] Merge Update --- Dice/BlackListManager.cpp | 274 ++++++++++------ Dice/BlackListManager.h | 6 + Dice/Dice.cpp | 33 +- Dice/DiceConsole.cpp | 29 +- Dice/DiceConsole.h | 10 +- Dice/DiceEvent.cpp | 352 ++++++++++++++------- Dice/DiceEvent.h | 35 ++- Dice/DiceFile.cpp | 3 +- Dice/DiceFile.hpp | 88 ++---- Dice/DiceJob.cpp | 644 +++++++++++++++++++------------------- Dice/DiceMod.cpp | 23 +- Dice/DiceMod.h | 10 +- Dice/DiceMsgSend.cpp | 6 +- Dice/DiceNetwork.cpp | 12 +- Dice/DiceSchedule.cpp | 2 + Dice/DiceSchedule.h | 2 + Dice/DiceSession.cpp | 21 +- Dice/DiceSession.h | 2 +- Dice/GlobalVar.cpp | 6 +- Dice/Jsonio.cpp | 6 + Dice/Jsonio.h | 8 +- Dice/ManagerSystem.cpp | 92 +++--- Dice/ManagerSystem.h | 35 ++- Dice/MsgFormat.h | 24 +- Dice/strExtern.cpp | 2 +- 25 files changed, 996 insertions(+), 729 deletions(-) diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 92a22be7..395fb447 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -370,7 +370,8 @@ bool DDBlackMark::isSource(long long qq) const { return DiceMaid == qq || masterQQ == qq; } -void DDBlackMark::check_remit(){ +void DDBlackMark::check_remit() +{ if (fromGroup.first && groupset(fromGroup.first, "") > 0)erase(); if (fromQQ.first && trustedQQ(fromQQ.first) > 1)erase(); if (inviterQQ.first && trustedQQ(inviterQQ.first) > 1)inviterQQ.second = 0; @@ -426,7 +427,8 @@ int DDBlackMark::check_cloud() return -1; } -int DDBlackManager::find(const DDBlackMark& mark) { +int DDBlackManager::find(const DDBlackMark& mark) +{ std::lock_guard lock_queue(blacklistMutex); unordered_set sRange; if (mark.wid && mCloud.count(mark.wid)) return mCloud[mark.wid]; @@ -434,15 +436,19 @@ int DDBlackManager::find(const DDBlackMark& mark) { if (mCloud.count(mark.wid)) return mCloud[mark.wid]; sRange = sIDEmpty; } - if (mark.time.length() == 19) { + if (mark.time.length() == 19) + { unordered_set sTimeRange = sTimeEmpty; - for (auto &[key,id] : multi_range(mTimeIndex, mark.time)) { + for (auto& [key,id] : multi_range(mTimeIndex, mark.time)) + { sTimeRange.insert(id); } - if (sRange.empty()) { + if (sRange.empty()) + { sRange.swap(sTimeRange); } - else { + else + { unordered_set sInter; for (const auto& ele : sRange) @@ -457,16 +463,20 @@ int DDBlackManager::find(const DDBlackMark& mark) { sRange.swap(sInter); } } - if (mark.fromGroup.first) { + if (mark.fromGroup.first) + { unordered_set sGroupRange = sGroupEmpty; - for (auto& [key, id] : multi_range(mGroupIndex, mark.fromGroup.first)) { + for (auto& [key, id] : multi_range(mGroupIndex, mark.fromGroup.first)) + { sGroupRange.insert(id); } if (sGroupRange.empty())return -1; - if (sRange.empty()) { + if (sRange.empty()) + { sRange.swap(sGroupRange); } - else { + else + { unordered_set sInter; // O(n) @@ -482,17 +492,21 @@ int DDBlackManager::find(const DDBlackMark& mark) { sRange.swap(sInter); } } - if (mark.fromQQ.first) { + if (mark.fromQQ.first) + { unordered_set sQQRange = sQQEmpty; - for (auto& [key, id] : multi_range(mQQIndex, mark.fromQQ.first)) { + for (auto& [key, id] : multi_range(mQQIndex, mark.fromQQ.first)) + { if (Enabled)console.log("ƥû¼" + to_string(id), 0); sQQRange.insert(id); } if (sQQRange.empty())return -1; - if (sRange.empty()) { + if (sRange.empty()) + { sRange.swap(sQQRange); } - else { + else + { unordered_set sInter; for (const auto& ele : sRange) @@ -507,45 +521,55 @@ int DDBlackManager::find(const DDBlackMark& mark) { sRange.swap(sInter); } } - for (auto i : sRange) { + for (auto i : sRange) + { if (vBlackList[i].isSame(mark))return i; } return -1; } -DDBlackMark& DDBlackMark::operator<<(const DDBlackMark& mark) { +DDBlackMark& DDBlackMark::operator<<(const DDBlackMark& mark) +{ // int delta_danger = mark.danger - danger; if (type == "null" && mark.type != "null")type = mark.type; - if (time.empty() && !mark.time.empty()) { + if (time.empty() && !mark.time.empty()) + { time = mark.time; } if (note != mark.note && (note.empty() || count_char(note, '?') > count_char(mark.note, '?') || note.length() < mark.note.length()) - ) { + ) + { note = mark.note; } - if (mark.fromGroup.first) { + if (mark.fromGroup.first) + { fromGroup = mark.fromGroup; } - if (mark.fromQQ.first) { + if (mark.fromQQ.first) + { fromQQ = mark.fromQQ; } - if (mark.inviterQQ.first) { + if (mark.inviterQQ.first) + { inviterQQ = mark.inviterQQ; } - if (mark.ownerQQ.first) { + if (mark.ownerQQ.first) + { ownerQQ = mark.ownerQQ; } if (!DiceMaid && mark.DiceMaid)DiceMaid = mark.DiceMaid; if (!masterQQ && mark.masterQQ)masterQQ = mark.masterQQ; //save comment if the mark changed at this update - if (!mark.comment.empty()) { + if (!mark.comment.empty()) + { comment = mark.comment; } return *this; } -void DDBlackManager::insert(DDBlackMark& ex_mark) { +void DDBlackManager::insert(DDBlackMark& ex_mark) +{ std::lock_guard lock_queue(blacklistMutex); unsigned id = vBlackList.size(); vBlackList.push_back(ex_mark); @@ -554,48 +578,62 @@ void DDBlackManager::insert(DDBlackMark& ex_mark) { else sIDEmpty.insert(id); if (mark.time.length() == 19)mTimeIndex.emplace(mark.time, id); else sTimeEmpty.insert(id); - if (mark.fromGroup.first) { + if (mark.fromGroup.first) + { mGroupIndex.emplace(mark.fromGroup.first, id); - if (mark.fromGroup.second) { + if (mark.fromGroup.second) + { up_group_danger(mark.fromGroup.first, mark); } } - else { + else + { sGroupEmpty.insert(id); } - if (mark.fromQQ.first) { + if (mark.fromQQ.first) + { mQQIndex.emplace(mark.fromQQ.first, id); - if (mark.fromQQ.second) { + if (mark.fromQQ.second) + { up_qq_danger(mark.fromQQ.first, mark); } } - else { + else + { sQQEmpty.insert(id); } - if (mark.inviterQQ.first) { + if (mark.inviterQQ.first) + { mQQIndex.emplace(mark.inviterQQ.first, id); - if (mark.inviterQQ.second) { + if (mark.inviterQQ.second) + { up_qq_danger(mark.inviterQQ.first, mark); } } - if (mark.ownerQQ.first) { + if (mark.ownerQQ.first) + { mQQIndex.emplace(mark.ownerQQ.first, id); - if (mark.ownerQQ.second) { + if (mark.ownerQQ.second) + { up_qq_danger(mark.ownerQQ.first, mark); } } if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); } -bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) { + +bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) +{ std::lock_guard lock_queue(blacklistMutex); DDBlackMark& old_mark = vBlackList[id]; int delta_danger = mark.danger - old_mark.danger; bool isUpdated = false; - if (delta_danger) { + if (delta_danger) + { old_mark.danger = mark.danger; isUpdated = true; } - if (mark.wid && !old_mark.wid) { + if (mark.wid && !old_mark.wid) + { sIDEmpty.erase(id); old_mark.wid = mark.wid; mCloud.emplace(old_mark.wid, id); @@ -608,95 +646,121 @@ bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) if (mark.time.length() == 19)mTimeIndex.emplace(old_mark.time, id); } if (old_mark.note != mark.note && - (old_mark.note.empty() || count_char(old_mark.note, '?') > count_char(mark.note, '?') || old_mark.note.length() < mark.note.length()) - ) { + (old_mark.note.empty() || count_char(old_mark.note, '?') > count_char(mark.note, '?') || old_mark.note.length() + < mark.note.length()) + ) + { old_mark.note = mark.note; } - if (mark.fromGroup.first) { - if (!old_mark.fromGroup.first) { + if (mark.fromGroup.first) + { + if (!old_mark.fromGroup.first) + { sGroupEmpty.erase(id); mGroupIndex.emplace(mark.fromGroup.first, id); - if (mark.fromGroup.second) { + if (mark.fromGroup.second) + { up_group_danger(mark.fromGroup.first, mark); } old_mark.fromGroup = mark.fromGroup; isUpdated = true; } - else if (mark.fromGroup.second != old_mark.fromGroup.second) { - if (old_mark.fromGroup.second) { + else if (mark.fromGroup.second != old_mark.fromGroup.second) + { + if (old_mark.fromGroup.second) + { old_mark.fromGroup.second = false; reset_group_danger(mark.fromGroup.first); isUpdated = true; } - else if (delta_danger > 0 || credit > 2){ + else if (delta_danger > 0 || credit > 2) + { old_mark.fromGroup.second = true; up_group_danger(mark.fromGroup.first, mark); isUpdated = true; } } } - if (mark.fromQQ.first) { - if (!old_mark.fromQQ.first) { + if (mark.fromQQ.first) + { + if (!old_mark.fromQQ.first) + { sQQEmpty.erase(id); mQQIndex.emplace(mark.fromQQ.first, id); old_mark.fromQQ = mark.fromQQ; - if (mark.fromQQ.second) { + if (mark.fromQQ.second) + { up_qq_danger(mark.fromQQ.first, mark); } isUpdated = true; } - else if (mark.fromQQ.second != old_mark.fromQQ.second) { - if (old_mark.fromQQ.second) { + else if (mark.fromQQ.second != old_mark.fromQQ.second) + { + if (old_mark.fromQQ.second) + { old_mark.fromQQ.second = false; reset_qq_danger(mark.fromQQ.first); isUpdated = true; } - else if (delta_danger > 0 || credit > 2) { + else if (delta_danger > 0 || credit > 2) + { old_mark.fromQQ.second = true; up_qq_danger(mark.fromQQ.first, mark); isUpdated = true; } } } - if (mark.inviterQQ.first) { - if (!old_mark.inviterQQ.first) { + if (mark.inviterQQ.first) + { + if (!old_mark.inviterQQ.first) + { mQQIndex.emplace(mark.inviterQQ.first, id); old_mark.inviterQQ = mark.inviterQQ; - if (mark.inviterQQ.second) { + if (mark.inviterQQ.second) + { up_qq_danger(mark.inviterQQ.first, mark); } isUpdated = true; } - else if (mark.inviterQQ.second != old_mark.inviterQQ.second) { - if (old_mark.inviterQQ.second) { + else if (mark.inviterQQ.second != old_mark.inviterQQ.second) + { + if (old_mark.inviterQQ.second) + { old_mark.inviterQQ.second = false; reset_qq_danger(mark.inviterQQ.first); isUpdated = true; } - else if (delta_danger > 0 || credit > 2) { + else if (delta_danger > 0 || credit > 2) + { old_mark.inviterQQ.second = true; up_qq_danger(mark.inviterQQ.first, mark); isUpdated = true; } } } - if (mark.ownerQQ.first) { - if (!old_mark.ownerQQ.first) { + if (mark.ownerQQ.first) + { + if (!old_mark.ownerQQ.first) + { sQQEmpty.erase(id); mQQIndex.emplace(mark.ownerQQ.first, id); old_mark.ownerQQ = mark.ownerQQ; - if (mark.ownerQQ.second) { + if (mark.ownerQQ.second) + { up_qq_danger(mark.ownerQQ.first, mark); } isUpdated = true; } - else if (mark.ownerQQ.second != old_mark.ownerQQ.second) { - if (old_mark.ownerQQ.second) { + else if (mark.ownerQQ.second != old_mark.ownerQQ.second) + { + if (old_mark.ownerQQ.second) + { old_mark.ownerQQ.second = false; reset_qq_danger(mark.ownerQQ.first); isUpdated = true; } - else if (delta_danger > 0 || credit > 2) { + else if (delta_danger > 0 || credit > 2) + { old_mark.ownerQQ.second = true; up_qq_danger(mark.ownerQQ.first, mark); isUpdated = true; @@ -706,7 +770,8 @@ bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) if (!old_mark.DiceMaid && mark.DiceMaid)old_mark.DiceMaid = mark.DiceMaid; if (!old_mark.masterQQ && mark.masterQQ)old_mark.masterQQ = mark.masterQQ; //save comment if the mark changed at this update - if (isUpdated) { + if (isUpdated) + { if (!mark.comment.empty())old_mark.comment = mark.comment; if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); } @@ -1002,63 +1067,83 @@ void DDBlackManager::add_black_qq(long long llqq, FromMsg* msg) insert(mark); msg->note("" + printQQ(llqq) + "ıغ¼"); } -void DDBlackManager::verify(void* pJson, long long operateQQ) { - DDBlackMark mark{ pJson }; + +void DDBlackManager::verify(void* pJson, long long operateQQ) +{ + DDBlackMark mark{pJson}; if (!mark.isValid)return; int credit = isReliable(operateQQ); //ݿǷм¼:-1=;0=ע;1=δȷ;2=ȷ; int is_cloud = -1; - if (console["CloudBlackShare"]) { - if (mark.wid) { + if (console["CloudBlackShare"]) + { + if (mark.wid) + { string strInfo; - if ((is_cloud = getCloudBlackMark(mark.wid, strInfo)) > -1) { - try { + if ((is_cloud = getCloudBlackMark(mark.wid, strInfo)) > -1) + { + try + { nlohmann::json j = nlohmann::json::parse(strInfo); if (j["isErased"].get())is_cloud = 0; else if (j["isCheck"].get())is_cloud = 2; - if (mark.fromQQ.first) { + if (mark.fromQQ.first) + { if (mark.fromQQ.first != j["fromQQ"].get())return; } - else { - if (!mark.isClear)mark.fromQQ = { j["fromQQ"].get() ,true }; + else + { + if (!mark.isClear)mark.fromQQ = {j["fromQQ"].get(), true}; } - if (mark.fromGroup.first) { + if (mark.fromGroup.first) + { if (mark.fromGroup.first != j["fromGroup"].get())return; } - else { - if (!mark.isClear)mark.fromGroup = { j["fromGroup"].get() ,true }; + else + { + if (!mark.isClear)mark.fromGroup = {j["fromGroup"].get(), true}; } mark.DiceMaid = j["DiceMaid"].get(); mark.masterQQ = j["masterQQ"].get(); mark.type = j["type"].get(); mark.time = j["time"].get(); - if (mark.note.empty()) { - if (j.count("note") && !j["note"].get().empty())mark.note = UTF8toGBK(j["note"].get()); + if (mark.note.empty()) + { + if (j.count("note") && !j["note"].get().empty())mark.note = UTF8toGBK( + j["note"].get()); else mark.fill_note(); } if (credit < 3 || !mark.danger)mark.danger = 2; } - catch (...) { + catch (...) + { console.log("ƶͬʧ:wid=" + to_string(mark.wid), 0); //mark.wid = 0; } } - else { + else + { console.log("ƶ˺ʧ:wid=" + to_string(mark.wid), 0); mark.wid = 0; } } - else { + else + { is_cloud = mark.check_cloud(); } } - else{ + else + { mark.wid = 0; } - if (credit < 3) { - if ((mark.isType("kick") && !console["ListenGroupKick"]) || (mark.isType("ban") && !console["ListenGroupBan"]) || (mark.isType("spam") && !console["ListenSpam"]))return; + if (credit < 3) + { + if ((mark.isType("kick") && !console["ListenGroupKick"]) || (mark.isType("ban") && !console["ListenGroupBan"]) + || (mark.isType("spam") && !console["ListenSpam"]))return; if (mark.type == "local" || mark.type == "other" || mark.isSource(console.DiceMaid)) { - if (credit > 0)console.log(getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼(δ):\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); + if (credit > 0)console.log( + getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼(δ):\n!warning" + UTF8toGBK( + static_cast(pJson)->dump()), 1, printSTNow()); return; } } @@ -1067,29 +1152,36 @@ void DDBlackManager::verify(void* pJson, long long operateQQ) { mark.check_remit(); int index = find(mark); //¼¼ - if (index < 0) { + if (index < 0) + { //߻һк if (credit < 0 || get_qq_danger(mark.DiceMaid) || get_qq_danger(mark.masterQQ))return; if (!mark.isType())return; //ƶȷϼ¼Ҳ - if (is_cloud < 0 || is_cloud == 1) { + if (is_cloud < 0 || is_cloud == 1) + { if (mark.type == "ruler" || credit == 0)return; } //ƶעͬע - else if(is_cloud == 0){ + else if (is_cloud == 0) + { if (!mark.isClear && credit < 3)mark.erase(); } - if (credit < 3) { + if (credit < 3) + { if (is_cloud < 1 && mark.type == "extern")return; } - else { + else + { if (mark.type == "local" && credit < 4)return; } if (mark.fromGroup.first && (groupset(mark.fromGroup.first, "") > 0 || groupset(mark.fromGroup.first, "ЭЧ") > 0 || ExceptGroups.count(mark.fromGroup.first)))return; insert(mark); console.log(getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼" + to_string(vBlackList.size() - 1) + ":\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); } - else { //м¼ + else + { + //м¼ DDBlackMark& old_mark = vBlackList[index]; bool isSource = operateQQ == old_mark.DiceMaid || operateQQ == old_mark.masterQQ; //ΣյȼȨ޸ diff --git a/Dice/BlackListManager.h b/Dice/BlackListManager.h index 3cebd799..e997df4b 100644 --- a/Dice/BlackListManager.h +++ b/Dice/BlackListManager.h @@ -152,6 +152,12 @@ class DDBlackMarkFactory return *this; } + Factory& comment(string strNote) + { + mark.comment = strNote; + return *this; + } + Factory& inviterQQ(long long qq) { mark.inviterQQ = {qq, true}; diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 5ffcef91..9ccf09f3 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -443,7 +443,8 @@ EVE_Enable(eventEnable) // getDiceList(); getExceptGroup(); - console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, printSTNow()); + console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, + printSTNow()); llStartTime = clock(); return 0; } @@ -505,7 +506,8 @@ bool eve_GroupAdd(Chat& grp) cntUser++; ave_trust += getUser(each.QQID).nTrust; } - if (each.permissions > 1) { + if (each.permissions > 1) + { max_trust |= (1 << trustedQQ(each.QQID)); if (blacklist->get_qq_danger(each.QQID) > 1) { @@ -529,7 +531,8 @@ bool eve_GroupAdd(Chat& grp) ave_trust += ginf.nGroupSize * trustedQQ(each.QQID); strMsg += "Ⱥ" + printQQ(each.QQID) + ""; } - else { + else + { ave_trust += ginf.nGroupSize * trustedQQ(each.QQID) / 10; } } @@ -564,7 +567,8 @@ bool eve_GroupAdd(Chat& grp) strMsg += strNote; } if (console["Private"] && !grp.isset("ʹ")) - { //СȺƹûϰ + { + //СȺƹûϰ if (max_trust > 1 || ave_trust > 0.5) { grp.set("ʹ"); @@ -590,13 +594,13 @@ bool eve_GroupAdd(Chat& grp) if (!GlobalMsg["strAddGroup"].empty()) { this_thread::sleep_for(2s); - AddMsgToQueue(getMsg("strAddGroup"), { fromGroup, msgtype::Group }); + AddMsgToQueue(getMsg("strAddGroup"), {fromGroup, msgtype::Group}); } if (console["CheckGroupLicense"] && !grp.isset("ʹ")) { grp.set("δ"); this_thread::sleep_for(2s); - AddMsgToQueue(getMsg("strGroupLicenseDeny"), { fromGroup, msgtype::Group }); + AddMsgToQueue(getMsg("strGroupLicenseDeny"), {fromGroup, msgtype::Group}); } return false; } @@ -642,7 +646,7 @@ EVE_DiscussMsg_EX(eventDiscussMsg) if (blacklist->get_qq_danger(eve.fromQQ) && console["AutoClearBlack"]) { const string strMsg = "ֺû" + printQQ(eve.fromQQ) + "ԶִȺ"; - console.log(printChat({ eve.fromDiscuss, msgtype::Discuss }) + strMsg, 0b10, printSTNow()); + console.log(printChat({eve.fromDiscuss, msgtype::Discuss}) + strMsg, 0b10, printSTNow()); grp.leave(strMsg); return; } @@ -728,9 +732,10 @@ EVE_System_GroupMemberDecrease(eventGroupMemberDecrease) string strNote = printQQ(fromQQ) + "" + printQQ(beingOperateQQ) + "Ƴ" + printChat(grp); console.log(strNote, 0b1000, strNow); if (!console["ListenGroupKick"] || trustedQQ(fromQQ) > 1 || grp.isset("") || grp.isset("ЭЧ") || ExceptGroups.count(fromGroup)) return 0; - DDBlackMarkFactory mark{ fromQQ ,fromGroup }; + DDBlackMarkFactory mark{fromQQ, fromGroup}; mark.sign().type("kick").time(strNow).note(strNow + " " + strNote).comment(getMsg("strSelfCall") + "ԭ¼"); - if (grp.inviter && trustedQQ(grp.inviter) < 2) { + if (grp.inviter && trustedQQ(grp.inviter) < 2) + { strNote += ";Ⱥߣ" + printQQ(grp.inviter); if (console["KickedBanInviter"])mark.inviterQQ(grp.inviter).note(strNow + " " + strNote); } @@ -744,7 +749,7 @@ EVE_System_GroupMemberDecrease(eventGroupMemberDecrease) string strNote = printQQ(fromQQ) + "" + printQQ(beingOperateQQ) + "Ƴ" + printChat(grp); console.log(strNote, 0b1000, strNow); if (trustedQQ(fromQQ) > 1 || grp.isset("") || grp.isset("ЭЧ") || ExceptGroups.count(fromGroup)) return 0; - DDBlackMarkFactory mark{ fromQQ ,fromGroup }; + DDBlackMarkFactory mark{fromQQ, fromGroup}; mark.type("kick").time(strNow).note(strNow + " " + strNote).DiceMaid(beingOperateQQ).masterQQ(mDiceList[beingOperateQQ]).comment(strNow + " " + printQQ(console.DiceMaid) + "Ŀ"); grp.reset("ʹ").reset(""); blacklist->create(mark.product()); @@ -783,7 +788,8 @@ EVE_System_GroupBan(eventGroupBan) if (!console)return 0; mark.sign().comment(getMsg("strSelfCall") + "ԭ¼"); } - else { + else + { mark.DiceMaid(beingOperateQQ).masterQQ(mDiceList[beingOperateQQ]).comment(strNow + " " + printQQ(console.DiceMaid) + "Ŀ"); } //ͳȺڹ @@ -824,8 +830,9 @@ EVE_Request_AddGroup(eventGroupInvited) if (subType == 2 && groupset(fromGroup, "") < 1) { this_thread::sleep_for(3s); - string strNow = printSTNow(); - string strMsg = "Ⱥԣ" + printQQ(fromQQ) + ",Ⱥ:" + to_string(fromGroup) + ""; + const string strNow = printSTNow(); + string strMsg = "Ⱥԣ" + printQQ(fromQQ) + ",Ⱥ:" + + to_string(fromGroup) + ""; if (blacklist->get_group_danger(fromGroup)) { strMsg += "\nѾܾȺںУ"; diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 1b4ae43b..cd3d9f90 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -166,7 +166,7 @@ void Console::newMaster(long long qq) { masterQQ = qq; getUser(qq).trust(5); - setNotice({ qq,CQ::msgtype::Private }, 0b111111); + setNotice({qq, CQ::msgtype::Private}, 0b111111); save(); AddMsgToQueue(getMsg("strNewMaster"), qq); } @@ -326,24 +326,31 @@ bool operator<(const Console::Clock clock, const SYSTEMTIME& st) } //׼ʱ - void ConsoleTimer() { - Console::Clock clockNow{ stNow.wHour,stNow.wMinute }; - while (Enabled) { + void ConsoleTimer() + { + Console::Clock clockNow{stNow.wHour,stNow.wMinute}; + while (Enabled) + { GetLocalTime(&stNow); //ʱ䶯 - if (stTmp.wMinute != stNow.wMinute) { + if (stTmp.wMinute != stNow.wMinute) + { stTmp = stNow; - clockNow = { stNow.wHour,stNow.wMinute }; - for (auto &[clock,eve_type] : multi_range(console.mWorkClock, clockNow)) { - switch (eve_type) { + clockNow = {stNow.wHour, stNow.wMinute}; + for (const auto& [clock,eve_type] : multi_range(console.mWorkClock, clockNow)) + { + switch (eve_type) + { case ClockEvent::on: - if (console["DisabledGlobal"]) { + if (console["DisabledGlobal"]) + { console.set("DisabledGlobal", 0); console.log(getMsg("strClockToWork"), 0b10000, ""); } break; case ClockEvent::off: - if (!console["DisabledGlobal"]) { + if (!console["DisabledGlobal"]) + { console.set("DisabledGlobal", 1); console.log(getMsg("strClockOffWork"), 0b10000, ""); } @@ -356,7 +363,7 @@ bool operator<(const Console::Clock clock, const SYSTEMTIME& st) if (clearGroup("black")) console.log(GlobalMsg["strSelfName"] + "ʱȺɡ", 1, printSTNow()); break; - default:break; + default: break; } } } diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index d57836b9..a34aaa5c 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -165,13 +165,19 @@ void getExceptGroup(); extern std::map mDiceList; //ȡб void getDiceList(); - struct fromMsg { + + struct fromMsg + { std::string strMsg; long long fromQQ = 0; long long fromGroup = 0; fromMsg() = default; - fromMsg(std::string msg, long long QQ, long long Group) :strMsg(msg), fromQQ(QQ), fromGroup(Group) {}; + + fromMsg(std::string msg, long long QQ, long long Group) : strMsg(std::move(msg)), fromQQ(QQ), fromGroup(Group) + { + }; }; + //֪ͨ //һ extern int clearGroup(std::string strPara = "unpower", long long fromQQ = 0); diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 870106ae..79e527fb 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -9,6 +9,8 @@ #include "DiceSession.h" #include "GetRule.h" #include "CQAPI.h" +#include "DiceNetwork.h" +#include "DiceCloud.h" #include using namespace std; using namespace CQ; @@ -49,12 +51,13 @@ int FromMsg::AdminEvent(const string& strOption) if (console["DisabledJrrp"])res << "ȫֽ.jrrp"; if (console["DisabledDraw"])res << "ȫֽ.draw"; if (console["DisabledSend"])res << "ȫֽ.send"; - if (trusted > 3) res << "Ⱥ" + to_string(getGroupList().size()) + if (trusted > 3) + res << "Ⱥ" + to_string(getGroupList().size()) << "Ⱥ¼" + to_string(ChatList.size()) - << ""+to_string(getFriendList().size()) + << "" + to_string(getFriendList().size()) << "û¼" + to_string(UserList.size()) << "û" + to_string(today->cnt()) - << (PList.size() ? "ɫ¼" + to_string(PList.size()) : "") + << (!PList.empty() ? "ɫ¼" + to_string(PList.size()) : "") << "û" + to_string(blacklist->mQQDanger.size()) << "Ⱥ" + to_string(blacklist->mGroupDanger.size()); reply(GlobalMsg["strSelfName"] + "ĵǰ" + res.show()); @@ -87,7 +90,7 @@ int FromMsg::AdminEvent(const string& strOption) { note("ѾԱȨޡ", 0b100); getUser(fromQQ).trust(3); - console.NoticeList.erase({ fromQQ,msgtype::Private }); + console.NoticeList.erase({fromQQ, msgtype::Private}); return 1; } if (strOption == "on") @@ -411,7 +414,8 @@ int FromMsg::AdminEvent(const string& strOption) else { chat(llGroup).set("ʹ").reset("δ"); - if (!chat(llGroup).isset("") && !chat(llGroup).isset("δ"))AddMsgToQueue(getMsg("strAuthorized"), llGroup, Group); + if (!chat(llGroup).isset("") && !chat(llGroup).isset("δ"))AddMsgToQueue( + getMsg("strAuthorized"), llGroup, msgtype::Group); note("" + printGroup(llGroup) + "" + GlobalMsg["strSelfName"] + "ʹ"); } } @@ -438,130 +442,171 @@ int FromMsg::AdminEvent(const string& strOption) reply("ǰָƵ" + to_string(FrqMonitor::getFrqTotal())); return 1; } - else { + else + { bool boolErase = false; strVar["note"] = readPara(); - if (strMsg[intMsgCnt] == '-') { + if (strMsg[intMsgCnt] == '-') + { boolErase = true; intMsgCnt++; } if (strMsg[intMsgCnt] == '+') { intMsgCnt++; } long long llTargetID = readID(); - if (strOption == "dismiss") { - if (ChatList.count(llTargetID)) { + if (strOption == "dismiss") + { + if (ChatList.count(llTargetID)) + { note("" + GlobalMsg["strSelfName"] + "˳" + printChat(chat(llTargetID)), 0b10); chat(llTargetID).reset("").leave(); } - else { + else + { reply(GlobalMsg["strGroupGetErr"]); } return 1; } - else if (strOption == "boton") { - if (getGroupList().count(llTargetID)) { - if (groupset(llTargetID, "ָͣ") > 0) { + else if (strOption == "boton") + { + if (getGroupList().count(llTargetID)) + { + if (groupset(llTargetID, "ָͣ") > 0) + { chat(llTargetID).reset("ָͣ"); note("" + GlobalMsg["strSelfName"] + "" + printGroup(llTargetID) + "ָ"); } else reply(GlobalMsg["strSelfName"] + "ڸȺָ!"); } - else { + else + { reply(GlobalMsg["strGroupGetErr"]); } } - else if (strOption == "botoff") { - if (groupset(llTargetID, "ָͣ") < 1) { + else if (strOption == "botoff") + { + if (groupset(llTargetID, "ָͣ") < 1) + { chat(llTargetID).set("ָͣ"); note("" + GlobalMsg["strSelfName"] + "" + printGroup(llTargetID) + "ָͣ", 0b1); } else reply(GlobalMsg["strSelfName"] + "ڸȺָͣ!"); return 1; } - else if (strOption == "blackgroup") { - if (llTargetID == 0) { + else if (strOption == "blackgroup") + { + if (llTargetID == 0) + { strReply = "ǰȺб"; - for (auto [each, danger] : blacklist->mGroupDanger) { + for (auto [each, danger] : blacklist->mGroupDanger) + { strReply += "\n" + to_string(each); } reply(); return 1; } strVar["time"] = printSTNow(); - do { - if (boolErase) { + do + { + if (boolErase) + { blacklist->rm_black_group(llTargetID, this); } - else { + else + { blacklist->add_black_group(llTargetID, this); } - } while ((llTargetID = readID())); + } + while ((llTargetID = readID())); return 1; } - else if (strOption == "whiteqq") { - if (llTargetID == 0) { + else if (strOption == "whiteqq") + { + if (llTargetID == 0) + { strReply = "ǰûб"; - for (auto& [qq, user] : UserList) { + for (auto& [qq, user] : UserList) + { if (user.nTrust)strReply += "\n" + printQQ(qq) + ":" + to_string(user.nTrust); } reply(); return 1; } - do { - if (boolErase) { - if (trustedQQ(llTargetID)) { - if (trusted <= trustedQQ(llTargetID)) { + do + { + if (boolErase) + { + if (trustedQQ(llTargetID)) + { + if (trusted <= trustedQQ(llTargetID)) + { reply(GlobalMsg["strUserTrustDenied"]); } - else { + else + { getUser(llTargetID).trust(0); note("ջ" + GlobalMsg["strSelfName"] + "" + printQQ(llTargetID) + "Ρ", 0b1); } } - else { + else + { reply(printQQ(llTargetID) + "" + GlobalMsg["strSelfName"] + "İ"); } } - else { - if (trustedQQ(llTargetID)) { + else + { + if (trustedQQ(llTargetID)) + { reply(printQQ(llTargetID) + "Ѽ" + GlobalMsg["strSelfName"] + "İ!"); } - else { + else + { getUser(llTargetID).trust(1); note("" + GlobalMsg["strSelfName"] + "" + printQQ(llTargetID) + "Ρ", 0b1); strVar["user_nick"] = getName(llTargetID); AddMsgToQueue(format(GlobalMsg["strWhiteQQAddNotice"], GlobalMsg, strVar), llTargetID); } } - } while ((llTargetID = readID())); + } + while ((llTargetID = readID())); return 1; } - else if (strOption == "blackqq") { - if (llTargetID == 0) { + else if (strOption == "blackqq") + { + if (llTargetID == 0) + { strReply = "ǰûб"; - for (auto [each, danger] : blacklist->mQQDanger) { + for (auto [each, danger] : blacklist->mQQDanger) + { strReply += "\n" + printQQ(each); } reply(); return 1; } strVar["time"] = printSTNow(); - do { - if (boolErase) { + do + { + if (boolErase) + { blacklist->rm_black_qq(llTargetID, this); } - else { + else + { blacklist->add_black_qq(llTargetID, this); } - } while ((llTargetID = readID())); + } + while ((llTargetID = readID())); return 1; } else reply(GlobalMsg["strAdminOptionEmpty"]); return 0; } } -int FromMsg::MasterSet() { - std::string strOption = readPara(); - if (strOption.empty()) { + +int FromMsg::MasterSet() +{ + const std::string strOption = readPara(); + if (strOption.empty()) + { reply(GlobalMsg["strAdminOptionEmpty"]); return -1; } @@ -582,13 +627,16 @@ int FromMsg::MasterSet() { console.killMaster(); return 1; } - else if (strOption == "reset") { - if (console.master() != fromQQ) { + else if (strOption == "reset") + { + if (console.master() != fromQQ) + { reply(GlobalMsg["strNotMaster"]); return 1; } - string strMaster = readDigit(); - if (strMaster.empty() || stoll(strMaster) == console.master()) { + const string strMaster = readDigit(); + if (strMaster.empty() || stoll(strMaster) == console.master()) + { reply("MasterҪDz{strSelfCall}!"); } else @@ -616,7 +664,7 @@ int FromMsg::MasterSet() { if (trustedQQ(llAdmin) > 3) { note("ջ" + printQQ(llAdmin) + "" + GlobalMsg["strSelfName"] + "ĹȨޡ", 0b100); - console.rmNotice({ llAdmin,msgtype::Private }); + console.rmNotice({llAdmin, msgtype::Private}); getUser(llAdmin).trust(0); } else @@ -633,7 +681,7 @@ int FromMsg::MasterSet() { else { getUser(llAdmin).trust(4); - console.addNotice({ llAdmin, msgtype::Private }, 0b1110); + console.addNotice({llAdmin, msgtype::Private}, 0b1110); note("" + printQQ(llAdmin) + "" + GlobalMsg["strSelfName"] + "ĹȨޡ", 0b100); } } @@ -654,7 +702,7 @@ int FromMsg::DiceReply() { if (strMsg[0] != '.')return 0; intMsgCnt++; - int intT = (int)fromChat.second; + int intT = static_cast(fromChat.second); while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; strVar["nick"] = getName(fromQQ, fromGroup); @@ -683,7 +731,8 @@ int FromMsg::DiceReply() } } if (pGrp->isset("ʹ") && !pGrp->isset("δ"))return 0; - if (trusted > 0) { + if (trusted > 0) + { pGrp->set("ʹ").reset("δ").reset("ЭЧ"); note("Ȩ" + printGroup(pGrp->ID) + "ʹ", 1); AddMsgToQueue(getMsg("strGroupAuthorized", strVar), pGrp->ID, msgtype::Group); @@ -701,7 +750,8 @@ int FromMsg::DiceReply() if (strLowerMessage.substr(intMsgCnt, 7) == "dismiss") { intMsgCnt += 7; - if (!intT) { + if (!intT) + { string QQNum = readDigit(); if (QQNum.empty()) { @@ -723,24 +773,29 @@ int FromMsg::DiceReply() grp.leave(GlobalMsg["strAdminDismiss"]); reply(GlobalMsg["strGroupExit"]); } - else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1){ + else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1) + { reply(GlobalMsg["strDismiss"]); } - else { + else + { reply(GlobalMsg["strPermissionDeniedErr"]); } return 1; } string QQNum = readDigit(); if (QQNum.empty() || QQNum == to_string(console.DiceMaid) || (QQNum.length() == 4 && stoll(QQNum) == getLoginQQ() % 10000)){ - if (trusted > 2) { + if (trusted > 2) + { pGrp->leave(GlobalMsg["strAdminDismiss"]); } if (pGrp->isset("ЭЧ"))return 0; - if (isAuth) { + if (isAuth) + { pGrp->leave(GlobalMsg["strDismiss"]); } - else { + else + { if (!isCalled && (pGrp->isset("ָͣ") || GroupInfo(fromGroup).nGroupSize > 200))AddMsgToQueue(getMsg("strPermissionDeniedErr", strVar), fromQQ); else reply(GlobalMsg["strPermissionDeniedErr"]); } @@ -806,10 +861,12 @@ int FromMsg::DiceReply() } return 1; } - else if (intT != PrivateT && pGrp->isset("ЭЧ")) { + else if (intT != PrivateT && pGrp->isset("ЭЧ")) + { return 0; } - else if (blacklist->get_qq_danger(fromQQ) || (intT != PrivateT && blacklist->get_group_danger(fromGroup))) { + if (blacklist->get_qq_danger(fromQQ) || (intT != PrivateT && blacklist->get_group_danger(fromGroup))) + { return 0; } if (strLowerMessage.substr(intMsgCnt, 3) == "bot") @@ -824,9 +881,12 @@ int FromMsg::DiceReply() { if (console["DisabledGlobal"])reply(GlobalMsg["strGlobalOff"]); else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ"))))reply(GlobalMsg["strGroupLicenseDeny"]); - else if (intT) { - if (isAuth){ - if (groupset(fromGroup, "ָͣ") > 0) { + else if (intT) + { + if (isAuth) + { + if (groupset(fromGroup, "ָͣ") > 0) + { chat(fromGroup).reset("ָͣ"); reply(GlobalMsg["strBotOn"]); } @@ -843,23 +903,29 @@ int FromMsg::DiceReply() } } } - else if (Command == "off"){ - if (isAuth){ - if (groupset(fromGroup, "ָͣ")) { + else if (Command == "off") + { + if (isAuth) + { + if (groupset(fromGroup, "ָͣ")) + { if (!isCalled && QQNum.empty() && pGrp->isGroup && GroupInfo(fromGroup).nGroupSize > 200)AddMsgToQueue(getMsg("strBotOffAlready", strVar), fromQQ); else reply(GlobalMsg["strBotOffAlready"]); } - else { + else + { chat(fromGroup).set("ָͣ"); reply(GlobalMsg["strBotOff"]); } } - else { + else + { if (groupset(fromGroup, "ָͣ"))AddMsgToQueue(getMsg("strPermissionDeniedErr", strVar), fromQQ); else reply(GlobalMsg["strPermissionDeniedErr"]); } } - else if (!Command.empty() && !isCalled && pGrp->isset("ָͣ")) { + else if (!Command.empty() && !isCalled && pGrp->isset("ָͣ")) + { return 0; } else if (intT == GroupT && pGrp->isset("ָͣ") && GroupInfo(fromGroup).nGroupSize >= 500 && !isCalled) @@ -980,7 +1046,9 @@ int FromMsg::DiceReply() } return true; } - else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ")))) { + else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && + !pGrp->isset("ʹ")))) + { return 0; } if (strLowerMessage.substr(intMsgCnt, 7) == "welcome") @@ -1133,7 +1201,6 @@ int FromMsg::DiceReply() reply("Miraiִ֧˹"); return -1; } - if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); @@ -1143,8 +1210,10 @@ int FromMsg::DiceReply() sch.push_job(*this); return 1; } - else if (strOption == "die") { - if (trusted < 5 && fromQQ != console.master()) { + else if (strOption == "die") + { + if (trusted < 5 && fromQQ != console.master()) + { reply(GlobalMsg["strNotMaster"]); return -1; } @@ -1365,11 +1434,13 @@ int FromMsg::DiceReply() res << "¼" + printDate(grp.tCreated); res << "¼" + printDate(grp.tUpdated); if (grp.inviter)res << "ߣ" + printQQ(grp.inviter); - if (isInGroup) { + if (isInGroup) + { res << string("Ⱥӭ") + (grp.isset("Ⱥӭ") ? "" : "δ") << "ûռȣ" + to_string(cntUser * 100 / (grpinfo.nGroupSize - 1)) + "%"; - res << (sInact.size() ? "\n30첻ԾȺԱ" + to_string(sInact.size()) : ""); - if (sBlackQQ.size()) { + res << (!sInact.empty() ? "\n30첻ԾȺԱ" + to_string(sInact.size()) : ""); + if (!sBlackQQ.empty()) + { if (sBlackQQ.size() > 8) res << GlobalMsg["strSelfName"] + "ĺԱ" + to_string(sBlackQQ.size()) + ""; else @@ -1442,68 +1513,86 @@ int FromMsg::DiceReply() else reply(GlobalMsg["strGroupWholeBan"]); return 1; } - else if (Command == "restart") { - if (intPms < 2 && trusted < 4) { + else if (Command == "restart") + { + if (intPms < 2 && trusted < 4) + { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - if(!setGroupWholeBan(llGroup, 0))reply(GlobalMsg["strGroupWholeUnban"]); + if (!setGroupWholeBan(llGroup, 0))reply(GlobalMsg["strGroupWholeUnban"]); return 1; } - else if (Command == "card") { - if (long long llqq = readID()) { - if (trusted < 4 && intPms < 2 && llqq != fromQQ) { + else if (Command == "card") + { + if (long long llqq = readID()) + { + if (trusted < 4 && intPms < 2 && llqq != fromQQ) + { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) { + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) + { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } - while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length())intMsgCnt++; + while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length()) + intMsgCnt++; while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; strVar["card"] = readRest(); strVar["target"] = getName(llqq, llGroup); - if (setGroupCard(llGroup, llqq, strVar["card"])) { + if (setGroupCard(llGroup, llqq, strVar["card"])) + { reply(GlobalMsg["strGroupCardSetErr"]); } - else { + else + { reply(GlobalMsg["strGroupCardSet"]); } } - else { + else + { reply(GlobalMsg["strQQIDEmpty"]); } return 1; } - else if ((intPms < 2 && (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3 || trusted < 5))) { + else if ((intPms < 2 && (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3 || trusted < 5))) + { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - else if (Command == "ban") { - if (trusted < 4) { + else if (Command == "ban") + { + if (trusted < 4) + { reply(GlobalMsg["strNotAdmin"]); return -1; } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) { + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) + { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } string QQNum = readDigit(); - if (QQNum.empty()) { + if (QQNum.empty()) + { reply(GlobalMsg["strQQIDEmpty"]); return -1; } long long llMemberQQ = stoll(QQNum); GroupMemberInfo Member = getGroupMemberInfo(llGroup, llMemberQQ); - if (Member.QQID == llMemberQQ){ + if (Member.QQID == llMemberQQ) + { strVar["member"] = getName(Member.QQID, llGroup); - if (Member.permissions > 1) { + if (Member.permissions > 1) + { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } string strMainDice = readDice(); - if (strMainDice.empty()) { + if (strMainDice.empty()) + { reply(GlobalMsg["strValueErr"]); return -1; } @@ -1520,36 +1609,45 @@ int FromMsg::DiceReply() } else reply("{self}޴ȺԱ"); } - else if (Command == "kick") { - if (trusted < 4) { + else if (Command == "kick") +{ + if (trusted < 4) + { reply(GlobalMsg["strNotAdmin"]); return -1; } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) { + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) + { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } long long llMemberQQ = readID(); - if (!llMemberQQ) { + if (!llMemberQQ) + { reply(GlobalMsg["strQQIDEmpty"]); return -1; } ResList resKicked, resDenied, resNotFound; GroupMemberInfo Member; - do { + do + { Member = getGroupMemberInfo(llGroup, llMemberQQ); - if (Member.QQID == llMemberQQ) { - if (Member.permissions > 1) { + if (Member.QQID == llMemberQQ) + { + if (Member.permissions > 1) + { resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; continue; } - if (setGroupKick(llGroup, llMemberQQ, false) == 0) { + if (setGroupKick(llGroup, llMemberQQ, false) == 0) + { resKicked << Member.Nick + "(" + to_string(Member.QQID) + ")"; } else resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; } else resNotFound << to_string(llMemberQQ); - } while ((llMemberQQ = readID())); + } + while ((llMemberQQ = readID())); strReply = GlobalMsg["strSelfName"]; if (!resKicked.empty())strReply += "ƳȺԱ" + resKicked.show() + "\n"; if (!resDenied.empty())strReply += "Ƴʧܣ" + resDenied.show() + "\n"; @@ -1557,24 +1655,31 @@ int FromMsg::DiceReply() reply(); return 1; } - else if (Command == "title") { - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3) { + else if (Command == "title") +{ + if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3) + { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } - if (long long llqq = readID()) { - while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length())intMsgCnt++; + if (long long llqq = readID()) + { + while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length()) + intMsgCnt++; while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; strVar["title"] = readRest(); - if (setGroupSpecialTitle(llGroup, llqq, strVar["title"])) { + if (setGroupSpecialTitle(llGroup, llqq, strVar["title"])) + { reply(GlobalMsg["strGroupTitleSetErr"]); } - else { + else + { strVar["target"] = getName(llqq, llGroup); reply(GlobalMsg["strGroupTitleSet"]); } } - else { + else + { reply(GlobalMsg["strQQIDEmpty"]); } return 1; @@ -2982,7 +3087,9 @@ int FromMsg::DiceReply() string strTurnCnt = strMsg.substr(intMsgCnt, strMsg.find('#') - intMsgCnt); //#ܷʶЧ if (strTurnCnt.empty())intMsgCnt++; - else if ((strTurnCnt.length() == 1 && isdigit(static_cast(strTurnCnt[0]))) || strTurnCnt == "10") { + else if ((strTurnCnt.length() == 1 && isdigit(static_cast(strTurnCnt[0]))) || strTurnCnt == + "10") + { intMsgCnt += strTurnCnt.length() + 1; intTurnCnt = stoi(strTurnCnt); } @@ -4034,7 +4141,7 @@ int FromMsg::CustomReply() { strVar["nick"] = getName(fromQQ, fromGroup); strVar["pc"] = getPCName(fromQQ, fromGroup); - strVar["at"] = fromType != msgtype::Private ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; + strVar["at"] = fromChat.second != msgtype::Private ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; } reply(CardDeck::drawCard(deck->second, true)); AddFrq(fromQQ, fromTime, fromChat); @@ -4076,8 +4183,10 @@ bool FromMsg::DiceFilter() if (fromChat.second == msgtype::Private) isCalled = true; trusted = trustedQQ(fromQQ); isBotOff = (console["DisabledGlobal"] && (trusted < 4 || !isCalled)) || (!(isCalled && console["DisabledListenAt"]) && (groupset(fromGroup, "ָͣ") > 0)); - if (DiceReply()) { - if (isAns) { + if (DiceReply()) + { + if (isAns) + { AddFrq(fromQQ, fromTime, fromChat); getUser(fromQQ).update(fromTime); } @@ -4093,12 +4202,14 @@ int FromMsg::readNum(int& num) { string strNum; while (intMsgCnt < strMsg.length() && !isdigit(static_cast(strMsg[intMsgCnt])) && strMsg[intMsgCnt] != '-')intMsgCnt++; - if (strMsg[intMsgCnt] == '-') { + if (strMsg[intMsgCnt] == '-') + { strNum += '-'; intMsgCnt++; } if (intMsgCnt >= strMsg.length())return -1; - while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) { + while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) + { strNum += strMsg[intMsgCnt]; intMsgCnt++; } @@ -4128,7 +4239,6 @@ int FromMsg::readChat(chatType& ct, bool isReroll) { ct.second = CQ::msgtype::Group; } - else if (strT == "discuss") { ct.second = CQ::msgtype::Discuss; @@ -4138,7 +4248,7 @@ int FromMsg::readChat(chatType& ct, bool isReroll) if (isReroll)intMsgCnt = intFormor; return -1; } - if (long long llID = readID(); llID) + if (const long long llID = readID(); llID) { ct.first = llID; return 0; diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 0807495c..d4464ecf 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -7,9 +7,12 @@ #include #include #include +#include #include "CQAPI_EX.h" #include "MsgMonitor.h" #include "DiceSchedule.h" +#include "DiceMsgSend.h" +#include "GlobalVar.h" using std::string; @@ -27,7 +30,7 @@ class FromMsg :public DiceJobDetail { bool isBlock = false; - void reply(const std::string& strReply, bool isFormat) + void reply(std::string strReply, bool isFormat) { isAns = true; if (isFormat) @@ -35,13 +38,14 @@ class FromMsg :public DiceJobDetail { else AddMsgToQueue(strReply, fromChat); } - void reply(const std::string& strReply, const std::initializer_list replace_str = {}, + void reply(std::string strReply, const std::initializer_list replace_str = {}, bool isFormat = true) { isAns = true; while (isspace(static_cast(strReply[0]))) strReply.erase(strReply.begin()); - if (!isFormat) { + if (!isFormat) + { AddMsgToQueue(strReply, fromChat); return; } @@ -67,8 +71,9 @@ class FromMsg :public DiceJobDetail { fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); reply(strMsg); - string note = getName(fromQQ) + strMsg; - for (const auto &[ct,level] : console.NoticeList) { + const string note = getName(fromQQ) + strMsg; + for (const auto& [ct,level] : console.NoticeList) + { if (!(level & note_lv) || pair(fromQQ, CQ::msgtype::Private) == ct || ct == fromChat)continue; AddMsgToQueue(note, ct); } @@ -116,7 +121,8 @@ class FromMsg :public DiceJobDetail { } //ո - void readSkipSpace() { + void readSkipSpace() + { while (intMsgCnt < strMsg.length() && isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt++; } @@ -130,7 +136,8 @@ class FromMsg :public DiceJobDetail { { string strPara; readSkipSpace(); - while (intMsgCnt < strMsg.length() && !isspace(static_cast(strLowerMessage[intMsgCnt]))) { + while (intMsgCnt < strMsg.length() && !isspace(static_cast(strLowerMessage[intMsgCnt]))) + { strPara += strMsg[intMsgCnt]; intMsgCnt++; } @@ -138,9 +145,10 @@ class FromMsg :public DiceJobDetail { } //ȡǿոհ׷ - string readUntilTab() { + string readUntilTab() + { while (intMsgCnt < strMsg.length() && isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; - int intBegin = intMsgCnt; + const int intBegin = intMsgCnt; int intEnd = intBegin; const unsigned int len = strMsg.length(); while (intMsgCnt < len && (!isspace(static_cast(strMsg[intMsgCnt])) || strMsg[intMsgCnt] == ' ')) @@ -179,12 +187,14 @@ class FromMsg :public DiceJobDetail { string readDigit(bool isForce = true) { string strMum; - if (isForce)while (intMsgCnt < strMsg.length() && !isdigit(static_cast(strMsg[intMsgCnt]))) { + if (isForce)while (intMsgCnt < strMsg.length() && !isdigit(static_cast(strMsg[intMsgCnt]))) + { if (strMsg[intMsgCnt] < 0)intMsgCnt++; intMsgCnt++; } else while(intMsgCnt < strMsg.length() && isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; - while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) { + while (intMsgCnt < strMsg.length() && isdigit(static_cast(strMsg[intMsgCnt]))) + { strMum += strMsg[intMsgCnt]; intMsgCnt++; } @@ -334,7 +344,8 @@ class FromMsg :public DiceJobDetail { if (strMsg[intMsgCnt] == ':' || strMsg[intMsgCnt] == '.')intMsgCnt++; if (strMsg.substr(intMsgCnt, 2) == "")intMsgCnt += 2; readSkipSpace(); - if (intMsgCnt >= strMsg.length() || !isdigit(static_cast(strMsg[intMsgCnt]))) { + if (intMsgCnt >= strMsg.length() || !isdigit(static_cast(strMsg[intMsgCnt]))) + { cc.second = 0; return 0; } diff --git a/Dice/DiceFile.cpp b/Dice/DiceFile.cpp index 6de8d81b..d5ed3bc9 100644 --- a/Dice/DiceFile.cpp +++ b/Dice/DiceFile.cpp @@ -18,7 +18,8 @@ int mkDir(const std::string& dir) return -2;*/ } -int clrDir(std::string dir, const std::unordered_set& exceptList) { +int clrDir(const std::string& dir, const std::unordered_set& exceptList) +{ int nCnt = 0; std::error_code err; for (const auto& p : std::filesystem::directory_iterator(dir, err)) diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index 7bf6257c..c05cc0ae 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -32,7 +32,7 @@ using std::unordered_map; int mkDir(const std::string& dir); -int clrDir(std::string dir, const unordered_set& exceptList); +int clrDir(const std::string& dir, const unordered_set& exceptList); template void map_merge(map& m1, const map& m2) @@ -214,26 +214,9 @@ int loadFile(std::string strPath, std::unordered_set& setTmp) return -1; } -template -int loadFile(std::string strPath, std::unordered_set& setTmp) { - std::ifstream fin(strPath); - if (fin) - { - int Cnt = 0; - T item; - while (fscan(fin, item)) - { - setTmp.insert(item); - Cnt++; - } - return Cnt; - } - fin.close(); - return -1; -} - -template -int loadFile(std::string strPath, std::map&mapTmp) { +template +int loadFile(std::string strPath, std::map& mapTmp) +{ std::ifstream fin(strPath); if (fin) { @@ -250,8 +233,9 @@ int loadFile(std::string strPath, std::map&mapTmp) { return -1; } -template -int loadFile(std::string strPath, std::unordered_map& mapTmp) { +template +int loadFile(std::string strPath, std::unordered_map& mapTmp) +{ std::ifstream fin(strPath); if (fin) { @@ -268,8 +252,9 @@ int loadFile(std::string strPath, std::unordered_map& mapTmp) { return -1; } -template -void loadFile(std::string strPath, std::multimap&mapTmp) { +template +void loadFile(std::string strPath, std::multimap& mapTmp) +{ std::ifstream fin(strPath); if (fin) { @@ -319,21 +304,6 @@ int loadBFile(std::string strPath, std::unordered_map& m) return Cnt; } -template -int loadBFile(std::string strPath, std::unordered_map& m) { - std::ifstream fin(strPath, std::ios::in | std::ios::binary); - if (!fin)return -1; - int len = fread(fin); - int Cnt = 0; - T key; - C val; - while (fin.peek() != EOF && len > Cnt++) { - key = fread(fin); - m[key].readb(fin); - } - fin.close(); - return Cnt; -} //ȡαini template int loadINI(std::string strPath, std::map& m) @@ -365,7 +335,7 @@ int loadXML(const std::string& strPath, std::map& m) } //ļ -int listDir(const string& dir, vector& files, bool isSub = false) noexcept; +int listDir(const string& dir, vector& files, bool isSub = false); template int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2& tmp, int& intFile, int& intFailure, @@ -378,8 +348,9 @@ int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2 { intFile++; string path = convert_w2a(p.path().filename().wstring().c_str()); - int Cnt = load(strDir + path, tmp); - if (Cnt < 0) { + const int Cnt = load(strDir + path, tmp); + if (Cnt < 0) + { files.push_back(path); intFailure++; } @@ -541,27 +512,14 @@ void saveFile(std::string strPath, const unordered_map& mTmp) fout.close(); } - -template -void saveFile(std::string strPath, const unordered_map& mTmp) { - if (clrEmpty(strPath, mTmp))return; - std::ofstream fout(strPath); - for (const auto& [key, val] : mTmp) - { - fout << key << "\t"; - fprint(fout, val); - fout << std::endl; - } - fout.close(); -} - -template -void saveBFile(std::string strPath, std::map& m) { +template +void saveBFile(std::string strPath, std::map& m) +{ if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); const int len = m.size(); fwrite(fout, len); - for (auto& [key, val] : m) + for (auto& [key,val] : m) { fwrite(fout, key); fwrite(fout, val); @@ -569,13 +527,15 @@ void saveBFile(std::string strPath, std::map& m) { fout.close(); } -template -void saveBFile(std::string strPath, std::unordered_map& m) { +template +void saveBFile(std::string strPath, std::unordered_map& m) +{ if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); - int len = m.size(); + const int len = m.size(); fwrite(fout, len); - for (auto& [key, val] : m) { + for (auto& [key, val] : m) + { fwrite(fout, key); fwrite(fout, val); } diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index e0e70951..bc66c128 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -1,323 +1,325 @@ -#pragma once -#include "DiceJob.h" -#include "DiceConsole.h" -#include -#include -#include "StrExtern.hpp" -#include "ManagerSystem.h" -#include "DiceCloud.h" -#include "BlackListManager.h" -#pragma warning(disable:28159) - -using namespace std; -using namespace CQ; - -int sendSelf(const string msg) { - static long long selfQQ = CQ::getLoginQQ(); - return CQ::sendPrivateMsg(selfQQ, msg); -} - -void cq_exit(DiceJob& job) { - int pid = _getpid(); - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(pe32); - HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hProcessSnap == INVALID_HANDLE_VALUE) { - job.note("ʧܣ̿մʧܣ", 1); - } - BOOL bResult = Process32First(hProcessSnap, &pe32); - int ppid(0); - while (bResult) { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - break; - } - bResult = Process32Next(hProcessSnap, &pe32); - } - if (!ppid) { - job.note("ʧܣδҵ̣", 1); - } - string strCMD("taskkill /f /pid " + to_string(ppid)); - job.note("" + getMsg("self") + "5ɱ", 1); - std::this_thread::sleep_for(5s); - job.echo(strCMD); - Enabled = false; - dataBackUp(); - system(strCMD.c_str()); -} -void cq_restart(DiceJob& job) { - char** path = new char* (); - _get_pgmptr(path); - string strSelfPath(*path); - delete path; - string strSelfName; - int pid = _getpid(); - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(pe32); - HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hProcessSnap == INVALID_HANDLE_VALUE) { - job.note("ʧܣ̿մʧܣ", 1); - } - BOOL bResult = Process32First(hProcessSnap, &pe32); - int ppid(0); - while (bResult) { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - job.echo("ȷϽ" + strSelfPath + "\nid:" + to_string(pe32.th32ProcessID) + "\nid:" + to_string(pe32.th32ParentProcessID)); - strSelfName = pe32.szExeFile; - break; - } - bResult = Process32Next(hProcessSnap, &pe32); - } - if (!ppid) { - job.note("ʧܣδҵ̣", 1); - } - string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + to_string(console.DiceMaid); - ofstream fout("reload.bat"); - fout << command << std::endl; - fout.close(); - job.note(command, 0); - std::this_thread::sleep_for(3s); - Enabled = false; - dataBackUp(); - switch (UINT res = -1; res = WinExec(".\\reload.bat", SW_SHOW)) { - case 0: - job.note("ʧܣڴԴѺľ", 1); - break; - case ERROR_FILE_NOT_FOUND: - job.note("ʧܣָļδҵ", 1); - break; - case ERROR_PATH_NOT_FOUND: - job.note("ʧܣָ·δҵ", 1); - break; - default: - if (res > 31)job.note("ɹ", 0); - else job.note("ʧܣδ֪" + to_string(res), 0); - break; - } -} - -void auto_save(DiceJob& job) { - if (sch.is_job_cold("autosave"))return; - dataBackUp(); - console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); - if (console["AutoSaveInterval"] > 0) { - sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"]); - sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); - } -} - -void check_system(DiceJob& job) { - static int perRAM(0), perLastRAM(0); - static double perLastCPU(0), perLastDisk(0), - perCPU(0), perDisk(0); - static bool isAlarmRAM(false), isAlarmCPU(false), isAlarmDisk(false); - static double mbFreeBytes = 0, mbTotalBytes = 0; - //ڴ - if (console["SystemAlarmRAM"] > 0) { - perRAM = getRamPort(); - if (perRAM > console["SystemAlarmRAM"] && perRAM > perLastRAM) { - console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳڴռô" + to_string(perRAM) + "%", 0b1000, printSTime(stNow)); - perLastRAM = perRAM; - isAlarmRAM = true; - } - else if (perLastRAM > console["SystemAlarmRAM"] && perRAM < console["SystemAlarmRAM"]) { - console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳڴռý" + to_string(perRAM) + "%", 0b10, printSTime(stNow)); - perLastRAM = perRAM; - isAlarmRAM = false; - } - } - //CPU - if (console["SystemAlarmCPU"] > 0) { - perCPU = getWinCpuUsage() / 10.0; - if (perCPU > console["SystemAlarmCPU"] && (!isAlarmCPU || perCPU > perLastCPU + 1)) { - console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳCPUռô" + toString(perCPU) + "%", 0b1000, printSTime(stNow)); - perLastCPU = perCPU; - isAlarmCPU = true; - } - else if (perLastCPU > console["SystemAlarmCPU"] && perCPU < console["SystemAlarmCPU"]) { - console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳCPUռý" + toString(perCPU) + "%", 0b10, printSTime(stNow)); - perLastCPU = perCPU; - isAlarmCPU = false; - } - } - //Ӳ̼ - if (console["SystemAlarmRAM"] > 0) { - perDisk = getDiskUsage(mbFreeBytes, mbTotalBytes) / 10.0; - if (perDisk > console["SystemAlarmDisk"] && (!isAlarmDisk || perDisk > perLastDisk + 1)) { - console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳӲռô" + toString(perDisk) + "%", 0b1000, printSTime(stNow)); - perLastDisk = perDisk; - isAlarmDisk = true; - } - else if (perLastDisk > console["SystemAlarmDisk"] && perDisk < console["SystemAlarmDisk"]) { - console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳӲռý" + toString(perDisk) + "%", 0b10, printSTime(stNow)); - perLastDisk = perDisk; - isAlarmDisk = false; - } - } - if (isAlarmRAM || isAlarmCPU || isAlarmDisk) { - sch.add_job_for(5 * 60, job); - } - else { - sch.add_job_for(30 * 60, job); - } -} - -//õͼƬб -void clear_image(DiceJob& job) { - if (!job.fromQQ) { - if (sch.is_job_cold("clrimage"))return; - if (console["AutoClearImage"] <= 0) { - sch.add_job_for(60 * 60, job); - return; - } - } - scanImage(GlobalMsg, sReferencedImage); - scanImage(HelpDoc, sReferencedImage); - scanImage(CardDeck::mPublicDeck, sReferencedImage); - scanImage(CardDeck::mReplyDeck, sReferencedImage); - scanImage(CardDeck::mGroupDeck, sReferencedImage); - scanImage(CardDeck::mPrivateDeck, sReferencedImage); - for (auto it : ChatList) { - scanImage(it.second.strConf, sReferencedImage); - } - job.note("" + GlobalMsg["strSelfName"] + "ͼƬ" + to_string(sReferencedImage.size()) + "", 0b0); - int cnt = clrDir("data\\image\\", sReferencedImage); - job.note("imageļ"+ to_string(cnt) + "", 1); - if (console["AutoClearImage"] > 0) { - sch.refresh_cold("clrimage", time(NULL) + console["AutoClearImage"]); - sch.add_job_for(console["AutoClearImage"] * 60 * 60, "clrimage"); - } -} - -void clear_group(DiceJob& job) { - int intCnt = 0; - ResList res; - std::mapstrVar; - if (job.strVar["clear_mode"] == "unpower") { - for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; - if (grp.isGroup && getGroupMemberInfo(id, console.DiceMaid).permissions == 1) { - res << printGroup(id); - grp.leave(getMsg("strLeaveNoPower")); - intCnt++; - this_thread::sleep_for(3s); - } - } - console.log(GlobalMsg["strSelfName"] + "ɸȺȨȺ" + to_string(intCnt) + ":" + res.show(), 0b10, printSTNow()); - } - else if (isdigit(static_cast(job.strVar["clear_mode"][0]))) { - int intDayLim = stoi(job.strVar["clear_mode"]); - string strDayLim = to_string(intDayLim); - time_t tNow = time(NULL); - for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; - time_t tLast = grp.tLastMsg; - if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0)tLast = tLMT; - if (!tLast)continue; - int intDay = (int)(tNow - tLast) / 86400; - if (intDay > intDayLim) { - strVar["day"] = to_string(intDay); - res << printGroup(id) + ":" + to_string(intDay) + "\n"; - grp.leave(getMsg("strLeaveUnused", strVar)); - intCnt++; - this_thread::sleep_for(2s); - } - } - console.log(GlobalMsg["strSelfName"] + "ɸDZˮ" + strDayLim + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10, printSTNow()); - } - else if (job.strVar["clear_mode"] == "black") { - try { - for (auto& [id, grp_name] : getGroupList()) { - Chat& grp = chat(id).group().name(grp_name); - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset(""))continue; - if (blacklist->get_group_danger(id)) { - res << printGroup(id) + "" + "Ⱥ"; - if (console["LeaveBlackGroup"])grp.leave(getMsg("strBlackGroup")); - } - vector MemberList = getGroupMemberList(id); - for (auto eachQQ : MemberList) { - if (blacklist->get_qq_danger(eachQQ.QQID) > 1) { - if (eachQQ.permissions < getGroupMemberInfo(id, getLoginQQ()).permissions) { - continue; - } - else if (eachQQ.permissions > getGroupMemberInfo(id, getLoginQQ()).permissions) { - res << printChat(grp) + "" + printQQ(eachQQ.QQID) + "ԷȺȨ޽ϸ"; - grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); - intCnt++; - break; - } - else if (console["LeaveBlackQQ"]) { - res << printChat(grp) + "" + printQQ(eachQQ.QQID); - grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); - intCnt++; - break; - } - } - } - } - } - catch (...) { - console.log("ѣ" + GlobalMsg["strSelfName"] + "Ⱥʱ", 0b10, printSTNow()); - } - if (intCnt) { - job.note("Ѱ" + getMsg("strSelfName") + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10); - } - else if (job.fromQQ) { - job.echo(getMsg("strSelfName") + "δִȺ"); - } - } - else if (job["clear_mode"] == "preserve") { - for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("ʹ") || grp.isset(""))continue; - if (grp.isGroup && getGroupMemberInfo(id, console.master()).permissions) { - grp.set("ʹ"); - continue; - } - res << printChat(grp); - grp.leave(getMsg("strPreserve")); - intCnt++; - this_thread::sleep_for(3s); - } - console.log(GlobalMsg["strSelfName"] + "ɸȺ" + to_string(intCnt) + "" + res.show(), 1, printSTNow()); - } - else - job.echo("޷ʶɸѡ"); -} - -// -void cloud_beat(DiceJob& job) { - Cloud::update(); - sch.add_job_for(5 * 60, job); -} - -void dice_update(DiceJob& job) { - job.note("ʼDice\n汾:" + job.strVar["ver"], 1); - char** path = new char* (); - _get_pgmptr(path); - string strAppPath(*path); - strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; - delete path; - string strURL("http://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); - switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { - case -1: - job.echo("ʧ:" + strURL); - break; - case -2: - job.echo("ʧ!ļδҵ:" + strAppPath); - break; - case 0: - job.note("Dice!" + job.strVar["ver"] + "ɹ\n.system reload Ӧø", 1); - } -} - -string print_master() { - return printQQ(console.master()); -} - -string list_deck() { - return listKey(CardDeck::mPublicDeck); -} +#pragma once +#include "DiceJob.h" +#include "DiceConsole.h" +#include +#include +#include "StrExtern.hpp" +#include "ManagerSystem.h" +#include "DiceCloud.h" +#include "BlackListManager.h" +#include "GlobalVar.h" +#include "CardDeck.h" +#pragma warning(disable:28159) + +using namespace std; +using namespace CQ; + +int sendSelf(const string msg) { + static long long selfQQ = CQ::getLoginQQ(); + return CQ::sendPrivateMsg(selfQQ, msg); +} + +void cq_exit(DiceJob& job) { + int pid = _getpid(); + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(pe32); + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) { + job.note("ʧܣ̿մʧܣ", 1); + } + BOOL bResult = Process32First(hProcessSnap, &pe32); + int ppid(0); + while (bResult) { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + break; + } + bResult = Process32Next(hProcessSnap, &pe32); + } + if (!ppid) { + job.note("ʧܣδҵ̣", 1); + } + string strCMD("taskkill /f /pid " + to_string(ppid)); + job.note("" + getMsg("self") + "5ɱ", 1); + std::this_thread::sleep_for(5s); + job.echo(strCMD); + Enabled = false; + dataBackUp(); + system(strCMD.c_str()); +} +void cq_restart(DiceJob& job) { + char** path = new char* (); + _get_pgmptr(path); + string strSelfPath(*path); + delete path; + string strSelfName; + int pid = _getpid(); + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(pe32); + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) { + job.note("ʧܣ̿մʧܣ", 1); + } + BOOL bResult = Process32First(hProcessSnap, &pe32); + int ppid(0); + while (bResult) { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + job.echo("ȷϽ" + strSelfPath + "\nid:" + to_string(pe32.th32ProcessID) + "\nid:" + to_string(pe32.th32ParentProcessID)); + strSelfName = pe32.szExeFile; + break; + } + bResult = Process32Next(hProcessSnap, &pe32); + } + if (!ppid) { + job.note("ʧܣδҵ̣", 1); + } + string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + to_string(console.DiceMaid); + ofstream fout("reload.bat"); + fout << command << std::endl; + fout.close(); + job.note(command, 0); + std::this_thread::sleep_for(3s); + Enabled = false; + dataBackUp(); + switch (UINT res = -1; res = WinExec(".\\reload.bat", SW_SHOW)) { + case 0: + job.note("ʧܣڴԴѺľ", 1); + break; + case ERROR_FILE_NOT_FOUND: + job.note("ʧܣָļδҵ", 1); + break; + case ERROR_PATH_NOT_FOUND: + job.note("ʧܣָ·δҵ", 1); + break; + default: + if (res > 31)job.note("ɹ", 0); + else job.note("ʧܣδ֪" + to_string(res), 0); + break; + } +} + +void auto_save(DiceJob& job) { + if (sch.is_job_cold("autosave"))return; + dataBackUp(); + console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); + if (console["AutoSaveInterval"] > 0) { + sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"]); + sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); + } +} + +void check_system(DiceJob& job) { + static int perRAM(0), perLastRAM(0); + static double perLastCPU(0), perLastDisk(0), + perCPU(0), perDisk(0); + static bool isAlarmRAM(false), isAlarmCPU(false), isAlarmDisk(false); + static double mbFreeBytes = 0, mbTotalBytes = 0; + //ڴ + if (console["SystemAlarmRAM"] > 0) { + perRAM = getRamPort(); + if (perRAM > console["SystemAlarmRAM"] && perRAM > perLastRAM) { + console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳڴռô" + to_string(perRAM) + "%", 0b1000, printSTime(stNow)); + perLastRAM = perRAM; + isAlarmRAM = true; + } + else if (perLastRAM > console["SystemAlarmRAM"] && perRAM < console["SystemAlarmRAM"]) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳڴռý" + to_string(perRAM) + "%", 0b10, printSTime(stNow)); + perLastRAM = perRAM; + isAlarmRAM = false; + } + } + //CPU + if (console["SystemAlarmCPU"] > 0) { + perCPU = getWinCpuUsage() / 10.0; + if (perCPU > console["SystemAlarmCPU"] && (!isAlarmCPU || perCPU > perLastCPU + 1)) { + console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳCPUռô" + toString(perCPU) + "%", 0b1000, printSTime(stNow)); + perLastCPU = perCPU; + isAlarmCPU = true; + } + else if (perLastCPU > console["SystemAlarmCPU"] && perCPU < console["SystemAlarmCPU"]) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳCPUռý" + toString(perCPU) + "%", 0b10, printSTime(stNow)); + perLastCPU = perCPU; + isAlarmCPU = false; + } + } + //Ӳ̼ + if (console["SystemAlarmRAM"] > 0) { + perDisk = getDiskUsage(mbFreeBytes, mbTotalBytes) / 10.0; + if (perDisk > console["SystemAlarmDisk"] && (!isAlarmDisk || perDisk > perLastDisk + 1)) { + console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳӲռô" + toString(perDisk) + "%", 0b1000, printSTime(stNow)); + perLastDisk = perDisk; + isAlarmDisk = true; + } + else if (perLastDisk > console["SystemAlarmDisk"] && perDisk < console["SystemAlarmDisk"]) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "ϵͳӲռý" + toString(perDisk) + "%", 0b10, printSTime(stNow)); + perLastDisk = perDisk; + isAlarmDisk = false; + } + } + if (isAlarmRAM || isAlarmCPU || isAlarmDisk) { + sch.add_job_for(5 * 60, job); + } + else { + sch.add_job_for(30 * 60, job); + } +} + +//õͼƬб +void clear_image(DiceJob& job) { + if (!job.fromQQ) { + if (sch.is_job_cold("clrimage"))return; + if (console["AutoClearImage"] <= 0) { + sch.add_job_for(60 * 60, job); + return; + } + } + scanImage(GlobalMsg, sReferencedImage); + scanImage(HelpDoc, sReferencedImage); + scanImage(CardDeck::mPublicDeck, sReferencedImage); + scanImage(CardDeck::mReplyDeck, sReferencedImage); + scanImage(CardDeck::mGroupDeck, sReferencedImage); + scanImage(CardDeck::mPrivateDeck, sReferencedImage); + for (auto it : ChatList) { + scanImage(it.second.strConf, sReferencedImage); + } + job.note("" + GlobalMsg["strSelfName"] + "ͼƬ" + to_string(sReferencedImage.size()) + "", 0b0); + int cnt = clrDir("data\\image\\", sReferencedImage); + job.note("imageļ"+ to_string(cnt) + "", 1); + if (console["AutoClearImage"] > 0) { + sch.refresh_cold("clrimage", time(NULL) + console["AutoClearImage"]); + sch.add_job_for(console["AutoClearImage"] * 60 * 60, "clrimage"); + } +} + +void clear_group(DiceJob& job) { + int intCnt = 0; + ResList res; + std::mapstrVar; + if (job.strVar["clear_mode"] == "unpower") { + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; + if (grp.isGroup && getGroupMemberInfo(id, console.DiceMaid).permissions == 1) { + res << printGroup(id); + grp.leave(getMsg("strLeaveNoPower")); + intCnt++; + this_thread::sleep_for(3s); + } + } + console.log(GlobalMsg["strSelfName"] + "ɸȺȨȺ" + to_string(intCnt) + ":" + res.show(), 0b10, printSTNow()); + } + else if (isdigit(static_cast(job.strVar["clear_mode"][0]))) { + int intDayLim = stoi(job.strVar["clear_mode"]); + string strDayLim = to_string(intDayLim); + time_t tNow = time(NULL); + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; + time_t tLast = grp.tLastMsg; + if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0)tLast = tLMT; + if (!tLast)continue; + int intDay = (int)(tNow - tLast) / 86400; + if (intDay > intDayLim) { + strVar["day"] = to_string(intDay); + res << printGroup(id) + ":" + to_string(intDay) + "\n"; + grp.leave(getMsg("strLeaveUnused", strVar)); + intCnt++; + this_thread::sleep_for(2s); + } + } + console.log(GlobalMsg["strSelfName"] + "ɸDZˮ" + strDayLim + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10, printSTNow()); + } + else if (job.strVar["clear_mode"] == "black") { + try { + for (auto& [id, grp_name] : getGroupList()) { + Chat& grp = chat(id).group().name(grp_name); + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset(""))continue; + if (blacklist->get_group_danger(id)) { + res << printGroup(id) + "" + "Ⱥ"; + if (console["LeaveBlackGroup"])grp.leave(getMsg("strBlackGroup")); + } + vector MemberList = getGroupMemberList(id); + for (auto eachQQ : MemberList) { + if (blacklist->get_qq_danger(eachQQ.QQID) > 1) { + if (eachQQ.permissions < getGroupMemberInfo(id, getLoginQQ()).permissions) { + continue; + } + else if (eachQQ.permissions > getGroupMemberInfo(id, getLoginQQ()).permissions) { + res << printChat(grp) + "" + printQQ(eachQQ.QQID) + "ԷȺȨ޽ϸ"; + grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); + intCnt++; + break; + } + else if (console["LeaveBlackQQ"]) { + res << printChat(grp) + "" + printQQ(eachQQ.QQID); + grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); + intCnt++; + break; + } + } + } + } + } + catch (...) { + console.log("ѣ" + GlobalMsg["strSelfName"] + "Ⱥʱ", 0b10, printSTNow()); + } + if (intCnt) { + job.note("Ѱ" + getMsg("strSelfName") + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10); + } + else if (job.fromQQ) { + job.echo(getMsg("strSelfName") + "δִȺ"); + } + } + else if (job["clear_mode"] == "preserve") { + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("ʹ") || grp.isset(""))continue; + if (grp.isGroup && getGroupMemberInfo(id, console.master()).permissions) { + grp.set("ʹ"); + continue; + } + res << printChat(grp); + grp.leave(getMsg("strPreserve")); + intCnt++; + this_thread::sleep_for(3s); + } + console.log(GlobalMsg["strSelfName"] + "ɸȺ" + to_string(intCnt) + "" + res.show(), 1, printSTNow()); + } + else + job.echo("޷ʶɸѡ"); +} + +// +void cloud_beat(DiceJob& job) { + Cloud::update(); + sch.add_job_for(5 * 60, job); +} + +void dice_update(DiceJob& job) { + job.note("ʼDice\n汾:" + job.strVar["ver"], 1); + char** path = new char* (); + _get_pgmptr(path); + string strAppPath(*path); + strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; + delete path; + string strURL("http://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); + switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { + case -1: + job.echo("ʧ:" + strURL); + break; + case -2: + job.echo("ʧ!ļδҵ:" + strAppPath); + break; + case 0: + job.note("Dice!" + job.strVar["ver"] + "ɹ\n.system reload Ӧø", 1); + } +} + +string print_master() { + return printQQ(console.master()); +} + +string list_deck() { + return listKey(CardDeck::mPublicDeck); +} string list_extern_deck() { - return listKey(CardDeck::mExternPublicDeck); + return listKey(CardDeck::mExternPublicDeck); } \ No newline at end of file diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 78c1bc07..30c2624d 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -34,10 +34,12 @@ string DiceModManager::format(string s, const map& dict } string key = s.substr(l + 1, r - l - 1), val; auto it = dict.find(key); - if (it != dict.end()) { + if (it != dict.end()) + { val = format(it->second, dict, mod_name); } - else if (auto func = strFuncs.find(key); func != strFuncs.end()) { + else if (auto func = strFuncs.find(key); func != strFuncs.end()) + { val = func->second(); } else continue; @@ -67,15 +69,18 @@ void DiceModManager::rm_help(const string& key) helpdoc.erase(key); } -int DiceModManager::load(string& strLog) { +int DiceModManager::load(string& strLog) +{ vector sFile; vector sFileErr; int cntFile = listDir(DiceDir + "\\mod\\", sFile, true); - int cntItem{ 0 }; + int cntItem{0}; if (cntFile <= 0)return cntFile; - for (auto& filename : sFile) { + for (auto& filename : sFile) + { nlohmann::json j = freadJson(filename); - if (j.is_null()) { + if (j.is_null()) + { sFileErr.push_back(filename.filename().string()); continue; } @@ -83,12 +88,14 @@ int DiceModManager::load(string& strLog) { { cntItem += readJMap(j["helpdoc"], helpdoc); } - if (j.count("global_char")) { + if (j.count("global_char")) + { cntItem += readJMap(j["global_char"], GlobalChar); } } strLog += "ȡ" + DiceDir + "\\mod\\е" + std::to_string(cntFile) + "ļ, " + std::to_string(cntItem) + "Ŀ\n"; - if (!sFileErr.empty()) { + if (!sFileErr.empty()) + { strLog += "ȡʧ" + std::to_string(sFileErr.size()) + ":\n"; for (auto& it : sFileErr) { diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index 060604fe..877b2504 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -13,7 +13,8 @@ using std::string; using std::vector; using std::map; -class DiceGenerator { +class DiceGenerator +{ //ȴʱ //int cold_time; //γȡ @@ -30,7 +31,8 @@ class BaseDeck vector cards; }; -class DiceMod{ +class DiceMod +{ string mod_name; string auther; string ver; @@ -46,7 +48,9 @@ class DiceMod{ map> private_deck, map> public_deck, map generator*/ - ):mod_name(name), m_helpdoc(helpdoc) /*,m_private_deck(private_deck),m_public_deck(public_deck),m_generator(generator)*/ { + ) : mod_name(std::move(name)), m_helpdoc(std::move(helpdoc)) + /*,m_private_deck(private_deck),m_public_deck(public_deck),m_generator(generator)*/ + { } friend class DiceModFactory; }; diff --git a/Dice/DiceMsgSend.cpp b/Dice/DiceMsgSend.cpp index d51273bc..f387ee47 100644 --- a/Dice/DiceMsgSend.cpp +++ b/Dice/DiceMsgSend.cpp @@ -78,8 +78,10 @@ void SendMsg() msgQueue.pop(); } } - if (!msg.msg.empty()){ - if (int pos = msg.msg.find('\f'); pos != string::npos) { + if (!msg.msg.empty()) + { + if (int pos = msg.msg.find('\f'); pos != string::npos) + { AddMsgToQueue(msg.msg.substr(pos + 1), msg.target_id, msg.msg_type); msg.msg = msg.msg.substr(0, pos); } diff --git a/Dice/DiceNetwork.cpp b/Dice/DiceNetwork.cpp index fb31d74b..556cd100 100644 --- a/Dice/DiceNetwork.cpp +++ b/Dice/DiceNetwork.cpp @@ -93,8 +93,10 @@ namespace Network const char* header = "Content-Type: application/x-www-form-urlencoded"; const HINTERNET hInternet = InternetOpenA(DiceRequestHeader, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); - const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, INTERNET_SERVICE_HTTP, 0, 0); - const HINTERNET hRequest = HttpOpenRequestA(hConnect, "POST", objectName, "HTTP/1.1", nullptr, acceptTypes, 0, 0); + const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, + INTERNET_SERVICE_HTTP, 0, 0); + const HINTERNET hRequest = HttpOpenRequestA(hConnect, "POST", objectName, "HTTP/1.1", nullptr, acceptTypes, 0, + 0); const BOOL res = HttpSendRequestA(hRequest, header, strlen(header), frmdata, strlen(frmdata)); @@ -193,8 +195,10 @@ namespace Network const char* acceptTypes[] = {"*/*", nullptr}; const HINTERNET hInternet = InternetOpenA(DiceRequestHeader, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); - const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, INTERNET_SERVICE_HTTP, 0, 0); - const HINTERNET hRequest = HttpOpenRequestA(hConnect, "GET", objectName, "HTTP/1.1", nullptr, acceptTypes, INTERNET_FLAG_NO_CACHE_WRITE, 0); + const HINTERNET hConnect = InternetConnectA(hInternet, serverName, port, nullptr, nullptr, + INTERNET_SERVICE_HTTP, 0, 0); + const HINTERNET hRequest = HttpOpenRequestA(hConnect, "GET", objectName, "HTTP/1.1", nullptr, acceptTypes, + INTERNET_FLAG_NO_CACHE_WRITE, 0); const BOOL res = HttpSendRequestA(hRequest, nullptr, 0, nullptr, 0); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index d97c5381..e3fa3001 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -1,9 +1,11 @@ #include #include #include +#include "GlobalVar.h" #include "DiceJob.h" #include "ManagerSystem.h" #include "Jsonio.h" +#include "DiceSchedule.h" unordered_map mCommand = { {"syscheck",check_system}, diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index a4bce12d..4d6132b1 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -7,6 +7,8 @@ #include #include #include "DiceConsole.h" +#include "Jsonio.h" +#include "json.hpp" using std::string; using std::map; diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index aaf04cd6..2e9c7ea5 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -148,26 +148,33 @@ void DiceTableMaster::save() } } -int DiceTableMaster::load() { +int DiceTableMaster::load() +{ string strLog; std::unique_lock lock(sessionMutex); vector sFile; int cnt = listDir(DiceDir + "\\user\\session\\", sFile); if (cnt <= 0)return cnt; - for (auto& filename : sFile) { + for (auto& filename : sFile) + { nlohmann::json j = freadJson(filename); - if (j.is_null()) { + if (j.is_null()) + { cnt--; continue; } - if (j["type"] == "simple") { + if (j["type"] == "simple") + { auto pSession(std::make_shared(j["room"])); pSession->create(j["create_time"]).update(j["update_time"]); if (j.count("observer")) pSession->sOB = j["observer"].get>(); - if (j.count("tables")) { - for (nlohmann::json::iterator itTable = j["tables"].begin(); itTable != j["tables"].end(); ++itTable){ + if (j.count("tables")) + { + for (nlohmann::json::iterator itTable = j["tables"].begin(); itTable != j["tables"].end(); ++itTable) + { string strTable = UTF8toGBK(itTable.key()); - for (nlohmann::json::iterator itItem = itTable.value().begin(); itItem != j.end(); ++itItem) { + for (nlohmann::json::iterator itItem = itTable.value().begin(); itItem != j.end(); ++itItem) + { pSession->mTable[strTable].emplace(UTF8toGBK(itItem.key()), itItem.value()); } } diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index 6d821f2e..38539bdb 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -94,7 +94,7 @@ class DiceTableMaster Session& session(long long group); void session_end(long long group); void save(); - int load() const; + int load(); }; inline std::unique_ptr gm; diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 1cc237ed..04ca1635 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -32,7 +32,6 @@ bool Enabled = false; bool Mirai = false; std::string Dice_Full_Ver_For = Dice_Full_Ver + " For CoolQ]"; -std::string strModulePath; std::string strModulePath; @@ -320,7 +319,8 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( -560:֧Mirai +564:๦Żƶѵ +560:֧Mirai, GUI 558:ÿͳ 557:ʱҵϵͳ 556:ϵͳ @@ -334,7 +334,7 @@ const std::map HelpDoc = { 547:ָ 537:.send)"}, {"Э","0.ЭShiki(The StarDeathJudgementThe World)ķЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, -{"","鿴Դ:https://github.com/mystringEmpty/Dice\n:https://github.com/mystringEmpty/Dice/releases\nûֲ:http://shiki.stringempty.xyz/download/Shiki_User_Manual.pdf\nֲ:http://shiki.stringempty.xyz/download/Shiki_Master_Manual.pdf\nֲ:http://shiki.stringempty.xyz/download/DiceMaid_CookBook.html\n(ĵ)https://dice.c-j.dev/\nst│:http://shiki.stringempty.xyz/download/COC7_player_card_shiki.xlsx"}, +{"","Dice!̳: https://kokona.tech \n Dice!̳: https://forum.kokona.tech"}, {"趨","Master{master_QQ}\n.meʹãֹ\n.jrrpʹã\n봦ƣǽ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n\nȺ:δã\nٷ(ˮ)Ⱥ: 624807593 941980833 882747577\n˽Ⱥ863062599\nȺ1029435374"}, {"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, {"ָ",R"(atָָﵥӦat.bot off diff --git a/Dice/Jsonio.cpp b/Dice/Jsonio.cpp index 10984cf8..55b9d239 100644 --- a/Dice/Jsonio.cpp +++ b/Dice/Jsonio.cpp @@ -34,3 +34,9 @@ nlohmann::json freadJson(const std::filesystem::path& path) } return j; } + +void fwriteJson(std::string strPath, const json& j) +{ + std::ofstream fout(strPath); + fout << std::setw(2) << j; +} \ No newline at end of file diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index 891714c3..4a655e86 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -56,7 +56,7 @@ std::enable_if_t, T> readJKey(const std::string& strJson return stoll(strJson); } -nlohmann::json freadJson(std::string strPath); +nlohmann::json freadJson(const std::string& strPath); nlohmann::json freadJson(const std::filesystem::path& path); void fwriteJson(std::string strPath, const json& j); @@ -94,10 +94,12 @@ template int loadJMap(const std::string& strLoc, std::map &mapTmp) { nlohmann::json j = freadJson(strLoc); if (j.is_null())return -2; - try { + try + { return readJMap(j, mapTmp); } - catch (...) { + catch (...) + { return -1; } } diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index b1f2040f..94be615c 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -14,23 +14,24 @@ string DiceDir = "DiceData"; //õͼƬб unordered_set sReferencedImage; -const map mChatConf{//0-ȺԱ2-23-34-Ա5-ϵͳ - {"",4}, - {"Ϣ",0}, - {"ָͣ",0}, - {"ûظ",0}, - {"jrrp",0}, - {"draw",0}, - {"me",0}, - {"help",0}, - {"ob",0}, - {"ʹ",1}, - {"δ",1}, - {"",2}, - {"",4}, - {"ЭЧ",4}, - {"δ",5}, - {"",5} +const map mChatConf{ + //0-ȺԱ2-23-34-Ա5-ϵͳ + {"", 4}, + {"Ϣ", 0}, + {"ָͣ", 0}, + {"ûظ", 0}, + {"jrrp", 0}, + {"draw", 0}, + {"me", 0}, + {"help", 0}, + {"ob", 0}, + {"ʹ", 1}, + {"δ", 1}, + {"", 2}, + {"", 4}, + {"ЭЧ", 4}, + {"δ", 5}, + {"", 5} }; User& getUser(long long qq) @@ -62,7 +63,8 @@ int clearUser() return QQDelete.size(); } -string getName(long long QQ, long long GroupID){ +string getName(long long QQ, long long GroupID) +{ if (QQ == console.DiceMaid)return getMsg("strSelfCall"); string nick; if (UserList.count(QQ) && getUser(QQ).getNick(nick, GroupID))return nick; @@ -70,36 +72,46 @@ string getName(long long QQ, long long GroupID){ if (!(nick = strip(CQ::getStrangerInfo(QQ).nick)).empty())return nick; return GlobalMsg["stranger"] + "(" + to_string(QQ) + ")"; } -void filter_CQcode(string& nick, long long fromGroup) { +void filter_CQcode(string& nick, long long fromGroup) +{ size_t posL(0); - while ((posL = nick.find(CQ_AT)) != string::npos) { + while ((posL = nick.find(CQ_AT)) != string::npos) + { //atʽ - if (size_t posR = nick.find(']',posL); posR != string::npos) { + if (size_t posR = nick.find(']',posL); posR != string::npos) + { std::string_view stvQQ(nick); stvQQ = stvQQ.substr(posL + 10, posR - posL - 10); //QQŸʽ bool isDig = true; - for (auto ch: stvQQ) { - if (!isdigit(static_cast(ch))) { + for (auto ch: stvQQ) + { + if (!isdigit(static_cast(ch))) + { isDig = false; break; } } //ת - if (isDig && posR - posL < 29) { + if (isDig && posR - posL < 29) + { nick.replace(posL, posR - posL + 1, "@" + getName(stoll(string(stvQQ)), fromGroup)); } - else if (stvQQ == "all") { + else if (stvQQ == "all") + { nick.replace(posL, posR - posL + 1, "@ȫԱ"); } - else { + else + { nick.replace(posL, posR - posL + 1, "@"); } } else return; } - while ((posL = nick.find("[CQ:")) != string::npos) { - if (size_t posR = nick.find(']', posL); posR != string::npos) { + while ((posL = nick.find("[CQ:")) != string::npos) + { + if (size_t posR = nick.find(']', posL); posR != string::npos) + { nick.erase(posL, posR - posL + 1); } else return; @@ -123,11 +135,15 @@ Chat& Chat::id(long long grp) { } return *this; } -int groupset(long long id, string st) { + +int groupset(long long id, string st) +{ if (!ChatList.count(id))return -1; return ChatList[id].isset(std::move(st)); } -string printChat(Chat& grp) { + +string printChat(Chat& grp) +{ if (CQ::getGroupList().count(grp.ID))return "[" + CQ::getGroupList()[grp.ID] + "](" + to_string(grp.ID) + ")"; if (!grp.Name.empty())return "[" + grp.Name + "](" + to_string(grp.ID) + ")"; if (grp.isset("Ⱥ"))return "[" + grp.strConf["Ⱥ"] + "](" + to_string(grp.ID) + ")"; @@ -135,7 +151,7 @@ string printChat(Chat& grp) { return "(" + to_string(grp.ID) + ")"; } -void scanImage(string s, unordered_set& list) { +void scanImage(const string& s, unordered_set& list) { int l = 0, r = 0; while ((l = s.find('[', r)) != string::npos && (r = s.find(']', l)) != string::npos) { @@ -147,7 +163,8 @@ void scanImage(string s, unordered_set& list) { } void scanImage(const vector& v, unordered_set& list) { - for (auto it : v) { + for (const auto& it : v) + { scanImage(it, sReferencedImage); } } @@ -169,9 +186,8 @@ __int64 compareFileTime(const FILETIME& ft1, const FILETIME& ft2) return t1 - t2; } -long long getWinCpuUsage() { - HANDLE hEvent; - BOOL res; +long long getWinCpuUsage() +{ FILETIME preidleTime; FILETIME prekernelTime; FILETIME preuserTime; @@ -180,9 +196,9 @@ long long getWinCpuUsage() { FILETIME userTime; if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) return -1; - FILETIME preidleTime = idleTime; - FILETIME prekernelTime = kernelTime; - FILETIME preuserTime = userTime; + preidleTime = idleTime; + prekernelTime = kernelTime; + preuserTime = userTime; Sleep(1000); if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) return -1; diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index 15ff3bbf..db7c24dc 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -194,11 +194,7 @@ class Chat map intConf{}; map strConf{}; - Chat& id(long long grp) - { - ID = grp; - return *this; - } + Chat& id(long long grp); Chat& group() { @@ -359,27 +355,36 @@ ifstream& operator>>(ifstream& fin, Chat& grp); ofstream& operator<<(ofstream& fout, const Chat& grp); extern unordered_setsReferencedImage; -void scanImage(string s, unordered_set& list); + +void scanImage(const string& s, unordered_set& list); void scanImage(const vector& v, unordered_set& list); -template -void scanImage(const map& m, unordered_set& list) { - for (auto it : m) { +template +void scanImage(const map& m, unordered_set& list) +{ + for (const auto& it : m) + { scanImage(it.first, sReferencedImage); scanImage(it.second, sReferencedImage); } } -template -void scanImage(const map& m, unordered_set& list) { - for (auto it : m) { + +template +void scanImage(const map& m, unordered_set& list) +{ + for (const auto& it : m) + { scanImage(it.first, sReferencedImage); scanImage(it.second, sReferencedImage); } } -template -void scanImage(const map& m, unordered_set& list) { - for (auto it : m) { + +template +void scanImage(const map& m, unordered_set& list) +{ + for (const auto& it : m) + { scanImage(it.second, sReferencedImage); } } diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index 55f28e1d..8b889ba5 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -62,17 +62,21 @@ std::string format(std::string s, const std::map string key = s.substr(l + 1, r - l - 1); string val; auto it = replace_str.find(key); - if (it != replace_str.end()) { + if (it != replace_str.end()) + { val = format(it->second, replace_str, str_tmp); } - else if ((it = GlobalChar.find(key)) != GlobalChar.end()) { + else if ((it = GlobalChar.find(key)) != GlobalChar.end()) + { val = it->second; } - else if ((it = str_tmp.find(key)) != str_tmp.end()) { + else if ((it = str_tmp.find(key)) != str_tmp.end()) + { if (key == "res")val = format(it->second, replace_str, str_tmp); else val = it->second; } - else if (auto func = strFuncs.find(key); func != strFuncs.end()) { + else if (auto func = strFuncs.find(key); func != strFuncs.end()) + { val = func->second(); } else continue; @@ -111,14 +115,18 @@ class ResList std::string show() { std::string s; - if (intMaxLen > intLineLen || isLineBreak) { - for (auto it = vRes.begin(); it != vRes.end(); it++) { + if (intMaxLen > intLineLen || isLineBreak) + { + for (auto it = vRes.begin(); it != vRes.end(); it++) + { if (it == vRes.begin())s = "\n" + *it; else s += strLongSepa + *it; } } - else { - for (auto it = vRes.begin(); it != vRes.end(); it++) { + else + { + for (auto it = vRes.begin(); it != vRes.end(); it++) + { if (it == vRes.begin())s = *it; else s += sDot + *it; } diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index 68f219b2..3b8b857f 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -43,7 +43,7 @@ string convert_w2a(const wchar_t* wch) wstring convert_a2w(const char* ch) { - int len = MultiByteToWideChar(CP_GB18030, 0, ch, -1, nullptr, 0); + const int len = MultiByteToWideChar(CP_GB18030, 0, ch, -1, nullptr, 0); wchar_t* m_char = new wchar_t[len]; MultiByteToWideChar(CP_GB18030, 0, ch, -1, m_char, len); std::wstring wstr(m_char); From 50d53ba49b82e7adc15923090b81896926e5d6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Thu, 23 Jul 2020 01:49:07 +0800 Subject: [PATCH 004/171] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f85b59df..3aeba6dc 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Dice! QQ Dice Robot For TRPG Based on CoolQ/Mirai -[![License](https://img.shields.io/github/license/w4123/Dice.svg)](http://www.gnu.org/licenses) -[![Build status](https://ci.appveyor.com/api/projects/status/6qm1l31k07dst0rk?svg=true)](https://ci.appveyor.com/project/w4123/dice) -[![Downloads](https://img.shields.io/github/downloads/w4123/dice/total.svg)](https://github.com/w4123/Dice/releases) -[![GitHub contributors](https://img.shields.io/github/contributors/w4123/dice.svg)](https://github.com/w4123/Dice/graphs/contributors) -[![GitHub last commit](https://img.shields.io/github/last-commit/w4123/dice.svg)](https://github.com/w4123/Dice/commits) +[![License](https://img.shields.io/github/license/Dice-Developer-Team/Dice.svg)](http://www.gnu.org/licenses) +[![Build status](https://ci.appveyor.com/api/projects/status/7uq2qi3348ny1tfv?svg=true)](https://ci.appveyor.com/project/w4123/dice-ovf7o) +[![Downloads](https://img.shields.io/github/downloads/Dice-Developer-Team/dice/total.svg)](https://github.com/Dice-Developer-Team/Dice/releases) +[![GitHub contributors](https://img.shields.io/github/contributors/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/graphs/contributors) +[![GitHub last commit](https://img.shields.io/github/last-commit/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/commits) ## 简介 @@ -13,9 +13,9 @@ Dice!是一款基于酷Q的QQ跑团掷骰机器人 交流QQ群:882747577或94198 主页: -Latest Stable Release: [![GitHub release](https://img.shields.io/github/release/w4123/dice.svg)](https://github.com/w4123/Dice/releases) [![GitHub Release Date](https://img.shields.io/github/release-date/w4123/dice.svg)](https://github.com/w4123/Dice/releases) +Latest Stable Release: [![GitHub release](https://img.shields.io/github/release/Dice-Developer-Team/dice.svg)](https://github.com/w4123/Dice-Developer-Team/releases) [![GitHub Release Date](https://img.shields.io/github/release-date/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/releases) -Latest Release: [![GitHub release](https://img.shields.io/github/release-pre/w4123/dice.svg)](https://github.com/w4123/Dice/releases) [![GitHub Release Date](https://img.shields.io/github/release-date-pre/w4123/dice.svg)](https://github.com/w4123/Dice/releases) +Latest Release: [![GitHub release](https://img.shields.io/github/release-pre/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/releases) [![GitHub Release Date](https://img.shields.io/github/release-date-pre/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/releases) ## 开发者 From 04ad77ee4ecbe2c300210c66a713e98f946909ac Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 23 Jul 2020 14:09:04 +0800 Subject: [PATCH 005/171] Bump Version 2.4.0 --- Dice/Dice.cpp | 4 +- Dice/DiceEvent.cpp | 4 +- Dice/GlobalVar.cpp | 892 ++++++++++++++++++++++----------------------- Dice/GlobalVar.h | 6 +- Dice/app.json | 4 +- 5 files changed, 454 insertions(+), 456 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 9ccf09f3..234ab6e6 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -213,9 +213,9 @@ EVE_Enable(eventEnable) else { sendPrivateMsg(console.DiceMaid, - R"(ӭʹDice!expˣ + R"(ӭʹDice!ˣ ҼQ->Ӧù->˵->MasterģʽлɿMasterģʽĺ̨ -ֲ:http://shiki.stringempty.xyz/download/DiceMaid_CookBook.html +ĵ: https://v2docs.kokona.tech ļο.help )"); } diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 79e527fb..8ac7d74b 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -816,10 +816,10 @@ int FromMsg::DiceReply() if (!console.master()) { console.newMaster(fromQQ); - strReply = "Ķǰ汾Masterְֲ·:shiki.stringempty.xyz/download/Shiki_Master_Manual.pdf"; - strReply += "\nûֲ:shiki.stringempty.xyz/download/Shiki_User_Manual.pdf"; + strReply = "Ķǰ汾MasterֲԼûֲᡣ°棬ע汾ŶӦ: https://v2docs.kokona.tech"; strReply += "\nҪӽ϶ûеȺصIJƼDisabledBlock֤ȺڵľĬ"; strReply += "\nĬϿȺƳԡˢ¼ļҪرֶ"; + strReply += "\nעƺϵͳĬϿ˹رCloudBlackShare"; string strOption = readRest(); if (strOption == "public") { diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index f3d583de..b57c54b7 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -7,7 +7,7 @@ * |_______/ |________| |________| |________| |__| * * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2019 w4123溯洄 + * Copyright (C) 2018-2019 w4123 * * This program is free software: you can redistribute it and/or modify it under the terms * of the GNU Affero General Public License as published by the Free Software Foundation, @@ -43,503 +43,501 @@ CQ::logger DiceLogger("Dice!"); std::map GlobalMsg { - {"strParaEmpty","参数不能为空×"}, //偷懒用万能回复 - {"strParaIllegal","参数非法×"}, //偷懒用万能回复 - {"stranger","用户"}, //{nick}无法获取非空昵称时的称呼 - {"strAdminOptionEmpty","找{self}有什么事么?{nick}"}, // - {"strGMTableShow","{self}记录的{table_name}列表:"}, - {"strGMTableNotExist","{self}没有保存的{table_name}记录"}, - {"strUserTrustShow","{user}在{self}处的信任级别为{trust}"}, - {"strUserTrusted","已将{self}对{user}的信任级别调整为{trust}"}, - {"strUserTrustDenied","{nick}在{self}处无权访问对方的权限×"}, - {"strUserTrustIllegal","将目标权限修改为{trust}是非法的×"}, - {"strUserNotFound","{self}无{user}的用户记录"}, - {"strGroupAuthorized","A roll to the table turns to a dice fumble!\nDice Roller {strSelfName}√\n本群已授权许可,请尽情使用本骰娘√\n请遵守协议使用,服务结束后使用.dismiss送出!" }, - {"strGroupLicenseDeny","本群未获{self}许可使用,自动在群内静默。\n请先.help协议 阅读并同意协议后向运营方申请许可使用,\n否则请管理员使用!dismiss送出{self}\n可按以下格式填写并发送申请:\n!authorize 申请用途:[*请写入理由*] 我已了解Dice!基本用法,仔细阅读并保证遵守{strSelfName}的用户协议,如需停用指令使用[*请写入指令*],用后使用[*请写入指令*]送出群" }, - {"strGroupLicenseApply","此群未通过自助授权×\n许可申请已发送√" }, - {"strGroupSetOn","现已开启{self}在此群的“{option}”选项√"}, //群内开关和遥控开关通用此文本 - {"strGroupSetOnAlready","{self}已在此群设置了{option}!"}, - {"strGroupSetOff","现已关闭{self}在此群的“{option}”选项√"}, - {"strGroupSetOffAlready","{self}未在此群设置{option}!"}, - {"strGroupSetAll","{self}已修改记录中{cnt}个群的“{option}”选项√"}, - {"strGroupDenied","{nick}在{self}处无权访问此群的设置×"}, - {"strGroupSetDenied","{nick}在{self}处设置{option}的权限不足×"}, - {"strGroupSetNotExist","{self}无{option}此选项×"}, - {"strGroupWholeUnban","{self}已关闭全局禁言√"}, - {"strGroupWholeBan","{self}已开启全局禁言√"}, - {"strGroupWholeBanErr","{self}开启全局禁言失败×"}, - {"strGroupUnban","{self}裁定:{member}解除禁言√"}, - {"strGroupBan","{self}裁定:{member}禁言{res}分钟√"}, - {"strGroupBanErr","{self}禁言{member}失败×"}, - {"strGroupNotFound","{self}无该群记录×"}, - {"strGroupNotIn","{self}当前不在该群或对象不是群!"}, - {"strGroupExit","{self}已退出该群√"}, - {"strGroupCardSet","{self}已将{target}的群名片修改为{card}√"}, - {"strGroupCardSetErr","{self}设置{target}的群名片失败×"}, - {"strGroupTitleSet","{self}已将{target}的头衔修改为{title}√"}, - {"strGroupTitleSetErr","{self}设置{target}的头衔失败×"}, - {"strPcNewEmptyCard","已为{nick}新建{type}空白卡{char}√"}, - {"strPcNewCardShow","已为{nick}新建{type}卡{char}:{show}"},//由于预生成选项而存在属性 - {"strPcCardSet","已将{nick}当前角色卡绑定为{char}√"},//{nick}-用户昵称 {pc}-原角色卡名 {char}-新角色卡名 - {"strPcCardReset","已解绑{nick}当前的默认卡√"},//{nick}-用户昵称 {pc}-原角色卡名 - {"strPcCardRename","已将{old_name}重命名为{new_name}√"}, - {"strPcCardDel","已将角色卡{char}删除√"}, - {"strPcCardCpy","已将{char2}的属性复制到{char1}√"}, - {"strPcClr","已清空{nick}的角色卡记录√"}, - {"strPcCardList","{nick}的角色列表:{show}"}, - {"strPcCardBuild","{nick}的{char}生成:{show}"}, - {"strPcCardShow","{nick}的<{type}>{char}:{show}"}, //{nick}-用户昵称 {type}-角色卡类型 {char}-角色卡名 - {"strPcCardRedo","{nick}的{char}重新生成:{show}"}, - {"strPcGroupList","{nick}的各群角色列表:{show}"}, - {"strPcNotExistErr","{self}无{nick}的角色卡记录,无法删除×"}, - {"strPcCardFull","{nick}在{self}处的角色卡已达上限,请先清理多余角色卡×"}, - {"strPcTempInvalid","{self}无法识别的角色卡模板×"}, - {"strPcNameEmpty","名称不能为空×"}, - {"strPcNameExist","{nick}已存在同名卡×"}, - {"strPcNameNotExist","{nick}无该名称角色卡卡×"}, - {"strPcNameInvalid","非法的角色卡名(存在冒号)×"}, - {"strPcInitDelErr","{nick}的初始卡不可删除×"}, - {"strPcNoteTooLong","备注长度不能超过255×"}, - {"strPcTextTooLong","文本长度不能超过48×"}, - {"strSensNote","发现指令中带敏感词,{self}已记录并上报!"}, - {"strSensWarn","发现指令中带敏感词,{self}拒绝响应且已上报!"}, - {"strSpamFirstWarning","你短时间内对{self}指令次数过多!请善用多轮掷骰和复数生成指令(刷屏初次警告)"}, - {"strSpamFinalWarning","请暂停你的一切指令,避免因高频指令被{self}拉黑!(刷屏最终警告)"}, - {"strReplySet","{self}对关键词{key}的回复已设置√"}, - {"strReplyDel","{self}对关键词{key}的回复已清除√"}, - {"strStModify","{self}对已记录{pc}的属性变化:"}, //存在技能值变化情况时,优先使用此文本 - {"strStDetail","{self}对已设置{pc}的属性:"}, //存在掷骰时,使用此文本(暂时无用) - {"strStValEmpty","{self}未记录{attr}原值×"}, - {"strBlackQQAddNotice","{user_nick},你已被{self}加入黑名单,详情请联系Master:{master_QQ}"}, - {"strBlackQQAddNoticeReason","{user_nick},由于{reason},你已被{self}加入黑名单,申诉解封请联系管理员。Master:{master_QQ}"}, - {"strBlackQQDelNotice","{user_nick},你已被{self}移出黑名单,现在可以继续使用了"}, - {"strWhiteQQAddNotice","{user_nick},您已获得{self}的信任,请尽情使用{self}√"}, - {"strWhiteQQDenied","你不是{self}信任的用户×"}, - {"strWhiteGroupDenied","本群聊不在白名单中×"}, - {"strDeckProNew","已新建自定义牌堆√"}, - {"strDeckProSet","已将{deck_name}设置为默认牌堆√"}, - {"strDeckProClr","已删除默认牌堆√"}, - {"strDeckProNull","默认牌堆不存在!"}, - {"strDeckTmpReset","已重置卡牌√"}, - {"strDeckTmpShow","当前剩余卡牌:"}, - {"strDeckTmpEmpty","已无剩余卡牌!"}, //剩余卡牌数为0 - {"strDeckTmpNotFound","不存在剩余卡牌×"}, //没有生成过牌堆 - {"strDeckNameEmpty","未指定牌堆名×"}, - {"strRollDice","{pc}掷骰: {res}"}, - {"strRollDiceReason","{pc}掷骰 {reason}: {res}"}, - {"strRollHidden","{pc}进行了一次暗骰"}, - {"strRollTurn","{pc}的掷骰轮数: {turn}轮"}, - {"strRollMultiDice","{pc}掷骰{turn}次: {dice_exp}={res}"}, - {"strRollMultiDiceReason","{pc}掷骰{turn}次{reason}: {dice_exp}={res}"}, - {"strRollSkill","{pc}进行{attr}检定:"}, - {"strRollSkillReason","由于{reason} {pc}进行{attr}检定:"}, - {"strEnRoll","{pc}的{attr}增强或成长检定:\n{res}"},//{attr}在用户省略技能名后替换为{strEnDefaultName} - {"strEnRollNotChange","{strEnRoll}\n{pc}的{attr}值没有变化"}, - {"strEnRollFailure","{strEnRoll}\n{pc}的{attr}变化{change}点,当前为{final}点"}, - {"strEnRollSuccess","{strEnRoll}\n{pc}的{attr}增加{change}点,当前为{final}点"}, - {"strEnDefaultName","属性或技能"},//默认文本 - {"strEnValEmpty", "未对{self}设定待成长属性值,请先.st {attr} 属性值 或查看.help en×"}, - {"strEnValInvalid", "{attr}值输入不正确,请输入1-99范围内的整数!"}, - {"strSendMsg","{self}已将消息送出√"},//Master定向发送的回执 - {"strSendMasterMsg","消息{self}已发送给Master√"},//向Master发送的回执 - {"strSendMsgEmpty","发送消息内容为空×"}, - {"strSendMsgInvalid","{self}没有可以发送的对象×"},//没有Master - {"strDefaultCOCClr","默认检定房规已清除√"}, - {"strDefaultCOCNotFound","默认检定房规不存在×"}, - {"strDefaultCOCSet","默认检定房规已设置:"}, - {"strLinkLoss","{self}的时空连接已断开√"}, - {"strLinked","{self}已创建时空门√"}, - {"strLinkWarning","尝试创建时空门,但不保证能否连通"}, - {"strLinkNotFound","时空门要通向不可名状的地方了×"}, - {"strNotMaster","你不是{self}的master!你想做什么?"}, - {"strNotAdmin","你不是{self}的管理员×"}, - { "strAdminDismiss", "{strDismiss}" }, //管理员指令退群的回执 - {"strDismiss", ""}, //.dismiss退群前的回执 - {"strHlpSet","已为{key}设置词条√"}, - {"strHlpReset","已清除{key}的词条√"}, - {"strHlpNameEmpty","Master想要自定义什么词条呀?"}, - {"strHlpNotFound","{self}未找到指定的帮助信息×"}, - {"strClockToWork","{self}已按时启用√"}, - {"strClockOffWork","{self}已按时关闭√"}, - {"strNameGenerator","{pc}的随机名称:{res}"}, - {"strDrawCard", "来看看{pc}抽到了什么:{res}"}, - {"strMeOn", "成功在这里启用{self}的.me命令√"}, - {"strMeOff", "成功在这里禁用{self}的.me命令√"}, - {"strMeOnAlready", "在这里{self}的.me命令没有被禁用!"}, - {"strMeOffAlready", "在这里{self}的.me命令已经被禁用!"}, - {"strObOn", "成功在这里启用{self}的旁观模式√"}, - {"strObOff", "成功在这里禁用{self}的旁观模式√"}, - {"strObOnAlready", "在这里{self}的旁观模式没有被禁用!"}, - {"strObOffAlready", "在这里{self}的旁观模式已经被禁用!"}, - {"strObList", "当前{self}的旁观者有:"}, - {"strObListEmpty", "当前{self}暂无旁观者"}, - {"strObListClr", "{self}成功删除所有旁观者√"}, - {"strObEnter", "{nick}成功加入{self}的旁观√"}, - {"strObExit", "{nick}成功退出{self}的旁观√"}, - {"strObEnterAlready", "{nick}已经处于{self}的旁观模式!"}, - {"strObExitAlready", "{nick}没有加入{self}的旁观模式!"}, - {"strQQIDEmpty", "QQ号不能为空×"}, - {"strGroupIDEmpty", "群号不能为空×"}, - {"strBlackGroup", "该群在黑名单中,如有疑问请联系master"}, - {"strBotOn", "成功开启{self}√"}, - {"strBotOff", "成功关闭{self}√"}, - {"strBotOnAlready", "{self}已经处于开启状态!"}, - {"strBotOffAlready", "{self}已经处于关闭状态!"}, - {"strRollCriticalSuccess", "大成功!"}, //一般检定用 - {"strRollExtremeSuccess", "极难成功"}, - {"strRollHardSuccess", "困难成功"}, - {"strRollRegularSuccess", "成功"}, - {"strRollFailure", "失败"}, - {"strRollFumble", "大失败!"}, - {"strFumble", "大失败!"}, //多轮检定用,请控制长度 - {"strFailure", "失败"}, - {"strSuccess", "成功"}, - {"strHardSuccess", "困难成功"}, - {"strExtremeSuccess", "极难成功"}, - {"strCriticalSuccess", "大成功!"}, - {"strNumCannotBeZero", "无意义的数目!莫要消遣于我!"}, - {"strDeckNotFound", "是说{deck_name}?{self}没听说过的牌堆名呢……"}, - {"strDeckEmpty", "{self}已经一张也不剩了!"}, - {"strNameNumTooBig", "生成数量过多!请输入1-10之间的数字!"}, - {"strNameNumCannotBeZero", "生成数量不能为零!请输入1-10之间的数字!"}, - {"strSetInvalid", "无效的默认骰!请输入1-9999之间的数字!"}, - {"strSetTooBig", "这面数……让我丢个球啊!请输入1-9999之间的数字!"}, - {"strSetCannotBeZero", "默认骰不能为零!请输入1-9999之间的数字!"}, - {"strCharacterCannotBeZero", "人物作成次数不能为零!请输入1-10之间的数字!"}, - {"strCharacterTooBig", "人物作成次数过多!请输入1-10之间的数字!"}, - {"strCharacterInvalid", "人物作成次数无效!请输入1-10之间的数字!"}, - {"strSanRoll", "{pc}的San Check:\n{res}"}, - {"strSanRollRes", "{strSanRoll}\n{pc}的San值减少{change}点,当前剩余{final}点"}, - {"strSanCostInvalid", "SC表达式输入不正确,格式为成功扣San/失败扣San,如1/1d6!"}, - {"strSanInvalid", "San值输入不正确,请输入1-99范围内的整数!"}, - {"strSanEmpty", "未设定San值,请先.st san 或查看.help sc×"}, - {"strSuccessRateErr", "这成功率还需要检定吗?"}, - {"strGroupIDInvalid", "无效的群号!"}, - {"strSendErr", "消息发送失败!"}, - {"strSendSuccess", "命令执行成功√"}, - {"strDisabledErr", "命令无法执行:机器人已在此群中被关闭!"}, - {"strActionEmpty", "动作不能为空×"}, - {"strMEDisabledErr", "管理员已在此群中禁用.me命令!"}, - {"strDisabledMeGlobal", "恕不提供.me服务×"}, - {"strDisabledJrrpGlobal", "恕不提供.jrrp服务×"}, - {"strDisabledDeckGlobal", "恕不提供.deck服务×"}, - {"strDisabledDrawGlobal", "恕不提供.draw服务×"}, - {"strDisabledSendGlobal", "恕不提供.send服务×"}, - {"strHELPDisabledErr", "管理员已在此群中禁用.help命令!"}, - {"strNameDelEmpty", "{nick}没有设置名称,无法删除!"}, - {"strValueErr", "掷骰表达式输入错误!"}, - {"strInputErr", "命令或掷骰表达式输入错误!"}, - {"strUnknownErr", "发生了未知错误!"}, - {"strUnableToGetErrorMsg", "无法获取错误信息!"}, - {"strDiceTooBigErr", "{self}被你扔出的骰子淹没了×"}, - {"strRequestRetCodeErr", "访问服务器时出现错误! HTTP状态码: {error}"}, - {"strRequestNoResponse", "服务器未返回任何信息×"}, - {"strTypeTooBigErr", "哇!让我数数骰子有多少面先~1...2..."}, - {"strZeroTypeErr", "这是...!!时空裂({self}被骰子产生的时空裂缝卷走了)"}, - {"strAddDiceValErr", "你这样要让{self}扔骰子扔到什么时候嘛~(请输入正确的加骰参数:5-10之内的整数)"}, - {"strZeroDiceErr", "咦?我的骰子呢?"}, - {"strRollTimeExceeded", "掷骰轮数超过了最大轮数限制!"}, - {"strRollTimeErr", "异常的掷骰轮数"}, - {"strObPrivate", "你想看什么呀?"}, - {"strDismissPrivate", "滚!"}, - {"strWelcomePrivate", "你在这欢迎谁呢?"}, - {"strWelcomeMsgClearNotice", "已清除本群的入群欢迎词√"}, - {"strWelcomeMsgClearErr", "没有设置入群欢迎词,清除失败×"}, - {"strWelcomeMsgUpdateNotice", "{self}已更新本群的入群欢迎词√"}, - {"strPermissionDeniedErr", "请让群内管理对{self}发送该指令×"}, - {"strSelfPermissionErr", "{self}权限不够无能为力呢×"}, - {"strNameTooLongErr", "名称过长×(最多为50英文字符)"}, - {"strNameClr", "已将{nick}的名称删除√"}, - {"strNameSet", "已将{nick}的名称更改为{new_nick}√"}, - {"strUnknownPropErr", "未设定{attr}成功率,请先.st {attr} 技能值 或查看.help rc×"}, - {"strEmptyWWDiceErr", "格式错误:正确格式为.w(w)XaY!其中X≥1, 5≤Y≤10"}, - {"strPropErr", "请认真的输入你的属性哦~"}, - {"strSetPropSuccess", "属性设置成功√"}, - {"strPropCleared", "已清空{char}的所有属性√"}, - {"strRuleReset", "已重置默认规则√"}, - {"strRuleSet", "已设置默认规则√"}, - {"strRuleErr", "规则数据获取失败,具体信息:\n"}, - {"strRulesFailedErr", "请求失败,{self}无法连接数据库×"}, - {"strPropDeleted", "已删除{pc}的{attr}√"}, - {"strPropNotFound", "属性{attr}不存在×"}, - {"strRuleNotFound", "{self}未找到对应的规则信息×"}, - {"strProp", "{pc}的{attr}为{val}"}, - {"strPropList", "{nick}的{char}属性列表为:{show}"}, - {"strStErr", "格式错误:请参考.help st获取.st命令的使用方法"}, - {"strRulesFormatErr", "格式错误:正确格式为.rules[规则名称:]规则条目 如.rules COC7:力量"}, - {"strLeaveDiscuss", "{self}现不支持讨论组服务,即将退出"}, - {"strLeaveNoPower", "{self}未获得群管理,即将退群"}, - {"strLeaveUnused", "{self}已经在这里被放置{day}天啦,马上就会离开这里了"}, - {"strGlobalOff", "{self}休假中,暂停服务×"}, - {"strPreserve", "{self}私有私用,勿扰勿怪\n如需申请许可请发送!authorize +[群号] [申请理由]"}, - {"strJrrp", "{nick}今天的人品值是: {res}"}, - {"strJrrpErr", "JRRP获取失败! 错误信息: \n{res}"}, - {"strAddFriendWhiteQQ", "{strAddFriend}"}, //白名单用户添加好友时回复此句 + {"strParaEmpty","Ϊա"}, //͵ܻظ + {"strParaIllegal","Ƿ"}, //͵ܻظ + {"stranger","û"}, //{nick}޷ȡǿdzʱijƺ + {"strAdminOptionEmpty","{self}ʲôô{nick}"}, // + {"strGMTableShow","{self}¼{table_name}б"}, + {"strGMTableNotExist","{self}ûб{table_name}¼"}, + {"strUserTrustShow","{user}{self}μΪ{trust}"}, + {"strUserTrusted","ѽ{self}{user}μΪ{trust}"}, + {"strUserTrustDenied","{nick}{self}ȨʶԷȨޡ"}, + {"strUserTrustIllegal","ĿȨ޸Ϊ{trust}ǷǷġ"}, + {"strUserNotFound","{self}{user}û¼"}, + {"strGroupAuthorized","A roll to the table turns to a dice fumble!\nDice Roller {strSelfName}\nȺȨɣ뾡ʹñ\nЭʹãʹ.dismissͳ!" }, + {"strGroupLicenseDeny","Ⱥδ{self}ʹãԶȺھĬ\n.helpЭ ĶͬЭӪʹã\nԱʹ!dismissͳ{self}\nɰ¸ʽд:\n!authorize ;:[*д*] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[*дָ*]úʹ[*дָ*]ͳȺ" }, + {"strGroupLicenseApply","ȺδͨȨ\nѷ͡" }, + {"strGroupSetOn","ѿ{self}ڴȺġ{option}ѡ"}, //Ⱥڿغңؿͨôı + {"strGroupSetOnAlready","{self}ڴȺ{option}"}, + {"strGroupSetOff","ѹر{self}ڴȺġ{option}ѡ"}, + {"strGroupSetOffAlready","{self}δڴȺ{option}"}, + {"strGroupSetAll","{self}޸ļ¼{cnt}Ⱥġ{option}ѡ"}, + {"strGroupDenied","{nick}{self}ȨʴȺá"}, + {"strGroupSetDenied","{nick}{self}{option}Ȩ޲"}, + {"strGroupSetNotExist","{self}{option}ѡ"}, + {"strGroupWholeUnban","{self}ѹرȫֽԡ"}, + {"strGroupWholeBan","{self}ѿȫֽԡ"}, + {"strGroupWholeBanErr","{self}ȫֽʧܡ"}, + {"strGroupUnban","{self}ö:{member}ԡ"}, + {"strGroupBan","{self}ö:{member}{res}ӡ"}, + {"strGroupBanErr","{self}{member}ʧܡ"}, + {"strGroupNotFound","{self}޸Ⱥ¼"}, + {"strGroupNotIn","{self}ǰڸȺȺ"}, + {"strGroupExit","{self}˳Ⱥ"}, + {"strGroupCardSet","{self}ѽ{target}ȺƬ޸Ϊ{card}"}, + {"strGroupCardSetErr","{self}{target}ȺƬʧܡ"}, + {"strGroupTitleSet","{self}ѽ{target}ͷ޸Ϊ{title}"}, + {"strGroupTitleSetErr","{self}{target}ͷʧܡ"}, + {"strPcNewEmptyCard","Ϊ{nick}½{type}հ׿{char}"}, + {"strPcNewCardShow","Ϊ{nick}½{type}{char}{show}"},//Ԥѡ + {"strPcCardSet","ѽ{nick}ǰɫΪ{char}"},//{nick}-ûdz {pc}-ԭɫ {char}-½ɫ + {"strPcCardReset","ѽ{nick}ǰĬϿ"},//{nick}-ûdz {pc}-ԭɫ + {"strPcCardRename","ѽ{old_name}Ϊ{new_name}"}, + {"strPcCardDel","ѽɫ{char}ɾ"}, + {"strPcCardCpy","ѽ{char2}ԸƵ{char1}"}, + {"strPcClr","{nick}Ľɫ¼"}, + {"strPcCardList","{nick}Ľɫб{show}"}, + {"strPcCardBuild","{nick}{char}ɣ{show}"}, + {"strPcCardShow","{nick}<{type}>{char}{show}"}, //{nick}-ûdz {type}-ɫ {char}-ɫ + {"strPcCardRedo","{nick}{char}ɣ{show}"}, + {"strPcGroupList","{nick}ĸȺɫб{show}"}, + {"strPcNotExistErr","{self}{nick}Ľɫ¼޷ɾ"}, + {"strPcCardFull","{nick}{self}ĽɫѴޣɫ"}, + {"strPcTempInvalid","{self}޷ʶĽɫģ"}, + {"strPcNameEmpty","ƲΪա"}, + {"strPcNameExist","{nick}Ѵͬ"}, + {"strPcNameNotExist","{nick}޸ƽɫ"}, + {"strPcNameInvalid","ǷĽɫðţ"}, + {"strPcInitDelErr","{nick}ijʼɾ"}, + {"strPcNoteTooLong","עȲܳ255"}, + {"strPcTextTooLong","ıȲܳ48"}, + {"strSensNote","ָддʣ{self}Ѽ¼ϱ"}, + {"strSensWarn","ָддʣ{self}ܾӦϱ"}, + {"strSpamFirstWarning","ʱڶ{self}ָ࣡ö͸ָˢξ棩"}, + {"strSpamFinalWarning","ͣһָƵָ{self}ڣˢվ棩"}, + {"strReplySet","{self}Թؼ{key}Ļظá"}, + {"strReplyDel","{self}Թؼ{key}Ļظ"}, + {"strStModify","{self}Ѽ¼{pc}Ա仯:"}, //ڼֵ仯ʱʹôı + {"strStDetail","{self}{pc}ԣ"}, //ʱʹôı(ʱ) + {"strStValEmpty","{self}δ¼{attr}ԭֵ"}, + {"strBlackQQAddNotice","{user_nick}ѱ{self}ϵMaster:{master_QQ}"}, + {"strBlackQQAddNoticeReason","{user_nick}{reason}ѱ{self}߽ϵԱMaster:{master_QQ}"}, + {"strBlackQQDelNotice","{user_nick}ѱ{self}ƳڿԼʹ"}, + {"strWhiteQQAddNotice","{user_nick}ѻ{self}Σ뾡ʹ{self}"}, + {"strWhiteQQDenied","㲻{self}εû"}, + {"strWhiteGroupDenied","ȺIJڰС"}, + {"strDeckProNew","½Զƶѡ"}, + {"strDeckProSet","ѽ{deck_name}ΪĬƶѡ"}, + {"strDeckProClr","ɾĬƶѡ"}, + {"strDeckProNull","ĬƶѲ!"}, + {"strDeckTmpReset","ÿơ"}, + {"strDeckTmpShow","ǰʣ࿨:"}, + {"strDeckTmpEmpty","ʣ࿨ƣ"}, //ʣ࿨Ϊ0 + {"strDeckTmpNotFound","ʣ࿨ơ"}, //ûɹƶ + {"strDeckNameEmpty","δָƶ"}, + {"strRollDice","{pc}: {res}"}, + {"strRollDiceReason","{pc} {reason}: {res}"}, + {"strRollHidden","{pc}һΰ"}, + {"strRollTurn","{pc}: {turn}"}, + {"strRollMultiDice","{pc}{turn}: {dice_exp}={res}"}, + {"strRollMultiDiceReason","{pc}{turn}{reason}: {dice_exp}={res}"}, + {"strRollSkill","{pc}{attr}춨"}, + {"strRollSkillReason","{reason} {pc}{attr}춨"}, + {"strEnRoll","{pc}{attr}ǿɳ춨\n{res}"},//{attr}ûʡԼ滻Ϊ{strEnDefaultName} + {"strEnRollNotChange","{strEnRoll}\n{pc}{attr}ֵûб仯"}, + {"strEnRollFailure","{strEnRoll}\n{pc}{attr}仯{change}㣬ǰΪ{final}"}, + {"strEnRollSuccess","{strEnRoll}\n{pc}{attr}{change}㣬ǰΪ{final}"}, + {"strEnDefaultName","Ի"},//Ĭı + {"strEnValEmpty", "δ{self}趨ɳֵ.st {attr} ֵ 鿴.help en"}, + {"strEnValInvalid", "{attr}ֵ벻ȷ,1-99Χڵ!"}, + {"strSendMsg","{self}ѽϢͳ"},//Master͵Ļִ + {"strSendMasterMsg","Ϣ{self}ѷ͸Master"},//Master͵Ļִ + {"strSendMsgEmpty","ϢΪա"}, + {"strSendMsgInvalid","{self}ûпԷ͵Ķ"},//ûMaster + {"strDefaultCOCClr","Ĭϼ춨"}, + {"strDefaultCOCNotFound","Ĭϼ춨治ڡ"}, + {"strDefaultCOCSet","Ĭϼ춨:"}, + {"strLinkLoss","{self}ʱѶϿ"}, + {"strLinked","{self}Ѵʱš"}, + {"strLinkWarning","Դʱţ֤ܷͨ"}, + {"strLinkNotFound","ʱҪͨ򲻿״ĵطˡ"}, + {"strNotMaster","㲻{self}masterʲô"}, + {"strNotAdmin","㲻{self}ĹԱ"}, + { "strAdminDismiss", "{strDismiss}" }, //ԱָȺĻִ + {"strDismiss", ""}, //.dismissȺǰĻִ + {"strHlpSet","Ϊ{key}ô"}, + {"strHlpReset","{key}Ĵ"}, + {"strHlpNameEmpty","MasterҪԶʲôѽ"}, + {"strHlpNotFound","{self}δҵָİϢ"}, + {"strClockToWork","{self}Ѱʱá"}, + {"strClockOffWork","{self}Ѱʱرա"}, + {"strNameGenerator","{pc}ƣ{res}"}, + {"strDrawCard", "{pc}鵽ʲô{res}"}, + {"strMeOn", "ɹ{self}.me"}, + {"strMeOff", "ɹ{self}.me"}, + {"strMeOnAlready", "{self}.meûб!"}, + {"strMeOffAlready", "{self}.meѾ!"}, + {"strObOn", "ɹ{self}Թģʽ"}, + {"strObOff", "ɹ{self}Թģʽ"}, + {"strObOnAlready", "{self}Թģʽûб!"}, + {"strObOffAlready", "{self}ԹģʽѾ!"}, + {"strObList", "ǰ{self}Թ:"}, + {"strObListEmpty", "ǰ{self}Թ"}, + {"strObListClr", "{self}ɹɾԹߡ"}, + {"strObEnter", "{nick}ɹ{self}Թۡ"}, + {"strObExit", "{nick}ɹ˳{self}Թۡ"}, + {"strObEnterAlready", "{nick}Ѿ{self}Թģʽ!"}, + {"strObExitAlready", "{nick}ûм{self}Թģʽ!"}, + {"strQQIDEmpty", "QQŲΪա"}, + {"strGroupIDEmpty", "ȺŲΪա"}, + {"strBlackGroup", "ȺںУϵmaster"}, + {"strBotOn", "ɹ{self}"}, + {"strBotOff", "ɹر{self}"}, + {"strBotOnAlready", "{self}Ѿڿ״̬!"}, + {"strBotOffAlready", "{self}Ѿڹر״̬!"}, + {"strRollCriticalSuccess", "ɹ"}, //һ춨 + {"strRollExtremeSuccess", "ѳɹ"}, + {"strRollHardSuccess", "ѳɹ"}, + {"strRollRegularSuccess", "ɹ"}, + {"strRollFailure", "ʧ"}, + {"strRollFumble", "ʧܣ"}, + {"strFumble", "ʧ!"}, //ּ춨ãƳ + {"strFailure", "ʧ"}, + {"strSuccess", "ɹ"}, + {"strHardSuccess", "ѳɹ"}, + {"strExtremeSuccess", "ѳɹ"}, + {"strCriticalSuccess", "ɹ!"}, + {"strNumCannotBeZero", "ĿĪҪDz!"}, + {"strDeckNotFound", "˵{deck_name}{self}û˵ƶء"}, + {"strDeckEmpty", "{self}ѾһҲʣˣ"}, + {"strNameNumTooBig", "!1-10֮!"}, + {"strNameNumCannotBeZero", "Ϊ!1-10֮!"}, + {"strSetInvalid", "ЧĬ!1-9999֮!"}, + {"strSetTooBig", "Ҷ!1-9999֮!"}, + {"strSetCannotBeZero", "ĬΪ!1-9999֮!"}, + {"strCharacterCannotBeZero", "ɴΪ!1-10֮!"}, + {"strCharacterTooBig", "ɴ!1-10֮!"}, + {"strCharacterInvalid", "ɴЧ!1-10֮!"}, + {"strSanRoll", "{pc}San Check\n{res}"}, + {"strSanRollRes", "{strSanRoll}\n{pc}Sanֵ{change},ǰʣ{final}"}, + {"strSanCostInvalid", "SCʽ벻ȷ,ʽΪɹSan/ʧܿSan,1/1d6!"}, + {"strSanInvalid", "Sanֵ벻ȷ,1-99Χڵ!"}, + {"strSanEmpty", "δ趨Sanֵ.st san 鿴.help sc"}, + {"strSuccessRateErr", "ɹʻҪ춨"}, + {"strGroupIDInvalid", "ЧȺ!"}, + {"strSendErr", "Ϣʧ!"}, + {"strSendSuccess", "ִгɹ"}, + {"strDisabledErr", "޷ִ:ڴȺбر!"}, + {"strActionEmpty", "Ϊա"}, + {"strMEDisabledErr", "ԱڴȺн.me!"}, + {"strDisabledMeGlobal", "ˡṩ.me"}, + {"strDisabledJrrpGlobal", "ˡṩ.jrrp"}, + {"strDisabledDeckGlobal", "ˡṩ.deck"}, + {"strDisabledDrawGlobal", "ˡṩ.draw"}, + {"strDisabledSendGlobal", "ˡṩ.send"}, + {"strHELPDisabledErr", "ԱڴȺн.help!"}, + {"strNameDelEmpty", "{nick}û,޷ɾ!"}, + {"strValueErr", "ʽ!"}, + {"strInputErr", "ʽ!"}, + {"strUnknownErr", "δ֪!"}, + {"strUnableToGetErrorMsg", "޷ȡϢ!"}, + {"strDiceTooBigErr", "{self}ӳûˡ"}, + {"strRequestRetCodeErr", "ʷʱִ! HTTP״̬: {error}"}, + {"strRequestNoResponse", "δκϢ"}, + {"strTypeTooBigErr", "!ж~1...2..."}, + {"strZeroTypeErr", "...!!ʱ({self}Ӳʱѷ)"}, + {"strAddDiceValErr", "Ҫ{self}ӵʲôʱ~(ȷļ:5-10֮ڵ)"}, + {"strZeroDiceErr", "?ҵ?"}, + {"strRollTimeExceeded", "!"}, + {"strRollTimeErr", "쳣"}, + {"strObPrivate", "뿴ʲôѽ"}, + {"strDismissPrivate", ""}, + {"strWelcomePrivate", "⻶ӭ˭أ"}, + {"strWelcomeMsgClearNotice", "ȺȺӭʡ"}, + {"strWelcomeMsgClearErr", "ûȺӭʣʧܡ"}, + {"strWelcomeMsgUpdateNotice", "{self}Ѹ±ȺȺӭʡ"}, + {"strPermissionDeniedErr", "Ⱥڹ{self}͸ָ"}, + {"strSelfPermissionErr", "{self}Ȩ޲Ϊء"}, + {"strNameTooLongErr", "ƹ(Ϊ50Ӣַ)"}, + {"strNameClr", "ѽ{nick}ɾ"}, + {"strNameSet", "ѽ{nick}ƸΪ{new_nick}"}, + {"strUnknownPropErr", "δ趨{attr}ɹʣ.st {attr} ֵ 鿴.help rc"}, + {"strEmptyWWDiceErr", "ʽ:ȷʽΪ.w(w)XaY!X1, 5Y10"}, + {"strPropErr", "Ŷ~"}, + {"strSetPropSuccess", "óɹ"}, + {"strPropCleared", "{char}ԡ"}, + {"strRuleReset", "ĬϹ"}, + {"strRuleSet", "ĬϹ"}, + {"strRuleErr", "ݻȡʧ,Ϣ:\n"}, + {"strRulesFailedErr", "ʧ,{self}޷ݿ"}, + {"strPropDeleted", "ɾ{pc}{attr}"}, + {"strPropNotFound", "{attr}ڡ"}, + {"strRuleNotFound", "{self}δҵӦĹϢ"}, + {"strProp", "{pc}{attr}Ϊ{val}"}, + {"strPropList", "{nick}{char}бΪ{show}"}, + {"strStErr", "ʽ:ο.help stȡ.stʹ÷"}, + {"strRulesFormatErr", "ʽ:ȷʽΪ.rules[:]Ŀ .rules COC7:"}, + {"strLeaveDiscuss", "{self}ֲ֧񣬼˳"}, + {"strLeaveNoPower", "{self}δȺȺ"}, + {"strLeaveUnused", "{self}Ѿﱻ{day}Ͼͻ뿪"}, + {"strGlobalOff", "{self}ݼУͣ"}, + {"strPreserve", "{self}˽˽ã\n뷢!authorize +[Ⱥ] []"}, + {"strJrrp", "{nick}Ʒֵ: {res}"}, + {"strJrrpErr", "JRRPȡʧ! Ϣ: \n{res}"}, + {"strAddFriendWhiteQQ", "{strAddFriend}"}, //ûӺʱظ˾ { "strAddFriend", - R"(欢迎使用{strSelfName}! -.help协议 确认服务协议 -.help指令 查看指令列表 -.help设定 确认骰娘设定 -.help链接 查看源码文档 -使用服务默认已经同意服务协议)" - }, //同意添加好友时额外发送的语句 + R"(ӭʹ{strSelfName} +.helpЭ ȷϷЭ +.helpָ 鿴ָб +.help趨 ȷ趨 +.help 鿴Դĵ +ʹ÷ĬѾͬЭ)" + }, //ͬӺʱⷢ͵ { "strAddGroup", - R"(欢迎使用{strSelfName}! -请使用.dismiss QQ号(或后四位) 使{self}退群退讨论组 -.bot on/off QQ号(或后四位) //开启或关闭指令 -.group +/-禁用回复 //禁用或启用回复 -.help协议 确认服务协议 -.help指令 查看指令列表 -.help设定 确认骰娘设定 -.help链接 查看源码文档 -邀请入群默认视为同意服务协议,知晓禁言或移出的后果)" + R"(ӭʹ{strSelfName} +ʹ.dismiss QQţλ ʹ{self}Ⱥ +.bot on/off QQţλ //رָ +.group +/-ûظ //ûûظ +.helpЭ ȷϷЭ +.helpָ 鿴ָб +.help趨 ȷ趨 +.help 鿴Դĵ +ȺĬΪͬЭ飬֪ԻƳĺ)" }, {"strSelfName", ""}, {"strSelfCall", "&strSelfName"}, {"self", "&strSelfCall"}, - {"strBotMsg", "\n使用.help更新 查看{self}更新内容"}, + {"strBotMsg", "\nʹ.help 鿴{self}"}, { "strHlpMsg", - R"(请使用.dismiss QQ号(或后四位) 使{self}退群退讨论组 -.bot on/off QQ号(或后四位) //开启或关闭指令 -.help协议 确认服务协议 -.help指令 查看指令列表 -.help群管 查看群管指令 -.help设定 确认骰娘设定 -.help链接 查看源码文档 -官方论坛:https://forum.kokona.tech/ -官方(水)群: 624807593 941980833 882747577 -Shiki交流群: 1029435374 -私骰分流群: 863062599)" + R"(ʹ.dismiss QQţλ ʹ{self}Ⱥ +.bot on/off QQţλ //رָ +.helpЭ ȷϷЭ +.helpָ 鿴ָб +.helpȺ 鿴Ⱥָ +.help趨 ȷ趨 +.help 鿴Դĵ +ٷ̳: https://forum.kokona.tech/ +̳: https://kokona.tech)" } }; std::map EditedMsg; const std::map HelpDoc = { -{"更新",R"( -564:多功能优化,牌数牌堆等 -560:支持Mirai加载, 基础GUI -558:新增每日统计 -557:定时作业系统 -556:黑名单系统重做 -555:用户记录/群管系统 -554:新增多角色卡功能 -553:name功能调整 -552:后台系统大修 -551:文件夹批量读取牌堆 -550:允许多轮检定 -549:新增刷屏监测 -547:更新指令开关 -537:更新.send功能)"}, -{"协议","0.本协议是Shiki(The Star、Death、Judgement、The World)的服务协议。如果你看到了这句话,意味着Master应用默认协议,请注意。\n1.邀请骰娘、使用掷骰服务和在群内阅读此协议视为同意并承诺遵守此协议,否则请使用.dismiss移出骰娘。\n2.不允许禁言、移出骰娘或刷屏掷骰等对骰娘的不友善行为,这些行为将会提高骰娘被制裁的风险。开关骰娘响应请使用.bot on/off。\n3.骰娘默认邀请行为已事先得到群内同意,因而会自动同意群邀请。因擅自邀请而使骰娘遭遇不友善行为时,邀请者因未履行预见义务而将承担连带责任。\n4.禁止将骰娘用于赌博及其他违法犯罪行为。\n5.对于设置敏感昵称等无法预见但有可能招致言论审查的行为,骰娘可能会出于自我保护而拒绝提供服务\n6.由于技术以及资金原因,我们无法保证机器人100%的时间稳定运行,可能不定时停机维护或遭遇冻结,但是相应情况会及时通过各种渠道进行通知,敬请谅解。临时停机的骰娘不会有任何响应,故而不会影响群内活动,此状态下仍然禁止不友善行为。\n7.对于违反协议的行为,骰娘将视情况终止对用户和所在群提供服务,并将不良记录共享给其他服务提供方。黑名单相关事宜可以与服务提供方协商,但最终裁定权在服务提供方。\n8.本协议内容随时有可能改动。请注意帮助信息、签名、空间、官方群等处的骰娘动态。\n9.骰娘提供掷骰服务是完全免费的,欢迎投食。\n10.本服务最终解释权归服务提供方所有。"}, -{"链接","Dice!论坛导航贴: https://kokona.tech \n Dice!论坛: https://forum.kokona.tech"}, -{"设定","Master:{master_QQ}\n.me使用:禁止\n.jrrp使用:允许\n邀请处理:黑名单制,非禁即入\n讨论组使用:允许\n移出反制:拉黑群和操作者\n禁言反制:默认拉黑群和群主\n刷屏反制:警告\n邀请人责任:有限连带\n窥屏可能:有\n其他插件:无\n骰娘个人群:(未设置)\n官方(水)群: 624807593 941980833 882747577\n私骰分流群:863062599\n开发交流群:1029435374"}, -{"作者","Copyright (C) 2018-2020 w4123溯洄\nCopyright (C) 2019-2020 String.Empty"}, -{"指令",R"(at骰娘后接指令可以指定骰娘单独响应,如at骰娘.bot off -多数指令需要后接参数,请.help对应指令 获取详细信息 -掷骰指令包括: -.dismiss 退群 -.authorize 授权许可 -.bot 开关 -.welcome 入群欢迎 -.rules 规则速查 -.r 掷骰 -.ob 旁观模式 -.set 设置默认骰 -.name 随机姓名 -.nn 设置昵称 -.coc COC人物作成 -.dnd DND人物作成 -.st 属性记录 -.pc 角色卡 -.rc 检定 -.setcoc 设置检定房规 -.sc 理智检定 -.en 成长检定 -.ri 先攻 -.init 先攻列表 -.ww 骰池 -.me 第三人称动作 -.jrrp 今日人品 -.send 向管理发送消息 -.group 群管 -.draw 抽牌 -为了避免未预料到的指令误判,请尽可能在参数之间使用空格)"}, -{"deck","该指令可以设置默认牌堆,使用.draw不指定牌堆名时将使用此牌堆。该牌堆不会放回直到抽完最后一张后洗牌。\n.deck set 公共牌堆名 设置默认牌堆\n.deck set 正整数1-100 设置指定长度的数列\n.deck show 查看剩余卡牌\n.deck reset 重置剩余卡牌\n.deck new 自定义牌堆(用空格或|分割)(白名单限定)\n.deck new 有弹|无弹|无弹|无弹|无弹|无弹\n除show外其他群内操作需要管理权限"}, -{"退群","&dismiss"}, -{"退群指令","&dismiss"}, -{"dismiss","该指令需要群管理员权限,使用后即退出群聊\n!dismiss [目标QQ(完整或末四位)]指名退群\n!dismiss无视内置黑名单和静默状态,只要插件开启总是有效"}, -{"授权许可","&authoize"}, -{"authorize","授权许可(非信任用户使用时转为向管理申请许可)\n!authorize (+[群号]) ([申请理由])\n群内原地发送可省略群号,无法自动授权时会连同理由发给管理"}, -{"开关","&bot"}, -{"bot",".bot on/off开启/静默骰子(限群管理)\n.bot无视静默状态,只要插件开启且不在黑名单总是有效"}, -{"规则速查","&rules"}, -{"规则","&rules"}, -{"rules","规则速查:.rules ([规则]):[待查词条] 或.rules set [规则]\n.rules 跳跃 //复数规则有相同词条时,择一返回\n.rules COC:大失败 //coc默认搜寻coc7的词条,dnd默认搜寻3r\n.rules dnd:语言\n.rules set dnd //设置后优先查询dnd同名词条,无参数则清除设置"}, -{"掷骰","&r"}, +{"",R"( +564:๦Żƶѵ +560:֧Mirai, GUI +558:ÿͳ +557:ʱҵϵͳ +556:ϵͳ +555:û¼/Ⱥϵͳ +554:ɫ +553:nameܵ +552:̨ϵͳ +551:ļȡƶ +550:ּ춨 +549:ˢ +547:ָ +537:.send)"}, +{"Э","0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, +{"","Dice!̳: https://kokona.tech \n Dice!̳: https://forum.kokona.tech"}, +{"趨","Master{master_QQ}\n.meʹãֹ\n.jrrpʹã\n봦ƣǽ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n\nȺ:δã\nٷ(ˮ)Ⱥ: 624807593 941980833 882747577\n˽Ⱥ863062599\nȺ1029435374"}, +{"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, +{"ָ",R"(atָָﵥӦat.bot off +ָҪӲ.helpӦָ ȡϸϢ +ָ: +.dismiss Ⱥ +.authorize Ȩ +.bot +.welcome Ⱥӭ +.rules ٲ +.r +.ob Թģʽ +.set Ĭ +.name +.nn dz +.coc COC +.dnd DND +.st Լ¼ +.pc ɫ +.rc 춨 +.setcoc ü춨 +.sc Ǽ춨 +.en ɳ춨 +.ri ȹ +.init ȹб +.ww +.me ˳ƶ +.jrrp Ʒ +.send Ϣ +.group Ⱥ +.draw +Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)"}, +{"deck","ָĬƶѣʹ.drawָƶʱʹôƶѡƶѲŻֱһźϴơ\n.deck set ƶ Ĭƶ\n.deck set 1-100 ָȵ\n.deck show 鿴ʣ࿨\n.deck reset ʣ࿨\n.deck new Զƶѣÿո|ָ޶\n.deck new е|޵|޵|޵|޵|޵\nshowȺڲҪȨ"}, +{"Ⱥ","&dismiss"}, +{"Ⱥָ","&dismiss"}, +{"dismiss","ָҪȺԱȨޣʹú˳Ⱥ\n!dismiss [ĿQQ(ĩλ)]ָȺ\n!dismissú;Ĭ״ֻ̬ҪЧ"}, +{"Ȩ","&authoize"}, +{"authorize","Ȩ(ûʹʱתΪ)\n!authorize (+[Ⱥ]) ([])\nȺԭطͿʡȺţ޷ԶȨʱͬɷ"}, +{"","&bot"}, +{"bot",".bot on/off/ĬӣȺ\n.botӾĬ״ֻ̬ҪҲںЧ"}, +{"ٲ","&rules"}, +{"","&rules"}, +{"rules","ٲ飺.rules ([]):[] .rules set []\n.rules Ծ //ͬʱһ\n.rules COCʧ //cocĬѰcoc7Ĵ,dndĬѰ3r\n.rules dnd\n.rules set dnd //úȲѯdndͬ޲"}, +{"","&r"}, {"rd","&r"}, -{"r","掷骰:.r [掷骰表达式] ([掷骰原因]) [掷骰表达式]:([掷骰轮数]#)[骰子个数]d骰子面数(p[惩罚骰个数])(k[取点数最大的骰子数])不带参数时视为掷一个默认骰\n合法参数要求掷骰轮数1-10,奖惩骰个数1-9,个数范围1-100,面数范围1-1000\n.r3#d\t//3轮掷骰\n.rh心理学 暗骰\n.rs1D10+1D6+3 沙鹰伤害\t//省略单个骰子的点数,直接给结果\n现版本开头的r均可用o或d代替,但群聊中.ob会被识别为旁观指令"}, -{"暗骰","群聊限定,掷骰指令后接h视为暗骰,结果将私发本人和群内ob的用户\n为了保证发送成功,请加骰娘好友"}, -{"旁观","&ob"}, -{"旁观模式","&ob"}, -{"ob","旁观模式:.ob (exit/list/clr/on/off)\n.ob\t//加入旁观可以看到他人暗骰结果\n.ob exit\t//退出旁观模式\n.ob list\t//查看群内旁观者\n.ob clr\t//清除所有旁观者\n.ob on\t//全群允许旁观模式\n.ob off\t//禁用旁观模式\n暗骰与旁观仅在群聊中有效"}, -{"默认骰","&set"}, -{"set","当表达式中‘D’之后没有接面数时,视为投掷默认骰\n.set20 将默认骰设置为20\n.set 不带参数视为将默认骰重置为默认的100\n若所用规则判定掷骰形如2D6,推荐使用.st &=2D6"}, -{"个位骰","个位骰有十面,为0~9十个数字,百面骰的结果为十位骰与个位骰之和(但00+0时视为100)"}, -{"十位骰","十位骰有十面,为00~90十个数字,百面骰的结果为十位骰与个位骰之和(但00+0时视为100)"}, -{"奖励骰","&奖励/惩罚骰"}, -{"惩罚骰","&奖励/惩罚骰"}, -{"奖惩骰","&奖励/惩罚骰"}, -{"奖励/惩罚骰","COC中奖励/惩罚骰是额外投掷的十位骰,最终结果选用点数更低/更高的结果(不出现大失败的情况下等价于更小/更大的十位骰)\n.rb2 2个奖励骰\nrcp 射击 1个惩罚骰"}, -{"随机姓名","&name"}, -{"name","随机姓名:.name (cn/jp/en)([生成数量])\n.name 10\t//默认三类名称随机生成\n.name en\t//后接cn/jp/en则限定生成中文/日文/英文名\n名称生成个数范围1-10,太多容易发现姓名库的寒酸"}, -{"设置昵称","&nn"}, -{"昵称","&nn"}, -{"nn","设置昵称:.nn [昵称] / .nn / .nnn(cn/jp/en) \n.nn kp\t//昵称前的./!等符号会被自动忽略\n.nn\t//视为删除昵称\n.nnn\t//设置为随机昵称\n.nnn jp\t/设置限定随机昵称\n私聊.nn视为操作全局昵称\n优先级:群昵称>全局昵称>群名片>QQ昵称"}, -{"人物作成","该版本人物作成支持COC7(.coc、.draw调查员背景/英雄天赋)、COC6(.coc6、.draw煤气灯)、DND(.dnd)、AMGC(.draw AMGC)"}, -{"coc","克苏鲁的呼唤(COC)人物作成:.coc([7/6])(d)([生成数量])\n.coc 10\t//默认生成7版人物\n.coc6d\t//接d为详细作成,一次只能作成一个\n仅用作骰点法人物作成,可应用变体规则,参考.rules创建调查员的其他选项"}, -{"dnd","龙与地下城(DND)人物作成:.dnd([生成数量])\n.dnd 5\t//仅作参考,可自行应用变体规则"}, -{"属性记录","&st"}, -{"st","属性记录:.st (del/clr/show) ([属性名]:[属性值])\n用户默认所有群使用同一张卡,pl如需多开请使用.pc指令切卡\n.st力量:50 体质:55 体型:65 敏捷:45 外貌:70 智力:75 意志:35 教育:65 幸运:75\n.st hp-1 后接+/-时视为从原值上变化\n.st san+1d6 修改属性时可使用掷骰表达式\n.st del kp裁决\t//删除已保存的属性\n.st clr\t//清空当前卡\n.st show 灵感\t//查看指定属性\n.st show\t//无参数时查看所有属性,请使用只st加点过技能的半自动人物卡!\n部分COC属性会被视为同义词,如智力/灵感、理智/san、侦查/侦察"}, -{"角色卡","&pc"}, -{"pc",R"(角色卡:.pc -每名用户最多可同时保存16张角色卡 -.pc new ([模板]:([生成参数]:))([卡名]) -完全省略参数将生成一张COC7模板的随机姓名卡 -.pc tag ([卡名]) //为当前群绑定指定卡,为空则解绑使用默认卡 -所有群默认使用私聊绑定卡,未绑定则使用0号卡 -.pc show ([卡名]) //展示指定卡所有记录的属性,为空则展示当前卡 -.pc nn [新卡名] //重命名当前卡,不允许重名 -.pc cpy [卡名1]=[卡名2] //将后者属性复制给前者 -.pc del [卡名] //删除指定卡 -.pc list //列出全部角色卡 -.pc grp //列出各群绑定卡 -.pc build ([生成参数]:)(卡名) //根据模板填充生成属性(COC7为9项主属性) -.pc redo ([生成参数]:)(卡名) //清空原有属性后重新生成 -.pc clr //销毁全部角色卡记录 +{"r",".r [ʽ] ([ԭ]) [ʽ]([]#)[Ӹ]d(p[ͷ])(k[ȡ])ʱΪһĬ\nϷҪ1-101-9Χ1-100Χ1-1000\n.r3#d\t//3\n.rhѧ \n.rs1D10+1D6+3 ɳӥ˺\t//ʡԵӵĵֱӸ\nְ汾ͷrod棬Ⱥ.obᱻʶΪԹָ"}, +{"","Ⱥ޶ָhΪ˽˺Ⱥobû\nΪ˱֤ͳɹ"}, +{"Թ","&ob"}, +{"Թģʽ","&ob"}, +{"ob","Թģʽ.ob (exit/list/clr/on/off)\n.ob\t//ԹۿԿ˰\n.ob exit\t//˳Թģʽ\n.ob list\t//鿴ȺԹ\n.ob clr\t//Թ\n.ob on\t//ȫȺԹģʽ\n.ob off\t//Թģʽ\nԹ۽ȺЧ"}, +{"Ĭ","&set"}, +{"set","ʽСD֮ûнʱΪͶĬ\n.set20 ĬΪ20\n.set ΪĬΪĬϵ100\nùж2D6Ƽʹ.st &=2D6"}, +{"λ","λʮ棬Ϊ0~9ʮ֣ĽΪʮλλ֮ͣ00+0ʱΪ100"}, +{"ʮλ","ʮλʮ棬Ϊ00~90ʮ֣ĽΪʮλλ֮ͣ00+0ʱΪ100"}, +{"","&/ͷ"}, +{"ͷ","&/ͷ"}, +{"","&/ͷ"}, +{"/ͷ","COCн/ͷǶͶʮλսѡõ/ߵĽִʧܵµȼڸС/ʮλ\n.rb2 2\nrcp 1ͷ"}, +{"","&name"}, +{"name",".name (cn/jp/en)([])\n.name 10\t//Ĭ\n.name en\t//cn/jp/en޶//Ӣ\nɸΧ1-10̫׷ĺ"}, +{"dz","&nn"}, +{"dz","&nn"}, +{"nn","dzƣ.nn [dz] / .nn / .nnn(cn/jp/en) \n.nn kp\t//dzǰ./ȷŻᱻԶ\n.nn\t//Ϊɾdz\n.nnn\t//Ϊdz\n.nnn jp\t/޶dz\n˽.nnΪȫdz\nȼȺdz>ȫdz>ȺƬ>QQdz"}, +{"","ð汾֧COC7(.coc.drawԱ/Ӣ츳)COC6(.coc6.drawú)DND(.dnd)AMGC(.draw AMGC)"}, +{"coc","³ĺ(COC)ɣ.coc([7/6])(d)([])\n.coc 10\t//Ĭ7\n.coc6d\t//dΪϸɣһֻһ\n㷨ɣӦñ򣬲ο.rulesԱѡ"}, +{"dnd","³(DND)ɣ.dnd([])\n.dnd 5\t//οӦñ"}, +{"Լ¼","&st"}, +{"st","Լ¼.st (del/clr/show) ([]:[ֵ])\nûĬȺʹͬһſpl࿪ʹ.pcָп\n.st:50 :55 :65 :45 ò:70 :75 ־:35 :65 :75\n.st hp-1 +/-ʱΪԭֵϱ仯\n.st san+1d6 ޸ʱʹʽ\n.st del kpþ\t//ɾѱ\n.st clr\t//յǰ\n.st show \t//鿴ָ\n.st show\t//޲ʱ鿴ԣʹֻstӵܵİԶ│\nCOCԻᱻΪͬʣ/С/san/"}, +{"ɫ","&pc"}, +{"pc",R"(ɫ.pc +ÿûͬʱ16Žɫ +.pc new ([ģ]:([ɲ]:))([]) +ȫʡԲһCOC7ģ +.pc tag ([]) //ΪǰȺָΪʹĬϿ +ȺĬʹ˽İ󶨿δʹ0ſ +.pc show ([]) //չʾָм¼ԣΪչʾǰ +.pc nn [¿] //ǰ +.pc cpy [1]=[2] //ԸƸǰ +.pc del [] //ɾָ +.pc list //гȫɫ +.pc grp //гȺ󶨿 +.pc build ([ɲ]:)() //ģԣCOC7Ϊ9ԣ +.pc redo ([ɲ]:)() //ԭԺ +.pc clr //ȫɫ¼ )" }, {"rc", "&rc/ra"}, {"ra", "&rc/ra"}, - {"检定", "&rc/ra"}, + {"춨", "&rc/ra"}, { "rc/ra", - "检定指令:.rc/ra [属性名]([成功率])\n角色卡设置了属性时,可省略成功率\n.rc体质*5\t//允许使用+-*/,但顺序要求为乘法>加减>除法\n.rc 困难幸运\t//技能名开头的困难和极难会被视为关键词\n.rc 敏捷-10\t//修正后成功率必须在1-1000内\n.rcp 手枪\t//奖惩骰至多9个\n默认以规则书判定,大成功大失败的房规由.setcoc设置" + "춨ָ.rc/ra []([ɹ])\nɫʱʡԳɹ\n.rc*5\t//ʹ+-*/˳ҪΪ˷>Ӽ>\n.rc \t//ͷѺͼѻᱻΪؼ\n.rc -10\t//ɹʱ1-1000\n.rcp ǹ\t//9\nĬԹжɹʧܵķ.setcoc" }, { "setcoc", - "为当前群或讨论组设置COC房规,如.setcoc 1,当前参数0-5\n0 规则书\n出1大成功\n不满50出96 - 100大失败,满50出100大失败\n1\n不满50出1大成功,满50出1 - 5大成功\n不满50出96 - 100大失败,满50出100大失败\n2\n出1 - 5且 <= 成功率大成功\n出100或出96 - 99且 > 成功率大失败\n3\n出1 - 5大成功\n出96 - 100大失败\n4\n出1 - 5且 <= 十分之一大成功\n不满50出 >= 96 + 十分之一大失败,满50出100大失败\n5\n出1 - 2且 < 五分之一大成功\n不满50出96 - 100大失败,满50出99 - 100大失败\n" + "ΪǰȺCOC棬.setcoc 1,ǰ0-5\n0 \n1ɹ\n5096 - 100ʧܣ50100ʧ\n1\n501ɹ501 - 5ɹ\n5096 - 100ʧܣ50100ʧ\n2\n1 - 5 <= ɹʴɹ\n10096 - 99 > ɹʴʧ\n3\n1 - 5ɹ\n96 - 100ʧ\n4\n1 - 5 <= ʮ֮һɹ\n50 >= 96 + ʮ֮һʧܣ50100ʧ\n5\n1 - 2 < ֮һɹ\n5096 - 100ʧܣ5099 - 100ʧ\n" }, {"san check", "&sc"}, - {"理智检定", "&sc"}, + {"Ǽ춨", "&sc"}, { "sc", - "San Check指令:.sc[成功损失]/[失败损失] ([当前san值])\n已经.st了理智/san时,可省略最后的参数\n.sc0/1 70\n.sc1d10/1d100 直面外神\n大失败自动失去最大值\n当调用角色卡san时,san会自动更新为sc后的剩余值\n程序上可以损失负数的san,也就是可以用.sc-1d6/-1d6来回复san,但请避免这种奇怪操作" + "San Checkָ.sc[ɹʧ]/[ʧʧ] ([ǰsanֵ])\nѾ.st/sanʱʡIJ\n.sc0/1 70\n.sc1d10/1d100 ֱ\nʧԶʧȥֵ\nýɫsanʱsanԶΪscʣֵ\nϿʧsanҲǿ.sc-1d6/-1d6ظsanֲ" }, {"ti", "&ti/li"}, {"li", "&ti/li"}, - {"疯狂症状", "&ti/li"}, - {"ti/li", "疯狂症状:\n.ti 临时疯狂症状\n.li 总结疯狂症状\n适用coc7版规则,6版请自行用百面骰配合查表\n具体适用哪项参见.rules 疯狂发作"}, - {"成长检定", "&en"}, - {"增强检定", "&en"}, + {"֢״", "&ti/li"}, + {"ti/li", "֢״\n.ti ʱ֢״\n.li ֢ܽ״\ncoc76ðϲ\nμ.rules "}, + {"ɳ춨", "&en"}, + {"ǿ춨", "&en"}, { "en", - "成长检定:.en [技能名称]([技能值])(([失败成长值]/)[成功成长值])\n已经.st时,可省略最后的参数\n.en 教育 60 +1D10 教育增强\t//后接成功时成长值\n.en 幸运 +1D3/1D10幸运成长\t//调用人物卡属性时,成长后的值会自动更新\n可变成长值必须以加减号开头,不限制加减" + "ɳ춨.en []([ֵ])(([ʧܳɳֵ]/)[ɹɳֵ])\nѾ.stʱʡIJ\n.en 60 +1D10 ǿ\t//ӳɹʱɳֵ\n.en +1D3/1D10˳ɳ\t//│ʱɳֵԶ\nɱɳֵԼӼſͷƼӼ" }, - {"抽牌", "&draw"}, + {"", "&draw"}, { "draw", - "抽牌:.draw [牌堆名称] ([抽牌数量])\t//抽到的牌不放回,抽牌数量不能超过牌堆数量\n当前内置牌堆:硬币/性别/\n调查员职业/调查员背景/英雄天赋/煤气灯/个人描述/思想信念/重要之人/重要之人理由/意义非凡之地/宝贵之物/调查员特点/即时症状/总结症状/恐惧症状/狂躁症状/\n阵营/哈罗花色/冒险点子/\n人偶暗示/人偶宝物/人偶记忆碎片/人偶依恋/\nAMGC/AMGC身材/AMGC专精/AMGC武器/AMGC套装/AMGC才能/AMGC特技1/AMGC特技2/AMGC特技3\n/塔罗牌/正逆/塔罗牌占卜/单张塔罗牌/圣三角牌阵/四要素牌阵/小十字牌阵/六芒星牌阵/凯尔特十字牌阵\n.help审判正位(牌+方向)可获取塔罗牌解读\n扩展牌堆:{扩展牌堆}" + "ƣ.draw [ƶ] ([])\t//鵽ƲŻأܳƶ\nǰƶѣӲ/Ա/\nԱְҵ/Ա/Ӣ츳/ú//˼/Ҫ֮/Ҫ֮/Ƿ֮/֮/Աص/ʱ֢״/֢ܽ״/־֢״/֢״/\nӪ/޻ɫ/ðյ/\nżʾ/ż/żƬ/ż/\nAMGC/AMGC/AMGCר/AMGC/AMGCװ/AMGC/AMGCؼ1/AMGCؼ2/AMGCؼ3\n///ռ//ʥ/Ҫ/Сʮ/â/ʮ\n.helpλ+򣩿ɻȡƽ\nչƶ:{չƶ}" }, - {"先攻", "&ri"}, - {"扩展牌堆", ""}, - {"ri", "先攻(群聊限定):.ri([加值])([昵称])\n.ri -1 某pc\t//自动记入先攻列表\n.ri +5 boss"}, - {"先攻列表", "&init"}, - {"init", "先攻列表:\n.init\t//查看先攻列表\n.init clr\t//清除先攻列表"}, - {"骰池", "&ww"}, - {"ww", "骰池:.w(w) [骰子个数]a[加骰参数]\n.w会直接给出结果而.ww会给出每个骰子的点数\n固定10面骰,每有一个骰子点数达到加骰参数,则加骰一次,最后计算点数达到8的骰子数\n具体用法请参考相关游戏规则"}, - {"第三人称", "&me"}, - {"第三人称动作", "&me"}, + {"ȹ", "&ri"}, + {"չƶ", ""}, + {"ri", "ȹȺ޶.ri([ֵ])([dz])\n.ri -1 ijpc\t//Զȹб\n.ri +5 boss"}, + {"ȹб", "&init"}, + {"init", "ȹб\n.init\t//鿴ȹб\n.init clr\t//ȹб"}, + {"", "&ww"}, + {"ww", "أ.w(w) [Ӹ]a[]\n.wֱӸ.wwÿӵĵ\n̶10ÿһӵﵽһΣﵽ8\n÷οϷ"}, + {"˳", "&me"}, + {"˳ƶ", "&me"}, { "me", - "第三人称动作:.me([群号])[动作].me 笑出了声\t//仅限群聊使用\n.me 941980833 抱群主\t//仅限私聊使用,此命令可伪装成骰子在群里说话\n.me off\t//群内禁用.me\n.me on\t//群内启用.me" + "˳ƶ.me([Ⱥ])[].me Ц\t//Ⱥʹ\n.me 941980833 Ⱥ\t//˽ʹãαװȺ˵\n.me off\t//Ⱥڽ.me\n.me on\t//Ⱥ.me" }, - {"今日人品", "&jrrp"}, + {"Ʒ", "&jrrp"}, { "jrrp", - "今日人品:.jrrp\n.jrrp off\t//群内禁用jrrp\n.jrrp on\t//群内启用jrrp\n一天一个人品值\n2.3.5版本后随机值为均匀分布\n骰娘只负责从溯洄的服务器搬运结果,请勿无能狂怒\n如何发配所有人的命运,只有孕育万千骰娘生机之母,萌妹吃鱼之神,正五棱双角锥体对的监护人,一切诡秘的窥见者,时空舞台外的逆流者,永转的命运之轮——溯洄本体掌握" + "Ʒ.jrrp\n.jrrp off\t//Ⱥڽjrrp\n.jrrp on\t//Ⱥjrrp\nһһƷֵ\n2.3.5汾ֵΪȷֲ\nֻ䧵ķ˽ܿŭ\nη˵ˣֻǧ֮ĸó֮˫׶Եļ໤ˣһйصĿߣʱ̨ߣת֮֡䧱" }, - {"send", "发送消息:.send 想对Master说的话\n如果用来调戏Master请做好心理准备"}, - {"入群欢迎", "&welcome"}, - {"入群欢迎词", "&welcome"}, + {"send", "Ϣ.send Master˵Ļ\nϷMaster׼"}, + {"Ⱥӭ", "&welcome"}, + {"Ⱥӭ", "&welcome"}, { "welcome", - "入群欢迎词:.welcome\n.welcome \\\\{at}欢迎\\\\{nick}入群!\t//\\\\{at}视为at入群者,\\\\{nick}会替换为新人的昵称\n.welcome\t//不带参数时清除欢迎词\n无论开关状态,只要有欢迎词时有人入群,都会响应" + "Ⱥӭʣ.welcome\n.welcome \\\\{at}ӭ\\\\{nick}Ⱥ\t//\\\\{at}ΪatȺߣ\\\\{nick}滻Ϊ˵dz\n.welcome\t//ʱӭ\nۿ״ֻ̬ҪлӭʱȺӦ" }, - {"group", "&群管"}, + {"group", "&Ⱥ"}, { - "群管", - R"(群管指令.group(群管理员限定) -.group state //查看在群内对骰娘的设置 -.group pause/restart //群全体禁言/全体解除禁言 -.group card [at/用户QQ] [名片] //设置群员名片 -.group title [at/用户QQ] [头衔] //设置群员头衔 -.group diver //查看潜水成员 -.group +/-[群管词条] //为群加减设置,需要对应权限 -例:.group +禁用回复 //关闭本群自定义回复 -群管词条:停用指令/禁用回复/禁用jrrp/禁用draw/禁用me/禁用help/禁用ob/拦截消息/许可使用/免清/免黑)" + "Ⱥ", + R"(Ⱥָ.group(ȺԱ޶) +.group state //鿴Ⱥڶ +.group pause/restart //Ⱥȫ/ȫ +.group card [at/ûQQ] [Ƭ] //ȺԱƬ +.group title [at/ûQQ] [ͷ] //ȺԱͷ +.group diver //鿴DZˮԱ +.group +/-[Ⱥܴ] //ΪȺӼãҪӦȨ +:.group +ûظ //رձȺԶظ +Ⱥܴ:ָͣ/ûظ/jrrp/draw/me/help/ob/Ϣ/ʹ//)" }, - {"溯洄", "孕育万千骰娘生机之母,萌妹吃鱼之神,正五棱双角锥体对的监护人,一切诡秘的窥见者,拟人者主宰,时空舞台外的逆流者,永转的命运之轮"}, - {"投食", "投食Shiki,请选择http://shiki.stringempty.xyz/alipay.png\n投食溯洄,可选择https://afdian.net/@suhuiw4123"}, - {"愚者正位", "憧憬自然的地方、毫无目的地前行、喜欢尝试挑战新鲜事物、四处流浪。美好的梦想。"}, + {"", "ǧ֮ĸó֮˫׶Եļ໤ˣһйصĿߣףʱ̨ߣת֮"}, + {"Ͷʳ", "ͶʳShikiѡhttp://shiki.stringempty.xyz/alipay.png\nͶʳ䧣ѡhttps://afdian.net/@suhuiw4123"}, + {"λ", "ȻĵطĿĵǰСϲսĴˡõ롣"}, { - "愚者逆位", - "冒险的行动,追求可能性,重视梦想,无视物质的损失,离开家园,过于信赖别人,为出外旅行而烦恼。心情空虚、轻率的恋情、无法长久持续的融洽感、不安的爱情的旅程、对婚姻感到束缚、彼此忽冷忽热、不顾众人反对坠入爱河、为恋人的负心所伤、感情不专一。" + "λ", + "ðյж׷ԣ룬ʵʧ뿪԰ˣΪжա顢ʵ顢޷óǢСİó̡Իе˴˺ȡ˷׹밮ӡΪ˵ĸˡ鲻רһ" }, - {"魔术师正位", "事情的开始,行动的改变,熟练的技术及技巧,贯彻我的意志,运用自然的力量来达到野心。"}, - {"魔术师逆位", "意志力薄弱,起头难,走入错误的方向,知识不足,被骗和失败。"}, - {"女祭司正位", "开发出内在的神秘潜力,前途将有所变化的预言,深刻地思考,敏锐的洞察力,准确的直觉。"}, - {"女祭司逆位", "过于洁癖,无知,贪心,目光短浅,自尊心过高,偏差的判断,有勇无谋,自命不凡。"}, - {"女皇正位", "幸福,成功,收获,无忧无虑,圆满的家庭生活,良好的环境,美貌,艺术,与大自然接触,愉快的旅行,休闲。"}, - {"女皇逆位", "不活泼,缺乏上进心,散漫的生活习惯,无法解决的事情,不能看到成果,担于享乐,环境险恶,与家人发生纠纷。"}, - {"皇帝正位", "光荣,权力,胜利,握有领导权,坚强的意志,达成目标,父亲的责任,精神上的孤单。"}, - {"皇帝逆位", "幼稚,无力,独裁,撒娇任性,平凡,没有自信,行动力不足,意志薄弱,被支配。"}, - {"教皇正位", "援助,同情,宽宏大量,可信任的人给予的劝告,良好的商量对象,得到精神上的满足,遵守规则,志愿者。"}, - {"教皇逆位", "错误的讯息,恶意的规劝,上当,援助被中断,愿望无法达成,被人利用,被放弃。"}, - {"恋人正位", "撮合,爱情,流行,兴趣,充满希望的未来,魅力,增加朋友。"}, - {"恋人逆位", "禁不起诱惑,纵欲过度,反覆无常,友情变淡,厌倦,争吵,华丽的打扮,优柔寡断。"}, - {"战车正位", "努力而获得成功,胜利,克服障碍,行动力,自立,尝试,自我主张,年轻男子,交通工具,旅行运大吉。"}, - {"战车逆位", "争论失败,发生纠纷,阻滞,违返规则,诉诸暴力,顽固的男子,突然的失败,不良少年,挫折和自私自利。"}, - {"力量正位", "大胆的行动,有勇气的决断,新发展,大转机,异动,以意志力战胜困难,健壮的女人。"}, - {"力量逆位", "胆小,输给强者,经不起诱惑,屈服在权威与常识之下,没有实践便告放弃,虚荣,懦弱,没有耐性。"}, - {"隐者正位", "隐藏的事实,个别的行动,倾听他人的意见,享受孤独,有益的警戒,年长者,避开危险,祖父,乡间生活。"}, - {"隐者逆位", "憎恨孤独,自卑,担心,幼稚思想,过于慎重导致失败,偏差,不宜旅行。"}, - {"命运之轮正位", "关键性的事件,有新的机会,因的潮流,环境的变化,幸运的开端,状况好转,问题解决,幸运之神降临。"}, - {"命运之轮逆位", "挫折,计划泡汤,障碍,无法修正方向,往坏处发展,恶性循环,中断。"}, - {"正义正位", "公正、中立、诚实、心胸坦荡、表里如一、身兼二职、追求合理化、协调者、与法律有关、光明正大的交往、感情和睦。"}, - {"正义逆位", "失衡、偏见、纷扰、诉讼、独断专行、问心有愧、无法两全、表里不一、男女性格不合、情感波折、无视社会道德的恋情。"}, - {"倒吊人正位", "接受考验、行动受限、牺牲、不畏艰辛、不受利诱、有失必有得、吸取经验教训、浴火重生、广泛学习、奉献的爱。"}, - {"倒吊人逆位", "无谓的牺牲、骨折、厄运、不够努力、处于劣势、任性、利己主义者、缺乏耐心、受惩罚、逃避爱情、没有结果的恋情。"}, - {"死神正位", "失败、接近毁灭、生病、失业、维持停滞状态、持续的损害、交易停止、枯燥的生活、别离、重新开始、双方有很深的鸿沟、恋情终止。"}, - {"死神逆位", "抱有一线希望、起死回生、回心转意、摆脱低迷状态、挽回名誉、身体康复、突然改变计划、逃避现实、斩断情丝、与旧情人相逢。"}, - {"节制正位", "单纯、调整、平顺、互惠互利、好感转为爱意、纯爱、深爱。"}, - {"节制逆位", "消耗、下降、疲劳、损失、不安、不融洽、爱情的配合度不佳。"}, - {"恶魔正位", "被束缚、堕落、生病、恶意、屈服、欲望的俘虏、不可抗拒的诱惑、颓废的生活、举债度日、不可告人的秘密、私密恋情。"}, - {"恶魔逆位", "逃离拘束、解除困扰、治愈病痛、告别过去、暂停、别离、拒绝诱惑、舍弃私欲、别离时刻、爱恨交加的恋情。"}, - {"塔正位", "破产、逆境、被开除、急病、致命的打击、巨大的变动、受牵连、信念崩溃、玩火自焚、纷扰不断、突然分离,破灭的爱。"}, - {"塔逆位", "困境、内讧、紧迫的状态、状况不佳、趋于稳定、骄傲自大将付出代价、背水一战、分离的预感、爱情危机。"}, - {"星星正位", "前途光明、充满希望、想象力、创造力、幻想、满足愿望、水准提高、理想的对象、美好的恋情。"}, - {"星星逆位", "挫折、失望、好高骛远、异想天开、仓皇失措、事与愿违、工作不顺心、情况悲观、秘密恋情、缺少爱的生活。"}, - {"月亮正位", "不安、迷惑、动摇、谎言、欺骗、鬼迷心窍、动荡的爱、三角关系。"}, - {"月亮逆位", "逃脱骗局、解除误会、状况好转、预知危险、等待、正视爱情的裂缝。"}, - {"太阳正位", "活跃、丰富的生命力、充满生机、精力充沛、工作顺利、贵人相助、幸福的婚姻、健康的交际。"}, - {"太阳逆位", "消沉、体力不佳、缺乏连续性、意气消沉、生活不安、人际关系不好、感情波动、离婚。"}, - {"审判正位", "复活的喜悦、康复、坦白、好消息、好运气、初露锋芒、复苏的爱、重逢、爱的奇迹。"}, - {"审判逆位", "一蹶不振、幻灭、隐瞒、坏消息、无法决定、缺少目标、没有进展、消除、恋恋不舍。"}, - {"世界正位", "完成、成功、完美无缺、连续不断、精神亢奋、拥有毕生奋斗的目标、完成使命、幸运降临、快乐的结束、模范情侣。"}, - {"世界逆位", "未完成、失败、准备不足、盲目接受、一时不顺利、半途而废、精神颓废、饱和状态、合谋、态度不够融洽、感情受挫。"}, + {"ħʦλ", "Ŀʼжĸı䣬ļɣ᳹ҵ־ȻﵽҰġ"}, + {"ħʦλ", "־ͷѣķ֪ʶ㣬ƭʧܡ"}, + {"Ů˾λ", "ڵDZǰ;仯Ԥԣ̵˼Ķ׼ȷֱ"}, + {"Ů˾λ", "ڽ񱣬֪̰ģĿdzĹߣƫжϣı"}, + {"Ůλ", "ҸɹջǣԲļͥõĻòȻӴУС"}, + {"Ůλ", "ãȱϽģɢϰߣ޷飬ܿɹ֣ն˷ס"}, + {"ʵλ", "٣Ȩʤ쵼Ȩǿ־Ŀ꣬׵Σϵŵ"}, + {"ʵλ", "ɣãԣƽûţж㣬־֧䡣"}, + {"̻λ", "Ԯͬ飬ε˸Ȱ棬õ󣬵õϵ㣬ع־Ըߡ"}, + {"̻λ", "ѶϢĹȰϵԮжϣԸ޷ɣã"}, + {"λ", "ϣ飬УȤϣδѡ"}, + {"λ", "ջȣ޳䵭룬Ĵ磬Ѷϡ"}, + {"սλ", "Ŭóɹʤ˷ϰжԣţӣͨߣ˴󼪡"}, + {"սλ", "ʧܣףͣΥ̵ӣͻȻʧܣ꣬ۺ˽"}, + {"λ", "󵨵жľϣ·չת춯־սʤѣ׳Ůˡ"}, + {"λ", "СǿߣջȨ볣ʶ֮£ûʵ٣ųûԡ"}, + {"λ", "صʵж˵ܹ¶ľ䣬곤ߣܿΣգ游"}, + {"λ", "޹¶Աģ˼룬صʧܣƫС"}, + {"֮λ", "ؼԵ¼µĻᣬijı仯˵Ŀˣ״ת֮١"}, + {"֮λ", "ۣƻϰ޷չѭжϡ"}, + {"λ", "ʵ̹һְ׷Эߡ뷨йءĽ"}, + {"λ", "ʧ⡢ƫšϡרС޷ȫﲻһŮԸ񲻺ϡвۡµ顣"}, + {"λ", "ܿ顢жޡηաʧеáȡѵԡ㷺ѧϰ׵İ"}, + {"λ", "νۡˡŬơԡߡȱġܳͷӱܰ顢ûн顣"}, + {"λ", "ʧܡӽʧҵάͣ״̬𺦡ֹͣ롢¿ʼ˫кĺ蹵ֹ"}, + {"λ", "һϣת⡢ѵ״̬念ͻȻıƻӱʵն˿ꡣ"}, + {"λ", "ƽ˳ݻøתΪ⡢"}, + {"λ", "ġ½ƣ͡ʧǢ϶Ȳѡ"}, + {"ħλ", "䡢⡢ķ²ɿܵջǷϵծաɸ˵ܡ˽顣"}, + {"ħλ", "šʹȥͣ롢ܾջ˽ʱ̡޽ӵ顣"}, + {"λ", "Ʋ澳Ĵ޴ı䶯ǣԷ١ŲϡͻȻ룬İ"}, + {"λ", "ڧȵ״̬״ѡȶԴ󽫸ۡˮһսԤСΣ"}, + {"λ", "ǰ;ϣ롢Ըˮ׼ߡĶõ顣"}, + {"λ", "ۡʧøԶ쿪ֻʧ롢ԸΥ˳ġۡ顢ȱٰ"}, + {"λ", "Ի󡢶ҡԡƭϡİǹϵ"}, + {"λ", "ƭ֡ᡢ״תԤ֪ΣաȴӰѷ졣"}, + {"̫λ", "Ծḻ桢˳ҸĻĽʡ"}, + {"̫λ", "ѡȱԡ˼ʹϵá鲨顣"}, + {"λ", "ϲá̹סϢ¶âյİطꡢ漣"}, + {"λ", "һ겻񡢻Ϣ޷ȱĿꡢûнչᡣ"}, + {"λ", "ɡɹȱϡ񿺷ܡӵбܶĿꡢʹ˽١ֵĽģ¡"}, + {"λ", "δɡʧܡ׼㡢äĿܡһʱ˳;ϡǷϡ״̬ı̬ȲǢܴ졣"}, }; std::string getMsg(const std::string& key, const std::map& maptmp) diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 8078acba..0a17a232 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -35,9 +35,9 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 563u; -inline const std::string Dice_Ver_Without_Build = "2.4.0beta3"; -constexpr auto DiceRequestHeader = "Dice/2.4.0BETA3"; +const unsigned short Dice_Build = 564u; +inline const std::string Dice_Ver_Without_Build = "2.4.0"; +constexpr auto DiceRequestHeader = "Dice/2.4.0"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by Shiki Ver " + Dice_Ver; diff --git a/Dice/app.json b/Dice/app.json index c7d2e8ec..f2ad4dc9 100644 --- a/Dice/app.json +++ b/Dice/app.json @@ -2,8 +2,8 @@ "ret": 1, "apiver": 9, "name": "Dice!", - "version": "2.4.0beta3", - "version_id": 563, + "version": "2.4.0", + "version_id": 564, "author": "w4123溯洄 Shiki", "description": "跑团用骰子 本程序使用AGPLv3开源协议授权 Copyright (c) 2018-2019 w4123溯洄 Shiki", "event": [ From 55eb52474198a51572ce47fb9c52321783a41380 Mon Sep 17 00:00:00 2001 From: lunzhipenxil Date: Mon, 27 Jul 2020 21:19:30 +0800 Subject: [PATCH 006/171] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAdmin=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=8C=87=E4=BB=A4=E9=80=80=E7=BE=A4=E6=97=B6=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E8=AF=8D=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dice/DiceEvent.cpp | 4 ++-- Dice/GlobalVar.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 8ac7d74b..35169652 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -770,7 +770,7 @@ int FromMsg::DiceReply() reply(GlobalMsg["strGroupAway"]); } if (trustedQQ(fromQQ) > 2) { - grp.leave(GlobalMsg["strAdminDismiss"]); + grp.leave(format(GlobalMsg["strAdminDismiss"], GlobalMsg, strVar)); reply(GlobalMsg["strGroupExit"]); } else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1) @@ -787,7 +787,7 @@ int FromMsg::DiceReply() if (QQNum.empty() || QQNum == to_string(console.DiceMaid) || (QQNum.length() == 4 && stoll(QQNum) == getLoginQQ() % 10000)){ if (trusted > 2) { - pGrp->leave(GlobalMsg["strAdminDismiss"]); + pGrp->leave(format(GlobalMsg["strAdminDismiss"], GlobalMsg, strVar)); } if (pGrp->isset("ЭЧ"))return 0; if (isAuth) diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index b57c54b7..3bc3982c 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -153,8 +153,8 @@ std::map GlobalMsg {"strLinkNotFound","ʱҪͨ򲻿״ĵطˡ"}, {"strNotMaster","㲻{self}masterʲô"}, {"strNotAdmin","㲻{self}ĹԱ"}, - { "strAdminDismiss", "{strDismiss}" }, //ԱָȺĻִ - {"strDismiss", ""}, //.dismissȺǰĻִ + {"strAdminDismiss","{strDismiss}"}, //ԱָȺĻִ + {"strDismiss",""}, //.dismissȺǰĻִ {"strHlpSet","Ϊ{key}ô"}, {"strHlpReset","{key}Ĵ"}, {"strHlpNameEmpty","MasterҪԶʲôѽ"}, From 814b1384b8f381e8cf8a994fd1f53b46e9928c74 Mon Sep 17 00:00:00 2001 From: lunzhipenxil Date: Tue, 28 Jul 2020 01:04:29 +0800 Subject: [PATCH 007/171] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dice/DiceEvent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 35169652..420ca188 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -770,7 +770,7 @@ int FromMsg::DiceReply() reply(GlobalMsg["strGroupAway"]); } if (trustedQQ(fromQQ) > 2) { - grp.leave(format(GlobalMsg["strAdminDismiss"], GlobalMsg, strVar)); + grp.leave(getMsg("strAdminDismiss", strVar)); reply(GlobalMsg["strGroupExit"]); } else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1) @@ -787,7 +787,7 @@ int FromMsg::DiceReply() if (QQNum.empty() || QQNum == to_string(console.DiceMaid) || (QQNum.length() == 4 && stoll(QQNum) == getLoginQQ() % 10000)){ if (trusted > 2) { - pGrp->leave(format(GlobalMsg["strAdminDismiss"], GlobalMsg, strVar)); + pGrp->leave(getMsg("strAdminDismiss", strVar)); } if (pGrp->isset("ЭЧ"))return 0; if (isAuth) From e8150f5429e521bc7696de923dd6198bdbe52fc4 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 29 Jul 2020 21:22:46 +0800 Subject: [PATCH 008/171] update blacklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 允许读取外源黑名单 允许读取云不良记录 优化指令帮助 --- Dice/BlackListManager.cpp | 61 +++++++++++++++++++++++++++++---------- Dice/BlackListManager.h | 2 +- Dice/Dice.cpp | 11 +++---- Dice/DiceConsole.cpp | 4 +-- Dice/DiceEvent.cpp | 56 ++++++++++++++++++++++++++++++++--- Dice/DiceJob.cpp | 17 +++++++++++ Dice/DiceJob.h | 1 + Dice/DiceSchedule.cpp | 3 +- Dice/GlobalVar.cpp | 38 ++++++++++++++++-------- Dice/Jsonio.h | 12 +++++++- Dice/ManagerSystem.h | 2 +- Dice/StrExtern.hpp | 4 +-- 12 files changed, 167 insertions(+), 44 deletions(-) diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 395fb447..519291fa 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -98,6 +98,8 @@ std::queue warningQueue; // ϢͶ mutex warningMutex; +bool isLoadingExtern = false; + void AddWarning(const string& msg, long long DiceQQ, long long fromGroup) { lock_guard lock_queue(warningMutex); @@ -186,6 +188,13 @@ DDBlackMark::DDBlackMark(void* pJson) else if (!credit_limit.count(j["type"].get())) { return; } else type = j["type"].get(); } + if (j.count("wid")) { + j["wid"].get_to(wid); + if (j.count("isErased")) { + isClear = j["isErased"].get(); + isAdd = !isClear; + } + } if (j.count("fromGroup"))fromGroup = {j["fromGroup"].get(), isAdd}; if (j.count("fromQQ"))fromQQ = {j["fromQQ"].get(), isAdd}; if (j.count("inviterQQ"))inviterQQ = {j["inviterQQ"].get(), isAdd}; @@ -194,12 +203,14 @@ DDBlackMark::DDBlackMark(void* pJson) if (j.count("DiceMaid"))DiceMaid = j["DiceMaid"].get(); if (j.count("masterQQ"))masterQQ = j["masterQQ"].get(); - if (j.count("wid"))wid = j["wid"].get(); if (j.count("danger")) { danger = j["danger"].get(); if (danger < 1)return; } + else { + danger = (type == "ruler") ? 3 : 2; + } if (j.count("time"))time = UTF8toGBK(j["time"].get()); if (j.count("note"))note = UTF8toGBK(j["note"].get()); @@ -221,7 +232,7 @@ DDBlackMark::DDBlackMark(void* pJson) } catch (...) { - console.log("jsonʧܣ", 0b10, printSTNow()); + console.log("jsonʧܣ", 0, printSTNow()); return; } } @@ -618,7 +629,7 @@ void DDBlackManager::insert(DDBlackMark& ex_mark) up_qq_danger(mark.ownerQQ.first, mark); } } - if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); } bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) @@ -730,7 +741,7 @@ bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) reset_qq_danger(mark.inviterQQ.first); isUpdated = true; } - else if (delta_danger > 0 || credit > 2) + else if (delta_danger > 0 || credit > 2) { old_mark.inviterQQ.second = true; up_qq_danger(mark.inviterQQ.first, mark); @@ -773,7 +784,10 @@ bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) if (isUpdated) { if (!mark.comment.empty())old_mark.comment = mark.comment; - if (Enabled)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + else if (old_mark.comment.empty()) { + old_mark.comment = printSTNow() + " "; + } + if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); } return isUpdated; } @@ -794,7 +808,7 @@ void DDBlackManager::reset_group_danger(long long llgroup) else { mGroupDanger.erase(llgroup); - if (Enabled)console.log("" + printGroup(llgroup) + "Σյȼ", 0b10, printSTNow()); + if (Enabled && !isLoadingExtern)console.log("" + printGroup(llgroup) + "Σյȼ", 0b10, printSTNow()); } } @@ -821,7 +835,7 @@ void DDBlackManager::reset_qq_danger(long long llqq) mQQDanger.erase(llqq); if (Enabled) { - console.log("" + printQQ(llqq) + "Σյȼ", 0b10, printSTNow()); + if (!isLoadingExtern)console.log("" + printQQ(llqq) + "Σյȼ", 0b10, printSTNow()); if (UserList.count(llqq))AddMsgToQueue(getMsg("strBlackQQDelNotice", {{"user_nick", getName(llqq)}}), llqq); } } @@ -837,9 +851,9 @@ bool DDBlackManager::up_group_danger(long long llgroup, DDBlackMark& mark) if (mGroupDanger.count(llgroup) && mGroupDanger[llgroup] >= mark.danger)return false; if (Enabled) { - if (console["LeaveBlackGroup"] && ChatList.count(llgroup) && !chat(llgroup).isset(""))chat(llgroup).leave( + if (ChatList.count(llgroup) && !chat(llgroup).isset(""))chat(llgroup).leave( mark.warning()); - console.log(GlobalMsg["strSelfName"] + "ѽ" + printGroup(llgroup) + "Σյȼ" + to_string(mark.danger), 0b10, + if(!isLoadingExtern)console.log(GlobalMsg["strSelfName"] + "ѽ" + printGroup(llgroup) + "Σյȼ" + to_string(mark.danger), 0b10, printSTNow()); } mGroupDanger[llgroup] = mark.danger; @@ -850,7 +864,7 @@ bool DDBlackManager::up_qq_danger(long long llqq, DDBlackMark& mark) { if (trustedQQ(llqq) > 1 || llqq == console.master() || llqq == console.DiceMaid) { - if (mark.fromQQ.first == llqq)mark.fromQQ.second = false; + if (mark.fromQQ.first == llqq)mark.erase(); if (mark.inviterQQ.first == llqq)mark.inviterQQ.second = false; if (mark.ownerQQ.first == llqq)mark.ownerQQ.second = false; return false; @@ -864,9 +878,11 @@ bool DDBlackManager::up_qq_danger(long long llqq, DDBlackMark& mark) : AddMsgToQueue(getMsg("strBlackQQAddNoticeReason", { {"0", mark.note}, {"reason", mark.note}, {"user_nick", getName(llqq)} }), llqq); - console.log(GlobalMsg["strSelfName"] + "ѽ" + printQQ(llqq) + "Σյȼ" + to_string(mark.danger), 0b10, - printSTNow()); - checkGroupWithBlackQQ(mark, llqq); + if (!isLoadingExtern) { + console.log(GlobalMsg["strSelfName"] + "ѽ" + printQQ(llqq) + "Σյȼ" + to_string(mark.danger), 0b10, + printSTNow()); + checkGroupWithBlackQQ(mark, llqq); + } } mQQDanger[llqq] = mark.danger; return true; @@ -1041,6 +1057,7 @@ void DDBlackManager::add_black_group(long long llgroup, FromMsg* msg) mark.time = msg->strVar["time"]; mark.DiceMaid = console.DiceMaid; mark.masterQQ = console.masterQQ; + mark.comment = printSTNow() + " " + printQQ(msg->fromQQ) + ""; insert(mark); msg->note("" + printGroup(llgroup) + "ĺ¼"); } @@ -1064,6 +1081,7 @@ void DDBlackManager::add_black_qq(long long llqq, FromMsg* msg) mark.time = msg->strVar["time"]; mark.DiceMaid = console.DiceMaid; mark.masterQQ = console.masterQQ; + mark.comment = printSTNow() + " " + printQQ(msg->fromQQ) + ""; insert(mark); msg->note("" + printQQ(llqq) + "ıغ¼"); } @@ -1219,27 +1237,40 @@ void DDBlackManager::create(DDBlackMark& mark) insert(mark); } -int DDBlackManager::loadJson(string strPath) +int DDBlackManager::loadJson(string strPath, bool isExtern) { json j = freadJson(strPath); if (j.is_null())return -1; if (j.size() > vBlackList.capacity())vBlackList.reserve(j.size() * 2); + int cnt(0); for (auto& item : j) { DDBlackMark mark{&item}; if (!mark.isValid)continue; if (!mark.danger)mark.danger = 2; //² + isLoadingExtern = true; if (int res = find(mark); res < 0) { insert(mark); } else { + if (isExtern) { + if (mark.isSource(console.DiceMaid))continue; //漰IJ + if (mark.danger != vBlackList[res].danger)continue; //ΣյȼرĶIJ + } update(mark, res); } + isLoadingExtern = false; + cnt++; } - return vBlackList.size(); + if (isExtern) { + filesystem::remove(strPath); + console.log("Դ¼Ŀ" + to_string(cnt) + "", 1, printSTNow()); + blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + } + return cnt; } int DDBlackManager::loadHistory(const string& strLoc) diff --git a/Dice/BlackListManager.h b/Dice/BlackListManager.h index e997df4b..35d83a43 100644 --- a/Dice/BlackListManager.h +++ b/Dice/BlackListManager.h @@ -116,7 +116,7 @@ class DDBlackManager void verify(void*, long long); void create(DDBlackMark&); //ȡjsonʽ¼ - int loadJson(string strPath); + int loadJson(string strPath, bool isExtern = false); //ȡɰ汾б int loadHistory(const string& strLoc); void saveJson(const string& strPath) const; diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 234ab6e6..0a32733d 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -216,8 +216,7 @@ EVE_Enable(eventEnable) R"(ӭʹDice!ˣ ҼQ->Ӧù->˵->MasterģʽлɿMasterģʽĺ̨ ĵ: https://v2docs.kokona.tech -ļο.help -)"); +ļο.help)"); } ifstreamMaster.close(); std::map boolConsole; @@ -226,7 +225,7 @@ EVE_Enable(eventEnable) { console.set(key, val); } - console.setClock({ 5, 5 }, ClockEvent::clear); + console.setClock({ 11, 45 }, ClockEvent::clear); console.loadNotice(); console.save(); } @@ -393,7 +392,9 @@ EVE_Enable(eventEnable) blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); console.log("ʼ¼" + to_string(cnt) + "", 1); } - + else { + blacklist->loadJson(DiceDir + "\\conf\\BlackListEx.json", true); + } fmt = make_unique(); if (loadJMap(DiceDir + "\\conf\\CustomMsg.json", EditedMsg) < 0)loadJMap(strFileLoc + "CustomMsg.json", EditedMsg); //Ԥ޸ijظı @@ -558,7 +559,7 @@ bool eve_GroupAdd(Chat& grp) else { ave_trust /= cntMember; - strMsg += "\nûŨ" + to_string(cntUser * 100 / cntMember) + "%, ζ" + toString(ave_trust); + strMsg += "\nûŨ" + to_string(cntUser * 100 / cntMember) + "% (" + to_string(cntUser) + "/" + to_string(cntMember) + "), ζ" + toString(ave_trust); } } if (!blacks.empty()) diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index cd3d9f90..e813ffed 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -311,7 +311,7 @@ void getDiceList() void getExceptGroup() { std::string list; if (Network::GET("shiki.stringempty.xyz", "/DiceCloud/except_group.json", 80, list)) - json::parse(list, nullptr, false).get_to(ExceptGroups); + readJson(list, ExceptGroups); } @@ -435,7 +435,7 @@ int clearGroup(string strPara, long long fromQQ) if (blacklist->get_group_danger(id)) { res << printGroup(id) + "" + "Ⱥ"; - if (console["LeaveBlackGroup"])grp.leave(getMsg("strBlackGroup")); + grp.leave(getMsg("strBlackGroup")); } vector MemberList = getGroupMemberList(id); for (const auto& eachQQ : MemberList) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 8ac7d74b..7fe66ab8 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1272,6 +1272,11 @@ int FromMsg::DiceReply() } return 1; } + else if (strOpt == "black") { + cmd_key = "cloudblack"; + sch.push_job(*this); + return 1; + } } else if (strLowerMessage.substr(intMsgCnt, 5) == "coc7d" || strLowerMessage.substr(intMsgCnt, 4) == "cocd") { @@ -1292,6 +1297,10 @@ int FromMsg::DiceReply() intMsgCnt += 5; long long llGroup(fromGroup); readSkipSpace(); + if (strMsg.length() == intMsgCnt) { + reply(fmt->get_help("group")); + return 1; + } if (strLowerMessage.substr(intMsgCnt, 3) == "all") { if (trusted < 5) @@ -1695,12 +1704,12 @@ int FromMsg::DiceReply() return -1; } strVar["key"] = readUntilSpace(); - vector* Deck = nullptr; if (strVar["key"].empty()) { reply(GlobalMsg["strParaEmpty"]); return -1; } + vector* Deck = nullptr; CardDeck::mReplyDeck[strVar["key"]] = {}; Deck = &CardDeck::mReplyDeck[strVar["key"]]; while (intMsgCnt != strMsg.length()) @@ -1722,6 +1731,10 @@ int FromMsg::DiceReply() intMsgCnt += 5; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; + if (strMsg.length() == intMsgCnt) { + reply(fmt->get_help("rules")); + return 1; + } if (strLowerMessage.substr(intMsgCnt, 3) == "set") { intMsgCnt += 3; @@ -1806,6 +1819,10 @@ int FromMsg::DiceReply() } intMsgCnt += 4; readSkipSpace(); + if (strMsg.length() == intMsgCnt) { + reply(fmt->get_help("deck")); + return 1; + } string strPara = readPara(); vector *DeckPro = nullptr, *DeckTmp = nullptr; if (intT != PrivateT && CardDeck::mGroupDeck.count(fromGroup)) @@ -2311,6 +2328,10 @@ int FromMsg::DiceReply() { intMsgCnt += 4; readSkipSpace(); + if (strMsg.length() == intMsgCnt) { + reply(fmt->get_help("send")); + return 1; + } //ȿMasterָĿ귢 if (trusted > 2) { @@ -2417,6 +2438,18 @@ int FromMsg::DiceReply() reply(GlobalMsg["strUserTrusted"]); return 1; } + if (strOption == "diss") { + if (trusted < 4 && fromQQ != console.master()) { + reply(GlobalMsg["strNotAdmin"]); + return 1; + } + long long llTargetID(readID()); + if (!llTargetID) { + reply(GlobalMsg["strQQIDEmpty"]); + } + else blacklist->add_black_qq(llTargetID, this); + return 1; + } if (strOption == "kill") { if (trusted < 4 && fromQQ != console.master()) @@ -2590,6 +2623,10 @@ int FromMsg::DiceReply() intMsgCnt += 2; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; + if (strMsg.length() == intMsgCnt) { + reply(fmt->get_help("en")); + return 1; + } strVar["attr"] = readAttrName(); string strCurrentValue = readDigit(false); short nCurrentVal; @@ -2885,6 +2922,10 @@ int FromMsg::DiceReply() { intMsgCnt += 2; string strOption = readPara(); + if (strOption.empty()) { + reply(fmt->get_help("pc")); + return 1; + } Player& pl = getPlayer(fromQQ); if (strOption == "tag") { @@ -3078,6 +3119,10 @@ int FromMsg::DiceReply() { intMsgCnt += 2; readSkipSpace(); + if (strMsg.length() == intMsgCnt) { + reply(fmt->get_help("rc")); + return 1; + } int intRule = intT ? get(chat(fromGroup).intConf, string("rc"), 0) : get(getUser(fromQQ).intConf, string("rc"), 0); @@ -3372,8 +3417,11 @@ int FromMsg::DiceReply() string SanCost = readUntilSpace(); while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - if (SanCost.empty() || SanCost.find('/') == string::npos) - { + if (SanCost.empty()) { + reply(fmt->get_help("sc")); + return 1; + } + if(SanCost.find('/') == string::npos){ reply(GlobalMsg["strSanCostInvalid"]); return 1; } @@ -3463,7 +3511,7 @@ int FromMsg::DiceReply() intMsgCnt++; if (intMsgCnt == strLowerMessage.length()) { - reply(GlobalMsg["strStErr"]); + reply(fmt->get_help("st")); return 1; } if (strLowerMessage.substr(intMsgCnt, 3) == "clr") diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index bc66c128..92aae783 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -82,6 +82,7 @@ void cq_restart(DiceJob& job) { std::this_thread::sleep_for(3s); Enabled = false; dataBackUp(); + std::this_thread::sleep_for(3s); switch (UINT res = -1; res = WinExec(".\\reload.bat", SW_SHOW)) { case 0: job.note("ʧܣڴԴѺľ", 1); @@ -313,6 +314,22 @@ void dice_update(DiceJob& job) { } } +//ȡƲ¼ +void dice_cloudblack(DiceJob& job) { + job.note("ʼȡƶ˼¼", 1); + string strURL("http://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); + switch (Cloud::DownloadFile(strURL.c_str(), "DiceData/conf/CloudBlackList.json")) { + case -1: + job.echo("Ʋ¼ͬʧ:" + strURL); + break; + case -2: + job.echo("Ʋ¼ͬʧ!ļδҵ"); + break; + case 0: + job.note(getMsg("self") + "Ʋ¼ͬɹʼȡ", 1); + blacklist->loadJson("DiceData/conf/CloudBlackList.json", true); + } +} string print_master() { return printQQ(console.master()); } diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 14f5fca3..ea2f5198 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -17,6 +17,7 @@ void clear_group(DiceJob& job); void cloud_beat(DiceJob& job); void dice_update(DiceJob& job); +void dice_cloudblack(DiceJob& job); string print_master(); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index e3fa3001..6d50aa09 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -14,7 +14,8 @@ unordered_map mCommand = { {"reload",cq_restart}, {"die",cq_exit}, {"heartbeat",cloud_beat}, - {"update",dice_update} + {"update",dice_update}, + {"cloudblack",dice_cloudblack} }; diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index b57c54b7..476012b8 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -318,7 +318,11 @@ std::map EditedMsg; const std::map HelpDoc = { {"",R"( 564:๦Żƶѵ +563:Żָ +562:GUI +561:Ⱥ/бڲ 560:֧Mirai, GUI +559:Զ̸²/¼ 558:ÿͳ 557:ʱҵϵͳ 556:ϵͳ @@ -336,34 +340,38 @@ const std::map HelpDoc = { {"趨","Master{master_QQ}\n.meʹãֹ\n.jrrpʹã\n봦ƣǽ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n\nȺ:δã\nٷ(ˮ)Ⱥ: 624807593 941980833 882747577\n˽Ⱥ863062599\nȺ1029435374"}, {"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, {"ָ",R"(atָָﵥӦat.bot off -ָҪӲ.helpӦָ ȡϸϢ -ָ: +ָҪӲ.helpӦָ ȡϸϢ.help jrrp +ָ: .dismiss Ⱥ -.authorize Ȩ .bot -.welcome Ⱥӭ +.group Ⱥ +.authorize Ȩ +.send ̨Ϣ)" +"\f" +R"([ڶҳ]ָ .rules ٲ .r .ob Թģʽ .set Ĭ -.name -.nn dz .coc COC .dnd DND .st Լ¼ -.pc ɫ +.pc ɫ¼ .rc 춨 .setcoc ü춨 .sc Ǽ춨 .en ɳ춨 .ri ȹ .init ȹб -.ww -.me ˳ƶ -.jrrp Ʒ -.send Ϣ -.group Ⱥ +.ww )" +"\f" +R"([ҳ]ָ +.nn dz .draw +.name +.jrrp Ʒ +.welcome Ⱥӭ +.me ˳ƶ Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)"}, {"deck","ָĬƶѣʹ.drawָƶʱʹôƶѡƶѲŻֱһźϴơ\n.deck set ƶ Ĭƶ\n.deck set 1-100 ָȵ\n.deck show 鿴ʣ࿨\n.deck reset ʣ࿨\n.deck new Զƶѣÿո|ָ޶\n.deck new е|޵|޵|޵|޵|޵\nshowȺڲҪȨ"}, {"Ⱥ","&dismiss"}, @@ -426,9 +434,15 @@ const std::map HelpDoc = { "rc/ra", "춨ָ.rc/ra []([ɹ])\nɫʱʡԳɹ\n.rc*5\t//ʹ+-*/˳ҪΪ˷>Ӽ>\n.rc \t//ͷѺͼѻᱻΪؼ\n.rc -10\t//ɹʱ1-1000\n.rcp ǹ\t//9\nĬԹжɹʧܵķ.setcoc" }, + { + "", + "Dice!ĿǰֻΪCOC7춨ṩ棬ָΪ.setcoc:{setcoc}" + }, { "setcoc", "ΪǰȺCOC棬.setcoc 1,ǰ0-5\n0 \n1ɹ\n5096 - 100ʧܣ50100ʧ\n1\n501ɹ501 - 5ɹ\n5096 - 100ʧܣ50100ʧ\n2\n1 - 5 <= ɹʴɹ\n10096 - 99 > ɹʴʧ\n3\n1 - 5ɹ\n96 - 100ʧ\n4\n1 - 5 <= ʮ֮һɹ\n50 >= 96 + ʮ֮һʧܣ50100ʧ\n5\n1 - 2 < ֮һɹ\n5096 - 100ʧܣ5099 - 100ʧ\n" + R"(򿪷߷ +ΣȺڼ춨ֻȺãڳԱԵ)" }, {"san check", "&sc"}, {"Ǽ춨", "&sc"}, diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index 4a655e86..e80dd14c 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "json.hpp" #include "EncodingConvert.h" @@ -75,7 +76,16 @@ int readJMap(const nlohmann::json& j, std::map& mapTmp) } return intCnt; } - +template +int readJson(const std::string& strJson, std::set& setTmp) { + try { + nlohmann::json j(nlohmann::json::parse(strJson)); + j.get_to(setTmp); + return j.size(); + } catch (...) { + return -1; + } +} template int readJson(const std::string& strJson, std::map& mapTmp) { diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index db7c24dc..b70b8ec1 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -252,9 +252,9 @@ class Chat else CQ::sendDiscussMsg(ID, msg); Sleep(500); } - set(""); if (isGroup)CQ::setGroupLeave(ID); else CQ::setDiscussLeave(ID); + set(""); } [[nodiscard]] bool isset(const string& key) const diff --git a/Dice/StrExtern.hpp b/Dice/StrExtern.hpp index 2c1fd75b..fe61566c 100644 --- a/Dice/StrExtern.hpp +++ b/Dice/StrExtern.hpp @@ -16,14 +16,14 @@ string toString(int num, unsigned short size = 0); template typename std::enable_if::value, string>::type -toString(F num, unsigned short scale = 0, bool align = false) +toString(F num, unsigned short scale = 2, bool align = false) { string strNum{ to_string(num) }; size_t dot(strNum.find('.') + scale + 1); if (align)return strNum.substr(0, dot); size_t last(strNum.find_last_not_of('0') + 1); - if (strNum[last - 1] == '.')last--; if (last > dot)last = dot; + if (strNum[last - 1] == '.')last--; return strNum.substr(0, last); } From 9a78367e639a898ec31d772caaade61f19b1078e Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 31 Jul 2020 22:25:15 +0800 Subject: [PATCH 009/171] Use GBK instead of GB18030 to circumvent wine bug --- Dice/EncodingConvert.cpp | 10 +++++----- Dice/StrExtern.hpp | 2 +- Dice/strExtern.cpp | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Dice/EncodingConvert.cpp b/Dice/EncodingConvert.cpp index 6636e967..09bed551 100644 --- a/Dice/EncodingConvert.cpp +++ b/Dice/EncodingConvert.cpp @@ -21,7 +21,7 @@ * program. If not, see . */ -#define CP_GB18030 (54936) +#define CP_GBK (936) #define WIN32_LEAN_AND_MEAN #include "EncodingConvert.h" #include @@ -33,9 +33,9 @@ // ʵGB18030 std::string GBKtoUTF8(const std::string& strGBK) { - const int UTF16len = MultiByteToWideChar(CP_GB18030, 0, strGBK.c_str(), -1, nullptr, 0); + const int UTF16len = MultiByteToWideChar(CP_GBK, 0, strGBK.c_str(), -1, nullptr, 0); auto* const strUTF16 = new wchar_t[UTF16len]; - MultiByteToWideChar(CP_GB18030, 0, strGBK.c_str(), -1, strUTF16, UTF16len); + MultiByteToWideChar(CP_GBK, 0, strGBK.c_str(), -1, strUTF16, UTF16len); const int UTF8len = WideCharToMultiByte(CP_UTF8, 0, strUTF16, -1, nullptr, 0, nullptr, nullptr); auto* const strUTF8 = new char[UTF8len]; WideCharToMultiByte(CP_UTF8, 0, strUTF16, -1, strUTF8, UTF8len, nullptr, nullptr); @@ -58,9 +58,9 @@ std::string UTF8toGBK(const std::string& strUTF8) const int UTF16len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, nullptr, 0); auto* const strUTF16 = new wchar_t[UTF16len]; MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, strUTF16, UTF16len); - const int GBKlen = WideCharToMultiByte(CP_GB18030, 0, strUTF16, -1, nullptr, 0, nullptr, nullptr); + const int GBKlen = WideCharToMultiByte(CP_GBK, 0, strUTF16, -1, nullptr, 0, nullptr, nullptr); auto* const strGBK = new char[GBKlen]; - WideCharToMultiByte(CP_GB18030, 0, strUTF16, -1, strGBK, GBKlen, nullptr, nullptr); + WideCharToMultiByte(CP_GBK, 0, strUTF16, -1, strGBK, GBKlen, nullptr, nullptr); std::string strOutGBK(strGBK); delete[] strUTF16; delete[] strGBK; diff --git a/Dice/StrExtern.hpp b/Dice/StrExtern.hpp index fe61566c..115bab34 100644 --- a/Dice/StrExtern.hpp +++ b/Dice/StrExtern.hpp @@ -10,7 +10,7 @@ using std::string; using std::wstring; using std::to_string; -#define CP_GB18030 (54936) +#define CP_GBK (936) string toString(int num, unsigned short size = 0); diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index 3b8b857f..40d3224d 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -10,7 +10,7 @@ using std::wstring; using std::string_view; using std::to_string; -#define CP_GB18030 (54936) +#define CP_GBK (936) string toString(int num, unsigned short size) { @@ -33,9 +33,9 @@ int count_char(const string& s, char ch) string convert_w2a(const wchar_t* wch) { - const int len = WideCharToMultiByte(CP_GB18030, 0, wch, -1, nullptr, 0, nullptr, nullptr); + const int len = WideCharToMultiByte(CP_GBK, 0, wch, -1, nullptr, 0, nullptr, nullptr); char* m_char = new char[len]; - WideCharToMultiByte(CP_GB18030, 0, wch, -1, m_char, len, nullptr, nullptr); + WideCharToMultiByte(CP_GBK, 0, wch, -1, m_char, len, nullptr, nullptr); std::string str(m_char); delete[] m_char; return str; @@ -43,9 +43,9 @@ string convert_w2a(const wchar_t* wch) wstring convert_a2w(const char* ch) { - const int len = MultiByteToWideChar(CP_GB18030, 0, ch, -1, nullptr, 0); + const int len = MultiByteToWideChar(CP_GBK, 0, ch, -1, nullptr, 0); wchar_t* m_char = new wchar_t[len]; - MultiByteToWideChar(CP_GB18030, 0, ch, -1, m_char, len); + MultiByteToWideChar(CP_GBK, 0, ch, -1, m_char, len); std::wstring wstr(m_char); delete[] m_char; return wstr; From beb89adec150ad388cca217e1d91c4cc0b0cfda0 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 1 Aug 2020 15:49:29 +0800 Subject: [PATCH 010/171] fix 2.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复helpdoc扩展牌堆bug 优化一些通知类型 mod新增Dice版本检测 新增ResList分页排版 --- Dice/BlackListManager.cpp | 12 +++++++----- Dice/DiceCloud.cpp | 9 --------- Dice/DiceCloud.h | 1 - Dice/DiceConsole.cpp | 16 ++++++++-------- Dice/DiceJob.cpp | 14 +++++++------- Dice/DiceMod.cpp | 6 ++++++ Dice/EncodingConvert.cpp | 2 +- Dice/GlobalVar.cpp | 5 ++--- Dice/MsgFormat.h | 25 +++++++++++++++---------- 9 files changed, 46 insertions(+), 44 deletions(-) diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 519291fa..e14063cb 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -246,7 +246,7 @@ DDBlackMark::DDBlackMark(const string& strWarning) } catch (...) { - console.log("jsonʧܣ", 0b10, printSTNow()); + console.log("jsonʧܣ", 0, printSTNow()); return; } } @@ -385,8 +385,8 @@ void DDBlackMark::check_remit() { if (fromGroup.first && groupset(fromGroup.first, "") > 0)erase(); if (fromQQ.first && trustedQQ(fromQQ.first) > 1)erase(); - if (inviterQQ.first && trustedQQ(inviterQQ.first) > 1)inviterQQ.second = 0; - if (ownerQQ.first && trustedQQ(ownerQQ.first) > 1)ownerQQ.second = 0; + if (inviterQQ.first && trustedQQ(inviterQQ.first) > 1)inviterQQ = { 0,false }; + if (ownerQQ.first && trustedQQ(ownerQQ.first) > 1)ownerQQ = { 0,false }; } void DDBlackMark::erase() @@ -401,8 +401,10 @@ void DDBlackMark::upload() "&DiceMaid=" + std::to_string(DiceMaid) + "&masterQQ=" + std::to_string(masterQQ) + "&type=" + type + "&time=" + time + "¬e=" + UrlEncode(GBKtoUTF8(note)); string temp; - Network::POST("shiki.stringempty.xyz", "/DiceCloud/warning_upload.php", 80, frmdata.data(), temp); - if (isdigit(static_cast(temp[0])))wid = stoi(temp); + if (!Network::POST("shiki.stringempty.xyz", "/DiceCloud/warning_upload.php", 80, frmdata.data(), temp)) { + console.log("ϴ¼ʧ:" + temp, 0b1000); + } + else if (isdigit(static_cast(temp[0])))wid = stoi(temp); else if (temp == "denied")erase(); } diff --git a/Dice/DiceCloud.cpp b/Dice/DiceCloud.cpp index df47c062..d7654ab4 100644 --- a/Dice/DiceCloud.cpp +++ b/Dice/DiceCloud.cpp @@ -37,15 +37,6 @@ namespace Cloud delete[] frmdata; } - void upWarning(const char* warning) - { - char* frmdata = new char[strlen(warning) + 1]; - strcpy_s(frmdata, strlen(warning) + 1, warning); - string temp; - Network::POST("shiki.stringempty.xyz", "/DiceCloud/warning_upload.php", 80, frmdata, temp); - delete[] frmdata; - } - int checkWarning(const char* warning) { char* frmdata = new char[strlen(warning) + 1]; diff --git a/Dice/DiceCloud.h b/Dice/DiceCloud.h index 40d1fa3f..75f0e877 100644 --- a/Dice/DiceCloud.h +++ b/Dice/DiceCloud.h @@ -9,7 +9,6 @@ class FromMsg; namespace Cloud { void update(); - void upWarning(const char* warning); int checkWarning(const char* warning); int DownloadFile(const char* url, const char* downloadPath); int checkUpdate(FromMsg* msg); diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index e813ffed..519a8361 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -44,8 +44,7 @@ const std::mapConsole::intDefault{ {"ListenFriendRequest",1},{"ListenFriendAdd",1},{"AllowStranger",1}, {"AutoClearBlack",1},{"LeaveBlackQQ",0}, {"ListenGroupKick",1},{"ListenGroupBan",1},{"ListenSpam",1}, -{"BannedLeave",0},{"BannedBanInviter",0}, -{"KickedBanInviter",0}, +{"BannedBanInviter",0},{"KickedBanInviter",0}, {"GroupClearLimit",20}, {"CloudBlackShare",1},{"BelieveDiceList",0},{"CloudVisible",1}, {"SystemAlarmCPU",90},{"SystemAlarmRAM",90},{"SystemAlarmDisk",90}, @@ -537,36 +536,37 @@ EVE_Menu(eventGlobalSwitch) EVE_Request_AddFriend(eventAddFriend) { if (!console["ListenFriendRequest"])return 0; - string strMsg = "ԣ" + printQQ(fromQQ); + string strMsg = " " + printQQ(fromQQ)+ ":"; + if (msg && msg[0] != '\0') strMsg += msg; this_thread::sleep_for(3s); if (blacklist->get_qq_danger(fromQQ)) { - strMsg += "ѾܾûںУ"; + strMsg += "\nѾܾûںУ"; setFriendAddRequest(responseFlag, 2, ""); console.log(strMsg, 0b10, printSTNow()); } else if (trustedQQ(fromQQ)) { - strMsg += "ͬ⣨û"; + strMsg += "\nͬ⣨û"; setFriendAddRequest(responseFlag, 1, ""); AddMsgToQueue(getMsg("strAddFriendWhiteQQ"), fromQQ); console.log(strMsg, 1, printSTNow()); } else if (console["AllowStranger"] < 2 && !UserList.count(fromQQ)) { - strMsg += "Ѿܾû¼"; + strMsg += "\nѾܾû¼"; setFriendAddRequest(responseFlag, 2, ""); console.log(strMsg, 1, printSTNow()); } else if (console["AllowStranger"] < 1) { - strMsg += "Ѿܾû"; + strMsg += "\nѾܾû"; setFriendAddRequest(responseFlag, 2, ""); console.log(strMsg, 1, printSTNow()); } else { - strMsg += "ͬ"; + strMsg += "\nͬ"; setFriendAddRequest(responseFlag, 1, ""); AddMsgToQueue(getMsg("strAddFriend"), fromQQ); console.log(strMsg, 1, printSTNow()); diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 92aae783..bd58b2da 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -301,13 +301,13 @@ void dice_update(DiceJob& job) { string strAppPath(*path); strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; delete path; - string strURL("http://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); + string strURL("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { case -1: job.echo("ʧ:" + strURL); break; case -2: - job.echo("ʧ!ļδҵ:" + strAppPath); + job.note("Diceʧ!ļδҵ:" + strAppPath, 0b10); break; case 0: job.note("Dice!" + job.strVar["ver"] + "ɹ\n.system reload Ӧø", 1); @@ -316,17 +316,17 @@ void dice_update(DiceJob& job) { //ȡƲ¼ void dice_cloudblack(DiceJob& job) { - job.note("ʼȡƶ˼¼", 1); - string strURL("http://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); + job.echo("ʼȡƶ˼¼"); + string strURL("https://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); switch (Cloud::DownloadFile(strURL.c_str(), "DiceData/conf/CloudBlackList.json")) { case -1: - job.echo("Ʋ¼ͬʧ:" + strURL); + job.echo("ͬƲ¼ͬʧ:" + strURL); break; case -2: - job.echo("Ʋ¼ͬʧ!ļδҵ"); + job.echo("ͬƲ¼ͬʧ!ļδҵ"); break; case 0: - job.note(getMsg("self") + "Ʋ¼ͬɹʼȡ", 1); + job.note("ͬƲ¼ɹ" + getMsg("self") + "ʼȡ", 1); blacklist->loadJson("DiceData/conf/CloudBlackList.json", true); } } diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 30c2624d..72e2f24c 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -84,6 +84,12 @@ int DiceModManager::load(string& strLog) sFileErr.push_back(filename.filename().string()); continue; } + if (j.count("dice_build")) { + if (j["dice_build"] > Dice_Build) { + sFileErr.push_back(filename.filename().string()+"(Dice汾)"); + continue; + } + } if (j.count("helpdoc")) { cntItem += readJMap(j["helpdoc"], helpdoc); diff --git a/Dice/EncodingConvert.cpp b/Dice/EncodingConvert.cpp index 09bed551..7d3d6d8f 100644 --- a/Dice/EncodingConvert.cpp +++ b/Dice/EncodingConvert.cpp @@ -30,7 +30,7 @@ #include #include -// ʵGB18030 +// GBK std::string GBKtoUTF8(const std::string& strGBK) { const int UTF16len = MultiByteToWideChar(CP_GBK, 0, strGBK.c_str(), -1, nullptr, 0); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index cd6d21a6..bee1983c 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -96,7 +96,7 @@ std::map GlobalMsg {"strPcTempInvalid","{self}޷ʶĽɫģ"}, {"strPcNameEmpty","ƲΪա"}, {"strPcNameExist","{nick}Ѵͬ"}, - {"strPcNameNotExist","{nick}޸ƽɫ"}, + {"strPcNameNotExist","{nick}޸ƽɫ"}, {"strPcNameInvalid","ǷĽɫðţ"}, {"strPcInitDelErr","{nick}ijʼɾ"}, {"strPcNoteTooLong","עȲܳ255"}, @@ -466,7 +466,6 @@ const std::map HelpDoc = { "ƣ.draw [ƶ] ([])\t//鵽ƲŻأܳƶ\nǰƶѣӲ/Ա/\nԱְҵ/Ա/Ӣ츳/ú//˼/Ҫ֮/Ҫ֮/Ƿ֮/֮/Աص/ʱ֢״/֢ܽ״/־֢״/֢״/\nӪ/޻ɫ/ðյ/\nżʾ/ż/żƬ/ż/\nAMGC/AMGC/AMGCר/AMGC/AMGCװ/AMGC/AMGCؼ1/AMGCؼ2/AMGCؼ3\n///ռ//ʥ/Ҫ/Сʮ/â/ʮ\n.helpλ+򣩿ɻȡƽ\nչƶ:{չƶ}" }, {"ȹ", "&ri"}, - {"չƶ", ""}, {"ri", "ȹȺ޶.ri([ֵ])([dz])\n.ri -1 ijpc\t//Զȹб\n.ri +5 boss"}, {"ȹб", "&init"}, {"init", "ȹб\n.init\t//鿴ȹб\n.init clr\t//ȹб"}, @@ -504,7 +503,7 @@ const std::map HelpDoc = { Ⱥܴ:ָͣ/ûظ/jrrp/draw/me/help/ob/Ϣ/ʹ//)" }, {"", "ǧ֮ĸó֮˫׶Եļ໤ˣһйصĿߣףʱ̨ߣת֮"}, - {"Ͷʳ", "ͶʳShikiѡhttp://shiki.stringempty.xyz/alipay.png\nͶʳ䧣ѡhttps://afdian.net/@suhuiw4123"}, + {"Ͷʳ", "ͶʳShikiѡhttps://afdian.net/@dice_shiki\nͶʳ䧣ѡhttps://afdian.net/@suhuiw4123\nͶʳ{self}ѡ񡭡䳬᣿"}, {"λ", "ȻĵطĿĵǰСϲսĴˡõ롣"}, { "λ", diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index 8b889ba5..3c25b428 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -92,6 +92,7 @@ class ResList unsigned int intMaxLen = 0; bool isLineBreak = false; unsigned int intLineLen = 16; + unsigned int intPageLen = 512; string sDot = " "; string strLongSepa = "\n"; public: @@ -114,22 +115,26 @@ class ResList std::string show() { - std::string s; + std::string s, strHead, strSepa; + unsigned int lenPage(0),cntPage(1); if (intMaxLen > intLineLen || isLineBreak) { - for (auto it = vRes.begin(); it != vRes.end(); it++) - { - if (it == vRes.begin())s = "\n" + *it; - else s += strLongSepa + *it; - } + strHead = "\n"; + strSepa = strLongSepa; } else { - for (auto it = vRes.begin(); it != vRes.end(); it++) - { - if (it == vRes.begin())s = *it; - else s += sDot + *it; + strSepa = sDot; + } + for (auto it = vRes.begin(); it != vRes.end(); it++) { + //޺ҳ + if (lenPage > intPageLen) { + s += "\f[" + std::to_string(++cntPage) + "ҳ]\n" + *it; + lenPage = 0; } + else if (it == vRes.begin())s = strHead + *it; + else s += strSepa + *it; + lenPage += it->length(); } return s; } From 766bed10fcdc984056a11024213938625e00c435 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 3 Aug 2020 22:30:28 +0800 Subject: [PATCH 011/171] update for Mirai MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为Mirai适配更新和重载指令 修复部分地方DiceDir的错误 --- Dice/Dice.cpp | 7 ++- Dice/Dice.vcxproj | 2 +- Dice/DiceConsole.h | 4 +- Dice/DiceEvent.cpp | 19 ++++--- Dice/DiceJob.cpp | 128 ++++++++++++++++++++++++++++++++---------- Dice/DiceJob.h | 1 + Dice/DiceSchedule.cpp | 5 +- Dice/ManagerSystem.h | 3 +- Dice/RDConstant.h | 1 - 9 files changed, 126 insertions(+), 44 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 0a32733d..9ae7a09c 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -177,7 +177,12 @@ EVE_Enable(eventEnable) { Mirai = true; Dice_Full_Ver_For = Dice_Full_Ver + " For Mirai]"; - DiceDir = "Dice" + to_string(getLoginQQ()); + DiceDir = "Dice" + to_string(getLoginQQ()); + filesystem::path pathDir(DiceDir); + if (!exists(pathDir)) { + filesystem::path pathDirOld("DiceData"); + if (exists(pathDirOld))rename(pathDirOld, pathDir); + } this_thread::sleep_for(3s); //ȷMirai첽Ϣִ } console.setPath(DiceDir + "\\conf\\Console.xml"); diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index f4121b6b..e2c843bd 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -24,7 +24,7 @@ Win32Proj Dice 10.0 - app + com.w4123.dice diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index a34aaa5c..2378ff57 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -22,6 +22,8 @@ using namespace std::literals::chrono_literals; using std::string; using std::to_string; +extern string DiceDir; + enum class ClockEvent { off, on, save, clear }; class Console @@ -122,7 +124,7 @@ class Console } void save() { - mkDir("DiceData\\conf"); + mkDir(DiceDir + "/conf"); DDOM xml("console",""); xml.push(DDOM("mode", to_string(isMasterMode))); xml.push(DDOM("master", to_string(masterQQ))); diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 8fa8a8be..8af7b197 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1182,7 +1182,7 @@ int FromMsg::DiceReply() { if (Mirai) { - reply("Miraiִ֧˹"); + reply("MiraiҪ˹"); return -1; } if (trusted < 5) @@ -1196,17 +1196,21 @@ int FromMsg::DiceReply() } else if (strOption == "reload") { - if (Mirai) + if (trusted < 5 && fromQQ != console.master()) { - reply("Miraiִ֧˹"); + reply(GlobalMsg["strNotMaster"]); return -1; } - if (trusted < 5 && fromQQ != console.master()) - { + cmd_key = Mirai ? "reload" : "remake"; + sch.push_job(*this); + return 1; + } + else if (strOption == "remake") { + if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; } - cmd_key = "reload"; + cmd_key = "remake"; sch.push_job(*this); return 1; } @@ -1223,13 +1227,14 @@ int FromMsg::DiceReply() } if (strOption == "rexplorer") { - if (trusted < 5) + if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; } system(R"(taskkill /f /fi "username eq %username%" /im explorer.exe)"); system(R"(start %SystemRoot%\explorer.exe)"); + this_thread::sleep_for(3s); note("Դ\nǰڴռã" + to_string(getRamPort()) + "%"); } else if (strOption == "cmd") diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index bd58b2da..e70eb866 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -4,6 +4,7 @@ #include #include #include "StrExtern.hpp" +#include "CQAPI.h" #include "ManagerSystem.h" #include "DiceCloud.h" #include "BlackListManager.h" @@ -47,13 +48,17 @@ void cq_exit(DiceJob& job) { dataBackUp(); system(strCMD.c_str()); } + +inline PROCESSENTRY32 getProcess(int pid) { + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(pe32); + HANDLE hParentProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + Process32First(hParentProcess, &pe32); + return pe32; +} void cq_restart(DiceJob& job) { - char** path = new char* (); - _get_pgmptr(path); - string strSelfPath(*path); - delete path; + //string strSelfPath; string strSelfName; - int pid = _getpid(); PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); @@ -62,19 +67,43 @@ void cq_restart(DiceJob& job) { } BOOL bResult = Process32First(hProcessSnap, &pe32); int ppid(0); - while (bResult) { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - job.echo("ȷϽ" + strSelfPath + "\nid:" + to_string(pe32.th32ProcessID) + "\nid:" + to_string(pe32.th32ParentProcessID)); - strSelfName = pe32.szExeFile; - break; + if (Mirai) { + char buffer[MAX_PATH]; + const DWORD length = GetModuleFileNameA(nullptr, buffer, sizeof buffer); + std::string pathSelf(buffer, length); + pathSelf = pathSelf.substr(0, pathSelf.find("jre\\bin\\java.exe")) + "MiraiOK.exe"; + char pathFull[MAX_PATH]; + while (bResult) { + if (strcmp(pe32.szExeFile, "MiraiOK.exe") == 0) { + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); + GetModuleFileNameEx(hProcess, NULL, pathFull, sizeof(pathFull)); + if (pathSelf != pathFull)continue; + ppid = pe32.th32ProcessID; + job.echo("ȷϽ" + pathSelf + "\nid:" + to_string(ppid)); + break; + } + bResult = Process32Next(hProcessSnap, &pe32); + } + } + else { + int pid = _getpid(); + while (bResult) { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + PROCESSENTRY32 pp32 = getProcess(ppid); + strSelfName = pp32.szExeFile; + job.echo("ȷϽ" + strSelfName + "\nid:" + to_string(pe32.th32ProcessID) + "\nid:" + to_string(ppid)); + break; + } + bResult = Process32Next(hProcessSnap, &pe32); } - bResult = Process32Next(hProcessSnap, &pe32); } if (!ppid) { job.note("ʧܣδҵ̣", 1); + return; } string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + to_string(console.DiceMaid); + if (Mirai) command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\MiraiOK.exe"; ofstream fout("reload.bat"); fout << command << std::endl; fout.close(); @@ -100,6 +129,17 @@ void cq_restart(DiceJob& job) { } } +void mirai_reload(DiceJob& job){ + using cq_reload_type = int(*)(int32_t); + HMODULE hModule = LoadLibraryA("CQP.dll"); + cq_reload_type cq_reload = (cq_reload_type)GetProcAddress(hModule, "CQ_reload"); + if (!cq_reload) { + job.note("MiraiNativeʧܡ\n汾ɣ", 0b10); + return; + } + cq_reload(getAuthCode()); +} + void auto_save(DiceJob& job) { if (sch.is_job_cold("autosave"))return; dataBackUp(); @@ -296,21 +336,51 @@ void cloud_beat(DiceJob& job) { void dice_update(DiceJob& job) { job.note("ʼDice\n汾:" + job.strVar["ver"], 1); - char** path = new char* (); - _get_pgmptr(path); - string strAppPath(*path); - strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; - delete path; - string strURL("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); - switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { - case -1: - job.echo("ʧ:" + strURL); - break; - case -2: - job.note("Diceʧ!ļδҵ:" + strAppPath, 0b10); - break; - case 0: - job.note("Dice!" + job.strVar["ver"] + "ɹ\n.system reload Ӧø", 1); + if (Mirai) { + mkDir("plugins/MiraiNative/pluginsnew"); + char pathDll[] = "plugins/MiraiNative/pluginsnew/com.w4123.dice.dll"; + char pathJson[] = "plugins/MiraiNative/pluginsnew/com.w4123.dice.json"; + string urlDll("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "/com.w4123.dice.dll?" + to_string(job.fromTime)); + string urlJson("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "/com.w4123.dice.json?" + to_string(job.fromTime)); + switch (Cloud::DownloadFile(urlDll.c_str(), pathDll)) { + case -1: + job.echo("ʧ:" + urlDll); + break; + case -2: + job.note("Diceʧ!dllļδصָλ", 0b1); + break; + case 0: + default: + switch (Cloud::DownloadFile(urlJson.c_str(), pathJson)) { + case -1: + job.echo("ʧ:" + urlJson); + break; + case -2: + job.note("Diceʧ!jsonļδصָλ", 0b1); + break; + case 0: + default: + job.note("Dice!" + job.strVar["ver"] + "ɹ", 1); + } + } + } + else { + char** path = new char* (); + _get_pgmptr(path); + string strAppPath(*path); + delete path; + strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; + string strURL("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); + switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { + case -1: + job.echo("ʧ:" + strURL); + break; + case -2: + job.note("Diceʧ!ļδҵ:" + strAppPath, 0b10); + break; + case 0: + job.note("Dice!" + job.strVar["ver"] + "ɹ\n.system reload Ӧø", 1); + } } } @@ -318,7 +388,7 @@ void dice_update(DiceJob& job) { void dice_cloudblack(DiceJob& job) { job.echo("ʼȡƶ˼¼"); string strURL("https://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); - switch (Cloud::DownloadFile(strURL.c_str(), "DiceData/conf/CloudBlackList.json")) { + switch (Cloud::DownloadFile(strURL.c_str(), (DiceDir + "/conf/CloudBlackList.json").c_str())) { case -1: job.echo("ͬƲ¼ͬʧ:" + strURL); break; @@ -327,7 +397,7 @@ void dice_cloudblack(DiceJob& job) { break; case 0: job.note("ͬƲ¼ɹ" + getMsg("self") + "ʼȡ", 1); - blacklist->loadJson("DiceData/conf/CloudBlackList.json", true); + blacklist->loadJson(DiceDir + "/conf/CloudBlackList.json", true); } } string print_master() { diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index ea2f5198..5bcb9545 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -7,6 +7,7 @@ int sendSelf(const string msg); void cq_exit(DiceJob& job); void cq_restart(DiceJob& job); +void mirai_reload(DiceJob& job); void auto_save(DiceJob& job); void check_system(DiceJob& job); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 6d50aa09..1755f7bb 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -11,7 +11,8 @@ unordered_map mCommand = { {"syscheck",check_system}, {"autosave",auto_save}, {"clrimage",clear_image}, - {"reload",cq_restart}, + {"reload",mirai_reload}, + {"remake",cq_restart}, {"die",cq_exit}, {"heartbeat",cloud_beat}, {"update",dice_update}, @@ -39,7 +40,7 @@ void DiceJob::echo(const std::string& msg) { } } void DiceJob::note(const std::string& strMsg, int note_lv = 0b1) { - ofstream fout(string("DiceData\\audit\\log") + to_string(console.DiceMaid) + "_" + printDate() + ".txt", ios::out | ios::app); + ofstream fout(DiceDir + "/audit/log" + to_string(console.DiceMaid) + "_" + printDate() + ".txt", ios::out | ios::app); fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); echo(strMsg); diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index b70b8ec1..e912f1b0 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -21,8 +21,7 @@ using std::vector; using std::unordered_map; constexpr auto CQ_IMAGE = "[CQ:image,file="; constexpr auto CQ_AT = "[CQ:at,qq="; -constexpr time_t NEWYEAR = 1588262400; -extern string DiceDir; +constexpr time_t NEWYEAR = 1593532800; // void loadData(); diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index 9ec38531..98092d86 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -142,7 +142,6 @@ static std::map mVariableCOC7 = { {"ȡ", "&Ȼ"}, }; static std::map ExpressionCOC7 = { - {"", "D100"}, {"ҽѧظ", "1D3"}, {"ظ", "1D3"}, {"ͽ˺", "1D3+[DB]"}, From 9add9e55de7c174fb2cc6333d23d1072dccceef2 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 4 Aug 2020 00:28:19 +0800 Subject: [PATCH 012/171] =?UTF-8?q?=E5=AF=B9Mirai=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dice/Dice.cpp | 11 +++++++++++ Dice/DiceJob.cpp | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 9ae7a09c..5f37600b 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -930,7 +930,18 @@ EVE_Exit(eventExit) { if (!Enabled) return 0; + Enabled = false; + threads = {}; dataBackUp(); + sch.end(); + fmt.reset(); + gm.reset(); + PList.clear(); + ChatList.clear(); + UserList.clear(); + console.reset(); + EditedMsg.clear(); + blacklist.reset(); return 0; } diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index e70eb866..04371ed6 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -130,7 +130,7 @@ void cq_restart(DiceJob& job) { } void mirai_reload(DiceJob& job){ - using cq_reload_type = int(*)(int32_t); + using cq_reload_type = int(__stdcall*)(int32_t); HMODULE hModule = LoadLibraryA("CQP.dll"); cq_reload_type cq_reload = (cq_reload_type)GetProcAddress(hModule, "CQ_reload"); if (!cq_reload) { @@ -138,6 +138,7 @@ void mirai_reload(DiceJob& job){ return; } cq_reload(getAuthCode()); + FreeLibrary(hModule); } void auto_save(DiceJob& job) { From 46cf73aa479a81cc19d0468da0e9e1fe79b6f3a9 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Tue, 4 Aug 2020 08:16:03 +0800 Subject: [PATCH 013/171] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E9=98=BB=E5=A1=9E=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dice/DiceSchedule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 1755f7bb..e389de6b 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -72,7 +72,7 @@ void jobHandle() { cvJobWaited.notify_one(); } else{ - cvJob.wait(lock_queue, []() {return !queueJob.empty(); }); + cvJob.wait_for(lock_queue, 2s, []() {return !queueJob.empty(); }); } } } From 2cd682a312ef31d6b78159cfc06f92f7e3226ad1 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Tue, 4 Aug 2020 10:01:53 +0800 Subject: [PATCH 014/171] update thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 调整线程工作模式 --- Dice/Dice.cpp | 22 ++++++++-------------- Dice/DiceConsole.h | 15 ++++++++++----- Dice/DiceSchedule.cpp | 8 ++------ 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 5f37600b..e33a1b37 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -909,9 +909,9 @@ EVE_Menu(eventGUI) return GUIMain(); } -EVE_Disable(eventDisable) -{ +void global_exit() { Enabled = false; + threads.exit(); threads = {}; dataBackUp(); sch.end(); @@ -923,6 +923,11 @@ EVE_Disable(eventDisable) console.reset(); EditedMsg.clear(); blacklist.reset(); +} + +EVE_Disable(eventDisable) +{ + global_exit(); return 0; } @@ -930,18 +935,7 @@ EVE_Exit(eventExit) { if (!Enabled) return 0; - Enabled = false; - threads = {}; - dataBackUp(); - sch.end(); - fmt.reset(); - gm.reset(); - PList.clear(); - ChatList.clear(); - UserList.clear(); - console.reset(); - EditedMsg.clear(); - blacklist.reset(); + global_exit(); return 0; } diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index 2378ff57..f79fe284 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "STLExtern.hpp" #include "DiceXMLTree.h" #include "DiceFile.hpp" @@ -204,20 +205,24 @@ void ConsoleTimer(); class ThreadFactory { public: - ThreadFactory() - { - } + ThreadFactory() {} int rear = 0; - std::thread vTh[4]; + std::array vTh; void operator()(void (*func)()) { std::thread th(func); - th.detach(); vTh[rear] = std::move(th); rear++; } + void exit() { + for (auto& th : vTh) { + th.join(); + } + vTh = {}; + rear = 0; + } }; extern ThreadFactory threads; diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index e389de6b..b3a65398 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -128,7 +128,6 @@ void DiceScheduler::add_job_until(time_t cloc, const char* job_name) { std::unique_lock lock_queue(mtJobWaited); queueJobWaited.emplace(cloc, job_name); } -std::unique_ptr threadJobs; bool DiceScheduler::is_job_cold(const char* cmd) { return untilJobs[cmd] > time(NULL); @@ -138,10 +137,8 @@ void DiceScheduler::refresh_cold(const char* cmd, time_t until) { } void DiceScheduler::start() { - threadJobs = std::make_unique(jobHandle); - threadJobs->detach(); - std::thread thWaited(jobWait); - thWaited.detach(); + threads(jobHandle); + threads(jobWait); push_job("heartbeat"); push_job("syscheck"); if (console["AutoSaveInterval"] > 0)add_job_for(console["AutoSaveInterval"] * 60, "autosave"); @@ -149,7 +146,6 @@ void DiceScheduler::start() { else add_job_for(60 * 60, "clrimage"); } void DiceScheduler::end() { - threadJobs.reset(); } void DiceToday::daily_clear() { From 68344936e34dcfd27e43130d07ad7f352b9fda1e Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 5 Aug 2020 21:35:15 +0800 Subject: [PATCH 015/171] fix DiceDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将mirai下文档目录设置为绝对路径 --- Dice/Dice.cpp | 28 ++++++++++++++++++---------- Dice/DiceConsole.h | 1 + Dice/DiceJob.cpp | 4 ++-- Dice/DiceSession.h | 1 + Dice/GlobalVar.cpp | 2 +- Dice/GlobalVar.h | 2 +- Dice/ManagerSystem.cpp | 1 + 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index e33a1b37..9c98c0f6 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -62,6 +62,12 @@ multimap mFwdList; ThreadFactory threads; string strFileLoc; +constexpr auto msgInit{ R"(ӭʹDice!ˣ +Ӳ˵Dice!->ۺϹĺ̨ +Masterģʽͨ󼴿ɳΪҵ~ +ɷ.help鿴 +οĵο.help)" }; + // void loadData() { @@ -177,11 +183,17 @@ EVE_Enable(eventEnable) { Mirai = true; Dice_Full_Ver_For = Dice_Full_Ver + " For Mirai]"; + char buffer[MAX_PATH]; + const DWORD length = GetModuleFileNameA(nullptr, buffer, sizeof buffer); + std::string pathSelf(buffer, length); + dirExe = pathSelf.substr(0, pathSelf.find("jre\\bin\\java.exe")); DiceDir = "Dice" + to_string(getLoginQQ()); - filesystem::path pathDir(DiceDir); + filesystem::path pathDir(dirExe + DiceDir); + DiceDir = dirExe + DiceDir; if (!exists(pathDir)) { - filesystem::path pathDirOld("DiceData"); + filesystem::path pathDirOld(dirExe + "DiceData"); if (exists(pathDirOld))rename(pathDirOld, pathDir); + else filesystem::create_directory(pathDir); } this_thread::sleep_for(3s); //ȷMirai첽Ϣִ } @@ -217,11 +229,7 @@ EVE_Enable(eventEnable) } else { - sendPrivateMsg(console.DiceMaid, - R"(ӭʹDice!ˣ -ҼQ->Ӧù->˵->MasterģʽлɿMasterģʽĺ̨ -ĵ: https://v2docs.kokona.tech -ļο.help)"); + sendPrivateMsg(console.DiceMaid, msgInit); } ifstreamMaster.close(); std::map boolConsole; @@ -446,11 +454,11 @@ EVE_Enable(eventEnable) threads(warningHandler); threads(frqHandler); sch.start(); + console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, + printSTNow()); // getDiceList(); getExceptGroup(); - console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, - printSTNow()); llStartTime = clock(); return 0; } @@ -463,7 +471,7 @@ bool eve_GroupAdd(Chat& grp) unique_lock lock_queue(GroupAddMutex); if (grp.isset("δ") || grp.isset(""))grp.reset("δ").reset(""); else if (time(nullptr) - grp.tCreated > 1)return false; - lock_queue.unlock(); + if (ChatList.size() == 1 && !console.isMasterMode)sendGroupMsg(grp.ID, msgInit); } GroupInfo ginf(grp.ID); grp.Name = ginf.strGroupName; diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index f79fe284..b7d7ea85 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -23,6 +23,7 @@ using namespace std::literals::chrono_literals; using std::string; using std::to_string; +extern string dirExe; extern string DiceDir; enum class ClockEvent { off, on, save, clear }; diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 04371ed6..17c253b7 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -134,7 +134,7 @@ void mirai_reload(DiceJob& job){ HMODULE hModule = LoadLibraryA("CQP.dll"); cq_reload_type cq_reload = (cq_reload_type)GetProcAddress(hModule, "CQ_reload"); if (!cq_reload) { - job.note("MiraiNativeʧܡ\n汾ɣ", 0b10); + job.note("MiraiNativeʧܡ\nʹ˹ɻCQP.dll\n뱣֤汾MiraiNativeɾCQP.dll", 0b10); return; } cq_reload(getAuthCode()); @@ -257,7 +257,7 @@ void clear_group(DiceJob& job) { for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; time_t tLast = grp.tLastMsg; - if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0)tLast = tLMT; + if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0 && tLMT > tLast)tLast = tLMT; if (!tLast)continue; int intDay = (int)(tNow - tLast) / 86400; if (intDay > intDayLim) { diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index 38539bdb..6bc1472d 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -55,6 +55,7 @@ class DiceSession DiceSession& update() { tUpdate = time(nullptr); + save(); return *this; } diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index bee1983c..9c3025c6 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -55,7 +55,7 @@ std::map GlobalMsg {"strUserTrustIllegal","ĿȨ޸Ϊ{trust}ǷǷġ"}, {"strUserNotFound","{self}{user}û¼"}, {"strGroupAuthorized","A roll to the table turns to a dice fumble!\nDice Roller {strSelfName}\nȺȨɣ뾡ʹñ\nЭʹãʹ.dismissͳ!" }, - {"strGroupLicenseDeny","Ⱥδ{self}ʹãԶȺھĬ\n.helpЭ ĶͬЭӪʹã\nԱʹ!dismissͳ{self}\nɰ¸ʽд:\n!authorize ;:[*д*] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[*дָ*]úʹ[*дָ*]ͳȺ" }, + {"strGroupLicenseDeny","Ⱥδ{self}ʹãԶȺھĬ\n.helpЭ ĶͬЭӪʹã\nԱʹ!dismissͳ{self}\nɰ¸ʽд:\n!authorize ;:[ **д** ] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[ **дָ** ]úʹ[ **дָ** ]ͳȺ" }, {"strGroupLicenseApply","ȺδͨȨ\nѷ͡" }, {"strGroupSetOn","ѿ{self}ڴȺġ{option}ѡ"}, //Ⱥڿغңؿͨôı {"strGroupSetOnAlready","{self}ڴȺ{option}"}, diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 0a17a232..4ce6f372 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -35,7 +35,7 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 564u; +const unsigned short Dice_Build = 565u; inline const std::string Dice_Ver_Without_Build = "2.4.0"; constexpr auto DiceRequestHeader = "Dice/2.4.0"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 94be615c..14398744 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -10,6 +10,7 @@ #include "CardDeck.h" #include "GlobalVar.h" +string dirExe; string DiceDir = "DiceData"; //õͼƬб unordered_set sReferencedImage; From 84b43be23f86dcad9dd938f3a2b93e24c4c89e11 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Thu, 6 Aug 2020 20:11:59 +0800 Subject: [PATCH 016/171] update .log (error) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新log功能 (aws依赖部分编译失败) --- Dice/Dice.cpp | 5 +-- Dice/Dice.vcxproj | 20 ++++++++- Dice/Dice.vcxproj.filters | 8 ++++ Dice/DiceConsole.cpp | 4 +- Dice/DiceEvent.cpp | 49 ++++++++++++++++++---- Dice/DiceEvent.h | 3 +- Dice/DiceJob.cpp | 24 ++++++++++- Dice/DiceJob.h | 2 + Dice/DiceSchedule.cpp | 11 ++++- Dice/DiceSchedule.h | 7 +++- Dice/DiceSession.cpp | 85 ++++++++++++++++++++++++++++++++++----- Dice/DiceSession.h | 27 +++++++++++-- Dice/GlobalVar.cpp | 25 +++++++++++- Dice/GlobalVar.h | 2 +- Dice/ManagerSystem.cpp | 7 ++++ Dice/RD.h | 6 +++ Dice/S3PutObject.cpp | 69 +++++++++++++++++++++++++++++++ Dice/S3PutObject.h | 14 +++++++ Dice/packages.config | 7 ++++ 19 files changed, 337 insertions(+), 38 deletions(-) create mode 100644 Dice/S3PutObject.cpp create mode 100644 Dice/S3PutObject.h create mode 100644 Dice/packages.config diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 9c98c0f6..3a532dd6 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -470,7 +470,7 @@ bool eve_GroupAdd(Chat& grp) { unique_lock lock_queue(GroupAddMutex); if (grp.isset("δ") || grp.isset(""))grp.reset("δ").reset(""); - else if (time(nullptr) - grp.tCreated > 1)return false; + else return false; if (ChatList.size() == 1 && !console.isMasterMode)sendGroupMsg(grp.ID, msgInit); } GroupInfo ginf(grp.ID); @@ -626,7 +626,6 @@ EVE_PrivateMsg_EX(eventPrivateMsg) if (!Enabled)return; FromMsg Msg(eve.message, eve.fromQQ); if (Msg.DiceFilter())eve.message_block(); - Msg.FwdMsg(eve.message); } EVE_GroupMsg_EX(eventGroupMsg) @@ -640,7 +639,6 @@ EVE_GroupMsg_EX(eventGroupMsg) { FromMsg Msg(eve.message, eve.fromGroup, msgtype::Group, eve.fromQQ); if (Msg.DiceFilter())eve.message_block(); - Msg.FwdMsg(eve.message); } if (grp.isset("Ϣ"))eve.message_block(); } @@ -666,7 +664,6 @@ EVE_DiscussMsg_EX(eventDiscussMsg) } FromMsg Msg(eve.message, eve.fromDiscuss, msgtype::Discuss, eve.fromQQ); if (Msg.DiceFilter() || grp.isset("Ϣ"))eve.message_block(); - Msg.FwdMsg(eve.message); } EVE_System_GroupMemberIncrease(eventGroupMemberIncrease) diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index e2c843bd..41a31d92 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -270,6 +270,7 @@ + @@ -323,12 +324,15 @@ + + + @@ -341,10 +345,24 @@ - + + + + + + + + + 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + + + + \ No newline at end of file diff --git a/Dice/Dice.vcxproj.filters b/Dice/Dice.vcxproj.filters index d3cd2b0c..ef399fb4 100644 --- a/Dice/Dice.vcxproj.filters +++ b/Dice/Dice.vcxproj.filters @@ -126,6 +126,9 @@ 源文件 + + 源文件 + @@ -284,11 +287,16 @@ 头文件 + + 头文件 + 资源文件 + + diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 519a8361..bf1192f2 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -555,13 +555,13 @@ EVE_Request_AddFriend(eventAddFriend) else if (console["AllowStranger"] < 2 && !UserList.count(fromQQ)) { strMsg += "\nѾܾû¼"; - setFriendAddRequest(responseFlag, 2, ""); + setFriendAddRequest(responseFlag, 2, getMsg("strFriendDenyNotUser").c_str()); console.log(strMsg, 1, printSTNow()); } else if (console["AllowStranger"] < 1) { strMsg += "\nѾܾû"; - setFriendAddRequest(responseFlag, 2, ""); + setFriendAddRequest(responseFlag, 2, getMsg("strFriendDenyNoTrust").c_str()); console.log(strMsg, 1, printSTNow()); } else diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 8af7b197..d7a5df84 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -15,19 +15,26 @@ using namespace std; using namespace CQ; -void FromMsg::FwdMsg(const string& message) +void FromMsg::fwdMsg() { - if (mFwdList.count(fromChat) && !isLinkOrder) + if (mFwdList.count(fromChat) && strLowerMessage.find(".link") != 0) { const auto range = mFwdList.equal_range(fromChat); string strFwd; if (trusted < 5)strFwd += printFrom(); - strFwd += message; + strFwd += strMsg; for (auto it = range.first; it != range.second; ++it) { AddMsgToQueue(strFwd, it->second.first, it->second.second); } } + if (LogList.count(fromGroup)) { + string msg = strMsg; + filter_CQcode(msg, fromGroup); + ofstream logout(gm->session(fromGroup).log_path(), ios::out | ios::app); + logout << printQQ(fromQQ) + " " + printTTime(fromTime) << endl + << msg << endl << endl; + } } int FromMsg::AdminEvent(const string& strOption) @@ -708,10 +715,11 @@ int FromMsg::DiceReply() strVar["nick"] = getName(fromQQ, fromGroup); strVar["pc"] = getPCName(fromQQ, fromGroup); strVar["at"] = intT ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; - isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions > 1; + isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions > 1 || pGrp->inviter == fromQQ; strLowerMessage = strMsg; std::transform(strLowerMessage.begin(), strLowerMessage.end(), strLowerMessage.begin(), - [](unsigned char c) { return tolower(c); }); + [](unsigned char c) { return tolower(c); }); + fwdMsg(); //ָƥ if (strLowerMessage.substr(intMsgCnt, 9) == "authorize") { @@ -773,7 +781,7 @@ int FromMsg::DiceReply() grp.leave(getMsg("strAdminDismiss", strVar)); reply(GlobalMsg["strGroupExit"]); } - else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1) + else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1 || (grp.inviter == fromQQ)) { reply(GlobalMsg["strDismiss"]); } @@ -2211,7 +2219,6 @@ int FromMsg::DiceReply() reply(GlobalMsg["strNotAdmin"]); return true; } - isLinkOrder = true; string strOption = readPara(); if (strOption == "close") { @@ -2548,6 +2555,32 @@ int FromMsg::DiceReply() reply(strReply); return 1; } + else if (strLowerMessage.substr(intMsgCnt, 3) == "log") { + if (!intT) { + reply(fmt->get_help("log")); + return 1; + } + intMsgCnt += 3; + string strPara = readPara(); + if (strPara.empty()) { + reply(fmt->get_help("log")); + } + else if (DiceSession& game = gm->session(fromGroup); strPara == "new") { + game.log_new(this); + }else if(strPara == "on") { + game.log_on(this); + } + else if (strPara == "off") { + game.log_off(this); + } + else if (strPara == "end") { + game.log_end(this); + } + else { + reply(fmt->get_help("log")); + } + return 1; +} else if (strLowerMessage.substr(intMsgCnt, 3) == "nnn") { intMsgCnt += 3; @@ -2852,7 +2885,7 @@ int FromMsg::DiceReply() { if (intT == PrivateT) { - reply(GlobalMsg["strObPrivate"]); + reply(fmt->get_help("ob")); return 1; } intMsgCnt += 2; diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index d4464ecf..a4c64f85 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -90,7 +90,7 @@ class FromMsg :public DiceJobDetail { } //תϢ - void FwdMsg(const string& message); + void fwdMsg(); int AdminEvent(const string& strOption); int MasterSet(); int DiceReply(); @@ -106,7 +106,6 @@ class FromMsg :public DiceJobDetail { bool isBotOff = false; bool isCalled = false; bool isAuth = false; - bool isLinkOrder = false; short getGroupAuth(long long group = 0) { diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 17c253b7..5be2a09a 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -10,6 +10,7 @@ #include "BlackListManager.h" #include "GlobalVar.h" #include "CardDeck.h" +#include "S3PutObject.h" #pragma warning(disable:28159) using namespace std; @@ -103,7 +104,7 @@ void cq_restart(DiceJob& job) { return; } string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + to_string(console.DiceMaid); - if (Mirai) command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\MiraiOK.exe"; + if (Mirai) command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart " + dirExe + "MiraiOK.exe"; ofstream fout("reload.bat"); fout << command << std::endl; fout.close(); @@ -401,7 +402,26 @@ void dice_cloudblack(DiceJob& job) { blacklist->loadJson(DiceDir + "/conf/CloudBlackList.json", true); } } + +void log_put(DiceJob& job) { + job["ret"] = put_s3_object("dicelogger", + job.strVar["log_file"], + job.strVar["log_path"], + "ap-southeast-1"); + if (job["ret"] == "SUCCESS") { + job.echo(getMsg("strLogUpSuccess", job.strVar)); + return; + } + else if (++job.cntExec > 5) { + job.echo(getMsg("strLogUpFailure",job.strVar)); + } + else { + sch.add_job_for(2 * 60, job); + } +} + string print_master() { + if (!console.master())return ""; return printQQ(console.master()); } @@ -410,4 +430,4 @@ string list_deck() { } string list_extern_deck() { return listKey(CardDeck::mExternPublicDeck); -} \ No newline at end of file +} diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 5bcb9545..40905c10 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -20,6 +20,8 @@ void cloud_beat(DiceJob& job); void dice_update(DiceJob& job); void dice_cloudblack(DiceJob& job); +void log_put(DiceJob& job); + string print_master(); string list_deck(); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index b3a65398..b0ef9a83 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -16,7 +16,8 @@ unordered_map mCommand = { {"die",cq_exit}, {"heartbeat",cloud_beat}, {"update",dice_update}, - {"cloudblack",dice_cloudblack} + {"cloudblack",dice_cloudblack}, + {"uplog",log_put} }; @@ -176,4 +177,12 @@ void DiceToday::load() { } if (jFile.count("global")) { jFile["global"].get_to(cntGlobal); } if (jFile.count("user_cnt")) { jFile["user_cnt"].get_to(cntUser); } +} + +string printTTime(time_t tt) { + char timestamp[20]; + tm t{}; + if (!tt || localtime_s(&t, &tt))return "1970-00-00 00:00:00"; + sprintf_s(timestamp, "%04d-%02d-%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); + return timestamp; } \ No newline at end of file diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index 4d6132b1..cb571c6b 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -17,7 +17,7 @@ struct DiceJobDetail { chatType fromChat; string cmd_key; string strMsg; - time_t fromTime = time(NULL); + time_t fromTime = time(nullptr); //ʱ map strVar = {}; DiceJobDetail(const char* cmd, bool isFromSelf = false):cmd_key(cmd){ @@ -39,6 +39,7 @@ class DiceJob : public DiceJobDetail { public: DiceJob(DiceJobDetail detail) :DiceJobDetail(detail) {} Renum ren = Renum::NIL; + size_t cntExec{ 0 }; void exec(); void echo(const std::string&); void note(const std::string&, int); @@ -82,4 +83,6 @@ class DiceToday { size_t cnt(const string& key = "") { return cntUser.size(); } void daily_clear(); }; -inline std::unique_ptr today; \ No newline at end of file +inline std::unique_ptr today; + +string printTTime(time_t tt); \ No newline at end of file diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index 2e9c7ea5..05227646 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -35,7 +35,7 @@ bool DiceSession::table_clr(string key) return false; } -int DiceSession::ob_enter(FromMsg* msg) +void DiceSession::ob_enter(FromMsg* msg) { if (sOB.count(msg->fromQQ)) { @@ -47,10 +47,9 @@ int DiceSession::ob_enter(FromMsg* msg) msg->reply(GlobalMsg["strObEnter"]); } update(); - return 0; } -int DiceSession::ob_exit(FromMsg* msg) +void DiceSession::ob_exit(FromMsg* msg) { if (sOB.count(msg->fromQQ)) { @@ -62,10 +61,9 @@ int DiceSession::ob_exit(FromMsg* msg) msg->reply(GlobalMsg["strObExitAlready"]); } update(); - return 0; } -int DiceSession::ob_list(FromMsg* msg) const +void DiceSession::ob_list(FromMsg* msg) const { if (sOB.empty())msg->reply(GlobalMsg["strObListEmpty"]); else @@ -77,10 +75,9 @@ int DiceSession::ob_list(FromMsg* msg) const } msg->reply(GlobalMsg["strObList"] + res.linebreak().show()); } - return 0; } -int DiceSession::ob_clr(FromMsg* msg) +void DiceSession::ob_clr(FromMsg* msg) { if (sOB.empty())msg->reply(GlobalMsg["strObListEmpty"]); else @@ -89,7 +86,63 @@ int DiceSession::ob_clr(FromMsg* msg) msg->reply(GlobalMsg["strObListClr"]); } update(); - return 0; +} + +void DiceSession::log_new(FromMsg* msg) { + mkDir(DiceDir + logger.dirLog); + logger.tStart = time(nullptr); + logger.isLogging = true; + logger.fileLog = "group_" + to_string(msg->fromGroup) + "_" + to_string(time(nullptr)) + ".txt"; + //ȷϢ + msg->reply(GlobalMsg["strLogNew"]); + LogList.insert(msg->fromGroup); + update(); +} +void DiceSession::log_on(FromMsg* msg) { + if (!logger.tStart) { + log_new(msg); + return; + } + if (logger.isLogging) { + msg->reply(GlobalMsg["strLogOnAlready"]); + return; + } + logger.isLogging = true; + msg->reply(GlobalMsg["strLogOn"]); + LogList.insert(msg->fromGroup); + update(); +} +void DiceSession::log_off(FromMsg* msg) { + if (!logger.tStart) { + msg->reply(GlobalMsg["strLogNullErr"]); + return; + } + if (!logger.isLogging) { + msg->reply(GlobalMsg["strLogOffAlready"]); + return; + } + logger.isLogging = false; + //ȲϢ + LogList.erase(msg->fromGroup); + msg->reply(GlobalMsg["strLogOff"]); + update(); +} +void DiceSession::log_end(FromMsg* msg) { + if (!logger.tStart) { + msg->reply(GlobalMsg["strLogNullErr"]); + return; + } + logger.isLogging = false; + logger.tStart = time(0); + msg->strVar["log_file"] = logger.fileLog; + LogList.erase(msg->fromGroup); + msg->reply(GlobalMsg["strLogEnd"]); + update(); + msg->cmd_key = "uplog"; + msg->strVar["log_path"] = log_path(); +} +string DiceSession::log_path() { + return DiceDir + LogInfo::dirLog + "\\" + logger.fileLog; } void DiceSession::save() const @@ -116,6 +169,13 @@ void DiceSession::save() const jData["tables"][strTable][GBKtoUTF8(item)] = val; } } + if (logger.tStart) { + json& jLog = jData["log"]; + jLog["start"] = logger.tStart; + jLog["lastMsg"] = logger.tLastMsg; + jLog["file"] = logger.fileLog; + jLog["logging"] = logger.isLogging; + } fout << jData.dump(1); } @@ -136,7 +196,7 @@ void DiceTableMaster::session_end(long long group) mSession.erase(group); } -const enumap mSMTag{"type", "room", "gm", "player", "observer", "tables"}; +const enumap mSMTag{"type", "room", "gm", "log", "player", "observer", "tables"}; void DiceTableMaster::save() { @@ -179,6 +239,13 @@ int DiceTableMaster::load() } } } + if (j.count("log")) { + json& jLog = j["log"]; + jLog["start"].get_to(pSession->logger.tStart); + jLog["lastMsg"].get_to(pSession->logger.tLastMsg); + jLog["file"].get_to(pSession->logger.fileLog); + jLog["logging"].get_to(pSession->logger.isLogging); + } } } return cnt; diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index 6bc1472d..7252af6c 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -16,12 +16,23 @@ using std::set; class FromMsg; class DiceTableMaster; +struct LogInfo{ + static constexpr auto dirLog{ "\\user\\log" }; + bool isLogging{ false }; + //ʱ䣬Ϊ0򲻴 + time_t tStart{ 0 }; + time_t tLastMsg{ 0 }; + string fileLog{ false }; +}; + class DiceSession { //ֵ map> mTable; //Թ set sOB; + //־ + LogInfo logger; public: //Ⱥ long long room; @@ -65,10 +76,10 @@ class DiceSession bool table_clr(string key); //Թָ - int ob_enter(FromMsg*); - int ob_exit(FromMsg*); - int ob_list(FromMsg*) const; - int ob_clr(FromMsg*); + void ob_enter(FromMsg*); + void ob_exit(FromMsg*); + void ob_list(FromMsg*) const; + void ob_clr(FromMsg*); [[nodiscard]] set get_ob() const { return sOB; } DiceSession& clear_ob() @@ -76,6 +87,13 @@ class DiceSession sOB.clear(); return *this; } + + //logָ + void log_new(FromMsg*); + void log_on(FromMsg*); + void log_off(FromMsg*); + void log_end(FromMsg*); + string log_path(); void save() const; }; @@ -99,3 +117,4 @@ class DiceTableMaster }; inline std::unique_ptr gm; +inline setLogList; diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 9c3025c6..ea65df25 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -47,6 +47,15 @@ std::map GlobalMsg {"strParaIllegal","Ƿ"}, //͵ܻظ {"stranger","û"}, //{nick}޷ȡǿdzʱijƺ {"strAdminOptionEmpty","{self}ʲôô{nick}"}, // + {"strLogNew","{self}ʼ־¼\nʱ.log offͣ.log endɼ¼"}, + {"strLogOn","{self}ʼ־¼\nʹ.log offͣ¼"}, + {"strLogOnAlready","{self}ڼ¼У"}, + {"strLogOff","{self}ͣ־¼\nʹ.log onָ¼"}, + {"strLogOffAlready","{self}Ѿͣ¼"}, + {"strLogEnd","{self}־¼\nϴ־ļ{log_file}"}, + {"strLogNullErr","{self}־¼ѽ"}, + {"strLogUpSuccess","{self}־ϴ\n https://logpainter.kokona.tech/?s3={log_file} Բ鿴¼"}, + {"strLogUpFailure","ź{self}޷ɹϴ־ļȡϵMaster:{master_QQ}\nԭ {ret}"}, {"strGMTableShow","{self}¼{table_name}б"}, {"strGMTableNotExist","{self}ûб{table_name}¼"}, {"strUserTrustShow","{user}{self}μΪ{trust}"}, @@ -240,7 +249,6 @@ std::map GlobalMsg {"strZeroDiceErr", "?ҵ?"}, {"strRollTimeExceeded", "!"}, {"strRollTimeErr", "쳣"}, - {"strObPrivate", "뿴ʲôѽ"}, {"strDismissPrivate", ""}, {"strWelcomePrivate", "⻶ӭ˭أ"}, {"strWelcomeMsgClearNotice", "ȺȺӭʡ"}, @@ -274,6 +282,8 @@ std::map GlobalMsg {"strPreserve", "{self}˽˽ã\n뷢!authorize +[Ⱥ] []"}, {"strJrrp", "{nick}Ʒֵ: {res}"}, {"strJrrpErr", "JRRPȡʧ! Ϣ: \n{res}"}, + { "strFriendDenyNotUser", "źûʹ{self}ļ¼" }, + { "strFriendDenyNoTrust", "ź㲻{self}εû" }, {"strAddFriendWhiteQQ", "{strAddFriend}"}, //ûӺʱظ˾ { "strAddFriend", @@ -317,6 +327,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +565:.log־¼ 564:๦Żƶѵ 563:Żָ 562:GUI @@ -337,7 +348,9 @@ const std::map HelpDoc = { 537:.send)"}, {"Э","0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, {"","Dice!̳: https://kokona.tech \n Dice!̳: https://forum.kokona.tech"}, -{"趨","Master{master_QQ}\n.meʹãֹ\n.jrrpʹã\n봦ƣǽ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n\nȺ:δã\nٷ(ˮ)Ⱥ: 624807593 941980833 882747577\n˽Ⱥ863062599\nȺ1029435374"}, +{"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n{}\nûȺ:{ûȺ}\nٷ(ˮ)Ⱥ: 882747577\n˽Ⱥ863062599 192499947\nȺ1029435374"}, +{"ûȺ","δá"}, +{"","δá"}, {"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, {"ָ",R"(atָָﵥӦat.bot off ָҪӲ.helpӦָ ȡϸϢ.help jrrp @@ -373,6 +386,14 @@ const std::map HelpDoc = { .welcome Ⱥӭ .me ˳ƶ Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)"}, +{"master",R"(ǰMaster:{master_QQ} +MasterӵȨޣҿԵ)"}, +{"log",R"(־¼ +.log new ½־ʼ¼ +.log on ʼ¼ +.log off ͣ¼ +.log end ɼ¼־ļ +)"}, {"deck","ָĬƶѣʹ.drawָƶʱʹôƶѡƶѲŻֱһźϴơ\n.deck set ƶ Ĭƶ\n.deck set 1-100 ָȵ\n.deck show 鿴ʣ࿨\n.deck reset ʣ࿨\n.deck new Զƶѣÿո|ָ޶\n.deck new е|޵|޵|޵|޵|޵\nshowȺڲҪȨ"}, {"Ⱥ","&dismiss"}, {"Ⱥָ","&dismiss"}, diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 4ce6f372..f606da3b 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -36,7 +36,7 @@ * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ const unsigned short Dice_Build = 565u; -inline const std::string Dice_Ver_Without_Build = "2.4.0"; +inline const std::string Dice_Ver_Without_Build = "2.4.1beta1"; constexpr auto DiceRequestHeader = "Dice/2.4.0"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by Shiki Ver " + Dice_Ver; diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 14398744..c47dbaf5 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -109,6 +109,13 @@ void filter_CQcode(string& nick, long long fromGroup) } else return; } + while ((posL = nick.find(CQ_IMAGE)) != string::npos) { + //atʽ + if (size_t posR = nick.find(']', posL); posR != string::npos) { + nick.replace(posL, posR - posL + 1, "[ͼƬ]"); + } + else return; + } while ((posL = nick.find("[CQ:")) != string::npos) { if (size_t posR = nick.find(']', posL); posR != string::npos) diff --git a/Dice/RD.h b/Dice/RD.h index c4320a42..3d58ab4c 100644 --- a/Dice/RD.h +++ b/Dice/RD.h @@ -482,6 +482,12 @@ class RD while (strDice.find("D-") != std::string::npos) strDice.insert(strDice.find("D-") + 1, std::to_string(defaultDice)); + while (strDice.find("D*") != std::string::npos) + strDice.insert(strDice.find("D-") + 1, + std::to_string(defaultDice)); + while (strDice.find("D/") != std::string::npos) + strDice.insert(strDice.find("D-") + 1, + std::to_string(defaultDice)); while (strDice.find("DX") != std::string::npos) strDice.insert(strDice.find("DX") + 1, std::to_string(defaultDice)); diff --git a/Dice/S3PutObject.cpp b/Dice/S3PutObject.cpp new file mode 100644 index 00000000..0b5617fe --- /dev/null +++ b/Dice/S3PutObject.cpp @@ -0,0 +1,69 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "S3PutObject.h" + +#define WIN32_LEAN_AND_MEAN +#include + +#include "GlobalVar.h" + +// ֹWINAPIAWSSDKͻ +#undef GetMessage + +Aws::Auth::AWSCredentials awsCredentials("", ""); + +// жļǷ +bool file_exists(const std::string& file_name) +{ + const DWORD dwAttrib = GetFileAttributesA(file_name.c_str()); + + return (dwAttrib != INVALID_FILE_ATTRIBUTES && + !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +// ϴļS3, S3-accelerate +// ɹʱ"SUCCESS", 򷵻شϢ +std::string put_s3_object(const Aws::String& s3_bucket_name, + const Aws::String& s3_object_name, + const std::string& file_name, + const Aws::String& region) +{ + // Verify file_name exists + if (!file_exists(file_name)) { + return "־ļ!"; + } + + // If region is specified, use it + Aws::Client::ClientConfiguration clientConfig; + if (!region.empty()) + clientConfig.region = region; + clientConfig.endpointOverride = "s3-accelerate.amazonaws.com"; + + // Set up request + Aws::S3::S3Client s3_client(awsCredentials, clientConfig); + Aws::S3::Model::PutObjectRequest object_request; + + object_request.SetBucket(s3_bucket_name); + object_request.SetKey(s3_object_name); + const std::shared_ptr input_data = + Aws::MakeShared(file_name.c_str(), + file_name.c_str(), + std::ios_base::in | std::ios_base::binary); + object_request.SetBody(input_data); + + // Put the object + auto put_object_outcome = s3_client.PutObject(object_request); + if (!put_object_outcome.IsSuccess()) { + const auto& error = put_object_outcome.GetError(); + return "ERROR: " + error.GetExceptionName() + ": " + + error.GetMessage(); + } + return "SUCCESS"; +} \ No newline at end of file diff --git a/Dice/S3PutObject.h b/Dice/S3PutObject.h new file mode 100644 index 00000000..4ac1c950 --- /dev/null +++ b/Dice/S3PutObject.h @@ -0,0 +1,14 @@ +#pragma once +#ifndef TRPGLOGGER_S3PUTOBJECT +#define TRPGLOGGER_S3PUTOBJECT +#include +#include + +// ϴļS3, S3-accelerate +// ɹʱ"SUCCESS", 򷵻شϢ +std::string put_s3_object(const Aws::String& s3_bucket_name, + const Aws::String& s3_object_name, + const std::string& file_name, + const Aws::String& region = ""); + +#endif /*TRPGLOGGER_S3PUTOBJECT*/ \ No newline at end of file diff --git a/Dice/packages.config b/Dice/packages.config new file mode 100644 index 00000000..4265b893 --- /dev/null +++ b/Dice/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file From 9b502b028fd4fb8324a54a4ed22a8793c00f48b7 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 6 Aug 2020 20:36:58 +0800 Subject: [PATCH 017/171] Fix Build --- Dice.sln | 10 +--- Dice/Dice.vcxproj | 115 +++------------------------------------------- 2 files changed, 8 insertions(+), 117 deletions(-) diff --git a/Dice.sln b/Dice.sln index c9314f5b..f1c611a7 100644 --- a/Dice.sln +++ b/Dice.sln @@ -1,24 +1,18 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2015 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30225.117 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "com.w4123.dice", "Dice\Dice.vcxproj", "{BA051175-B8E8-4104-9DD9-B9E225738C42}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Debug|x64.ActiveCfg = Debug|x64 - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Debug|x64.Build.0 = Debug|x64 {BA051175-B8E8-4104-9DD9-B9E225738C42}.Debug|x86.ActiveCfg = Debug|Win32 {BA051175-B8E8-4104-9DD9-B9E225738C42}.Debug|x86.Build.0 = Debug|Win32 - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Release|x64.ActiveCfg = Release|x64 - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Release|x64.Build.0 = Release|x64 {BA051175-B8E8-4104-9DD9-B9E225738C42}.Release|x86.ActiveCfg = Release|Win32 {BA051175-B8E8-4104-9DD9-B9E225738C42}.Release|x86.Build.0 = Release|Win32 EndGlobalSection diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index 41a31d92..f81d6194 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -9,14 +9,6 @@ Release Win32 - - Debug - x64 - - - Release - x64 - 15.0 @@ -40,19 +32,6 @@ MultiByte v142 - - DynamicLibrary - true - v142 - MultiByte - - - DynamicLibrary - false - true - MultiByte - v142 - @@ -64,12 +43,6 @@ - - - - - - false @@ -81,6 +54,7 @@ false + static true @@ -88,20 +62,7 @@ static static false - - - true - MultiThreadedDebug - static - static - false - - - false - MultiThreaded - static - static - false + static @@ -135,7 +96,8 @@ UseLinkTimeCodeGeneration %(ForceSymbolReferences) NotSet - %(AdditionalLibraryDirectories) + ..\packages\AWSSDKCPP-Core.1.6.25\build\native\lib\Win32\Release\v141\static;..\packages\AWSSDKCPP-S3.1.6.20060301.25\build\native\lib\Win32\Release\v141\static;%(AdditionalLibraryDirectories) + aws-cpp-sdk-core.lib;aws-cpp-sdk-s3.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -161,73 +123,8 @@ Windows true - %(AdditionalLibraryDirectories) - - - - - - - copy /Y $(TargetPath) "D:\酷Q Air\dev\com.w4123.dice" - - - - - NotUsing - Level3 - Disabled - false - _DEBUG;DICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - SyncCThrow - ..\CQSDK;%(AdditionalIncludeDirectories) - MultiThreadedDebug - stdcpp17 - - - Windows - true - %(AdditionalLibraryDirectories) - - - - - - - copy /Y $(TargetPath) "D:\酷Q Air\dev\com.w4123.dice" - - - - - NotUsing - Level3 - MaxSpeed - true - true - true - NDEBUG;DICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - SyncCThrow - true - true - MultiThreaded - Precise - ..\CQSDK;%(AdditionalIncludeDirectories) - true - AnySuitable - Speed - true - stdcpp17 - - - Windows - true - true - false - false - UseLinkTimeCodeGeneration - NotSet - %(AdditionalLibraryDirectories) + ..\packages\AWSSDKCPP-Core.1.6.25\build\native\lib\Win32\Debug\v141\static;..\packages\AWSSDKCPP-S3.1.6.20060301.25\build\native\lib\Win32\Debug\v141\static;%(AdditionalLibraryDirectories) + aws-cpp-sdk-core.lib;aws-cpp-sdk-s3.lib;%(AdditionalDependencies) From 6d6cd7f09ac0a886367d67ef1d917a51195c7658 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 6 Aug 2020 20:40:01 +0800 Subject: [PATCH 018/171] Fix Appveyor --- appveyor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9988a5b3..f5cac2c7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,8 +2,10 @@ version: 2.{build} skip_tags: true image: - Visual Studio 2019 - -configuration: Release +before_build: +- cmd: nuget restore +configuration: Debug, Release platform: x86 build: + parallel: true verbosity: minimal From 5e46960c813eb4d3183de223f7f105e6400cce29 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 6 Aug 2020 20:41:42 +0800 Subject: [PATCH 019/171] Fix appveyor --- appveyor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f5cac2c7..6404a3cc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,9 @@ image: - Visual Studio 2019 before_build: - cmd: nuget restore -configuration: Debug, Release +configuration: +- Debug +- Release platform: x86 build: parallel: true From 55a10945e717d73a4fd12b55f0d209ca21694270 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 7 Aug 2020 19:47:52 +0800 Subject: [PATCH 020/171] fix .log & .link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复log功能 优化link功能 修复定时任务队列反向排序 优化ResList没有第一页的问题 --- Dice/CardDeck.cpp | 16 ++++ Dice/CharacterCard.cpp | 2 +- Dice/Dice.cpp | 9 +- Dice/Dice.vcxproj | 1 + Dice/DiceConsole.h | 4 - Dice/DiceEvent.cpp | 195 +++++++++++++++++------------------------ Dice/DiceEvent.h | 40 ++------- Dice/DiceJob.cpp | 7 +- Dice/DiceSchedule.cpp | 14 +-- Dice/DiceSchedule.h | 4 +- Dice/DiceSession.cpp | 165 ++++++++++++++++++++++++++++------ Dice/DiceSession.h | 33 +++++-- Dice/GlobalVar.cpp | 17 ++-- Dice/MsgFormat.cpp | 24 +++++ Dice/MsgFormat.h | 26 +----- Dice/RD.h | 9 +- Dice/S3PutObject.cpp | 10 +-- Dice/S3PutObject.h | 1 + Dice/strExtern.cpp | 2 +- 19 files changed, 335 insertions(+), 244 deletions(-) diff --git a/Dice/CardDeck.cpp b/Dice/CardDeck.cpp index fd7ae4b9..566175df 100644 --- a/Dice/CardDeck.cpp +++ b/Dice/CardDeck.cpp @@ -32,6 +32,7 @@ namespace CardDeck } }, {"", {"", "", "", "", "", "", "", "", "", ""}}, + {"֧", {"", "", "", "î", "", "", "", "δ", "", "", "", ""}}, { "first_name_cn", { @@ -358,6 +359,21 @@ namespace CardDeck } }, {"Ӳ", {"", ""}}, + { "˿", + { + "A","2","3","4","5","6","7","8","9","10","J","Q","K", + "A","2","3","4","5","6","7","8","9","10","J","Q","K", + "ƬA","Ƭ2","Ƭ3","Ƭ4","Ƭ5","Ƭ6","Ƭ7","Ƭ8","Ƭ9","Ƭ10","ƬJ","ƬQ","ƬK", + "÷A","÷2","÷3","÷4","÷5","÷6","÷7","÷8","÷9","÷10","÷J","÷Q","÷K", + "С",""} + }, + { "齫", + { + "::4::һ","::4::","::4::","::4::","::4::","::4::","::4::","::4::","::4::", + "::4::һͲ","::4::Ͳ","::4::Ͳ","::4::Ͳ","::4::Ͳ","::4::Ͳ","::4::Ͳ","::4::Ͳ","::4::Ͳ", + "::4::һ","::4::","::4::","::4::","::4::","::4::","::4::","::4::","::4::", + "::4::","::4::Ϸ","::4::","::4::","::4::","::4::","::4::"} + }, {"Ա", {"", "Ů"}}, { "żʾ", diff --git a/Dice/CharacterCard.cpp b/Dice/CharacterCard.cpp index 0a1a5eb2..7981f6fe 100644 --- a/Dice/CharacterCard.cpp +++ b/Dice/CharacterCard.cpp @@ -36,5 +36,5 @@ Player& getPlayer(long long qq) string getPCName(long long qq, long long group) { if (PList.count(qq) && PList[qq][group].Name != "ɫ")return PList[qq][group].Name; - return getName(qq, group); + return "{nick}"; } diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 3a532dd6..d380aa5e 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -49,6 +49,7 @@ #include "DiceEvent.h" #include "DiceSession.h" #include "DiceGUI.h" +#include "S3PutObject.h" #pragma warning(disable:4996) #pragma warning(disable:6031) @@ -57,8 +58,6 @@ using namespace std; using namespace CQ; unordered_map UserList{}; -map mLinkedList; -multimap mFwdList; ThreadFactory threads; string strFileLoc; @@ -131,6 +130,7 @@ void dataInit() for (auto [grp, qq] : ObserveGroup) { gm->session(grp).sOB.insert(qq); + gm->session(grp).update(); } ifstream ifINIT(strFileLoc + "INIT.DiceDB"); if (ifINIT) @@ -141,11 +141,11 @@ void dataInit() while (ifINIT >> Group >> nickname >> value) { gm->session(Group).mTable["ȹ"].emplace(base64_decode(nickname), value); + gm->session(Group).update(); } } ifINIT.close(); console.log("ʼԹȹ¼" + to_string(gm->mSession.size()) + "", 1); - gm->save(); } today = make_unique(DiceDir + "/user/DiceToday.json"); } @@ -165,7 +165,6 @@ void dataBackUp() saveBFile(DiceDir + "\\user\\PlayerCards.RDconf", PList); saveFile(DiceDir + "\\user\\ChatList.txt", ChatList); saveBFile(DiceDir + "\\user\\ChatConf.RDconf", ChatList); - clearUser(); saveFile(DiceDir + "\\user\\UserList.txt", UserList); saveBFile(DiceDir + "\\user\\UserConf.RDconf", UserList); } @@ -448,6 +447,7 @@ EVE_Enable(eventEnable) dataInit(); // ȷִ߳н while (msgSendThreadRunning)Sleep(10); + Aws::InitAPI(options); Enabled = true; threads(SendMsg); threads(ConsoleTimer); @@ -928,6 +928,7 @@ void global_exit() { console.reset(); EditedMsg.clear(); blacklist.reset(); + Aws::ShutdownAPI(options); } EVE_Disable(eventDisable) diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index f81d6194..c7431611 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -63,6 +63,7 @@ static false static + app diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index b7d7ea85..422b7039 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -185,10 +185,6 @@ void getExceptGroup(); //֪ͨ //һ extern int clearGroup(std::string strPara = "unpower", long long fromQQ = 0); - //ӵ촰 - extern std::map mLinkedList; - //תб - extern std::multimap mFwdList; //ʱ extern long long llStartTime; //ǰʱ diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index d7a5df84..1e80db92 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -15,25 +15,65 @@ using namespace std; using namespace CQ; + +void FromMsg::reply(std::string strReply, bool isFormat) { + isAns = true; + if (isFormat) + strReply = format(strReply, GlobalMsg, strVar); + AddMsgToQueue(strReply, fromChat); + if (LogList.count(fromSession)) { + filter_CQcode(strReply, fromGroup); + ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); + logout << GBKtoUTF8(getMsg("strSelfName")) + "(" + to_string(console.DiceMaid) + ") " + printTTime(fromTime) << endl + << GBKtoUTF8(strReply) << endl << endl; + } +} + +void FromMsg::reply(std::string strReply, const std::initializer_list replace_str, + bool isFormat) { + isAns = true; + while (isspace(static_cast(strReply[0]))) + strReply.erase(strReply.begin()); + if (isFormat) { + int index = 0; + for (const auto& s : replace_str) { + strVar[to_string(index++)] = s; + } + strReply = format(strReply, GlobalMsg, strVar); + } + AddMsgToQueue(strReply, fromChat); + if (LogList.count(fromSession)&& gm->session(fromSession).is_logging()) { + filter_CQcode(strReply, fromGroup); + ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); + logout << GBKtoUTF8(getMsg("strSelfName")) + "(" + to_string(console.DiceMaid) + ") " + printTTime(fromTime) << endl + << GBKtoUTF8(strReply) << endl << endl; + } +} + +void FromMsg::reply() { + reply(strReply); +} + void FromMsg::fwdMsg() { - if (mFwdList.count(fromChat) && strLowerMessage.find(".link") != 0) + if (LinkList.count(fromSession) && LinkList[fromSession].second && strLowerMessage.find(".link") != 0) { - const auto range = mFwdList.equal_range(fromChat); string strFwd; if (trusted < 5)strFwd += printFrom(); strFwd += strMsg; - for (auto it = range.first; it != range.second; ++it) - { - AddMsgToQueue(strFwd, it->second.first, it->second.second); + if (long long aim = LinkList[fromSession].first;aim < 0) { + AddMsgToQueue(strFwd, ~aim); + } + else if (ChatList.count(aim)) { + AddMsgToQueue(strFwd, aim, chat(aim).isGroup ? msgtype::Group : msgtype::Discuss); } } - if (LogList.count(fromGroup)) { + if (LogList.count(fromSession) && strLowerMessage.find(".log") != 0) { string msg = strMsg; filter_CQcode(msg, fromGroup); - ofstream logout(gm->session(fromGroup).log_path(), ios::out | ios::app); - logout << printQQ(fromQQ) + " " + printTTime(fromTime) << endl - << msg << endl << endl; + ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); + logout << GBKtoUTF8(printQQ(fromQQ)) + " " + printTTime(fromTime) << endl + << GBKtoUTF8(msg) << endl << endl; } } @@ -716,10 +756,6 @@ int FromMsg::DiceReply() strVar["pc"] = getPCName(fromQQ, fromGroup); strVar["at"] = intT ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions > 1 || pGrp->inviter == fromQQ; - strLowerMessage = strMsg; - std::transform(strLowerMessage.begin(), strLowerMessage.end(), strLowerMessage.begin(), - [](unsigned char c) { return tolower(c); }); - fwdMsg(); //ָƥ if (strLowerMessage.substr(intMsgCnt, 9) == "authorize") { @@ -738,7 +774,7 @@ int FromMsg::DiceReply() return 1; } } - if (pGrp->isset("ʹ") && !pGrp->isset("δ"))return 0; + if (pGrp->isset("ʹ") && !pGrp->isset("δ") && !pGrp->isset("ЭЧ"))return 0; if (trusted > 0) { pGrp->set("ʹ").reset("δ").reset("ЭЧ"); @@ -2091,15 +2127,15 @@ int FromMsg::DiceReply() while (isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt++; if (strLowerMessage.substr(intMsgCnt, 3) == "clr") { - if (gm->session(fromGroup).table_clr("ȹ")) + if (gm->session(fromSession).table_clr("ȹ")) reply("ɹȹ¼"); else reply("бΪգ"); return 1; } strVar["table_name"] = "ȹ"; - if (gm->session(fromGroup).table_count("ȹ")) - reply(GlobalMsg["strGMTableShow"] + gm->session(fromGroup).table_prior_show("ȹ")); + if (gm->session(fromSession).table_count("ȹ")) + reply(GlobalMsg["strGMTableShow"] + gm->session(fromSession).table_prior_show("ȹ")); else reply(GlobalMsg["strGMTableNotExist"]); return 1; } @@ -2219,91 +2255,16 @@ int FromMsg::DiceReply() reply(GlobalMsg["strNotAdmin"]); return true; } - string strOption = readPara(); - if (strOption == "close") - { - if (mLinkedList.count(fromChat)) - { - chatType ToChat = mLinkedList[fromChat]; - mLinkedList.erase(fromChat); - auto Range = mFwdList.equal_range(fromChat); - for (auto it = Range.first; it != Range.second;) - { - if (it->second == ToChat) - { - it = mFwdList.erase(it); - } - else - { - ++it; - } - } - Range = mFwdList.equal_range(ToChat); - for (auto it = Range.first; it != Range.second;) - { - if (it->second == fromChat) - { - it = mFwdList.erase(it); - } - else - { - ++it; - } - } - reply(GlobalMsg["strLinkLoss"]); - return 1; - } - return 1; - } - string strType = readPara(); - chatType ToChat; - string strID = readDigit(); - if (strID.empty()) - { - reply(GlobalMsg["strLinkNotFound"]); - return 1; - } - ToChat.first = stoll(strID); - if (strType == "qq") - { - ToChat.second = msgtype::Private; - } - else if (strType == "group") - { - ToChat.second = msgtype::Group; + strVar["option"] = readPara(); + if (strVar["option"] == "close") { + gm->session(fromSession).link_close(this); } - else if (strType == "discuss") - { - ToChat.second = msgtype::Discuss; + else if (strVar["option"] == "start") { + gm->session(fromSession).link_start(this); } - else - { - reply(GlobalMsg["strLinkNotFound"]); - return 1; + else if (strVar["option"] == "with" || strVar["option"] == "from" || strVar["option"] == "to") { + gm->session(fromSession).link_new(this); } - if (mLinkedList.count(fromChat) && mFwdList.count(mLinkedList[fromChat])) - { - mFwdList.erase(mLinkedList[fromChat]); - } - if (strOption == "with") - { - mLinkedList[fromChat] = ToChat; - mFwdList.insert({fromChat, ToChat}); - mFwdList.insert({ToChat, fromChat}); - } - else if (strOption == "from") - { - mLinkedList[fromChat] = ToChat; - mFwdList.insert({ToChat, fromChat}); - } - else if (strOption == "to") - { - mLinkedList[fromChat] = ToChat; - mFwdList.insert({fromChat, ToChat}); - } - else return 1; - if (ChatList.count(ToChat.first) || UserList.count(ToChat.first))reply(GlobalMsg["strLinked"]); - else reply(GlobalMsg["strLinkWarning"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 4) == "name") @@ -2556,16 +2517,12 @@ int FromMsg::DiceReply() return 1; } else if (strLowerMessage.substr(intMsgCnt, 3) == "log") { - if (!intT) { - reply(fmt->get_help("log")); - return 1; - } intMsgCnt += 3; string strPara = readPara(); if (strPara.empty()) { reply(fmt->get_help("log")); } - else if (DiceSession& game = gm->session(fromGroup); strPara == "new") { + else if (DiceSession& game = gm->session(fromSession); strPara == "new") { game.log_new(this); }else if(strPara == "on") { game.log_on(this); @@ -2904,7 +2861,7 @@ int FromMsg::DiceReply() if (groupset(fromGroup, strVar["option"]) < 1) { chat(fromGroup).set(strVar["option"]); - gm->session(fromGroup).clear_ob(); + gm->session(fromSession).clear_ob(); reply(GlobalMsg["strObOff"]); } else @@ -2933,13 +2890,13 @@ int FromMsg::DiceReply() } if (strOption == "list") { - gm->session(fromGroup).ob_list(this); + gm->session(fromSession).ob_list(this); } else if (strOption == "clr") { if (intT == DiscussT || getGroupMemberInfo(fromGroup, fromQQ).permissions >= 2) { - gm->session(fromGroup).ob_clr(this); + gm->session(fromSession).ob_clr(this); } else { @@ -2948,11 +2905,11 @@ int FromMsg::DiceReply() } else if (strOption == "exit") { - gm->session(fromGroup).ob_exit(this); + gm->session(fromSession).ob_exit(this); } else { - gm->session(fromGroup).ob_enter(this); + gm->session(fromSession).ob_enter(this); } return 1; } @@ -3444,7 +3401,7 @@ int FromMsg::DiceReply() reply(GlobalMsg["strUnknownErr"]); return 1; } - gm->session(fromGroup).table_add("ȹ", initdice.intTotal, strname); + gm->session(fromSession).table_add("ȹ", initdice.intTotal, strname); const string strReply = strname + "ȹ㣺" + initdice.FormCompleteString(); reply(strReply); return 1; @@ -3814,7 +3771,7 @@ int FromMsg::DiceReply() { strTurnNotice = "" + printChat(fromChat) + " " + strTurnNotice; AddMsgToQueue(strTurnNotice, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromGroup).get_ob()) + for (auto qq : gm->session(fromSession).get_ob()) { if (qq != fromQQ) { @@ -3926,7 +3883,7 @@ int FromMsg::DiceReply() strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromGroup).get_ob()) + for (auto qq : gm->session(fromSession).get_ob()) { if (qq != fromQQ) { @@ -3955,7 +3912,7 @@ int FromMsg::DiceReply() strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromGroup).get_ob()) + for (auto qq : gm->session(fromSession).get_ob()) { if (qq != fromQQ) { @@ -4072,7 +4029,7 @@ int FromMsg::DiceReply() { strReply = format("" + printChat(fromChat) + " " + GlobalMsg["strRollTurn"], GlobalMsg, strVar); AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromGroup).get_ob()) + for (auto qq : gm->session(fromSession).get_ob()) { if (qq != fromQQ) { @@ -4158,7 +4115,7 @@ int FromMsg::DiceReply() strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromGroup).get_ob()) + for (auto qq : gm->session(fromSession).get_ob()) { if (qq != fromQQ) { @@ -4199,7 +4156,7 @@ int FromMsg::DiceReply() strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromGroup).get_ob()) + for (auto qq : gm->session(fromSession).get_ob()) { if (qq != fromQQ) { @@ -4266,8 +4223,12 @@ bool FromMsg::DiceFilter() } if (isOtherCalled && !isCalled)return false; init2(strMsg); - if (fromChat.second == msgtype::Private) isCalled = true; + strLowerMessage = strMsg; + std::transform(strLowerMessage.begin(), strLowerMessage.end(), strLowerMessage.begin(), + [](unsigned char c) { return tolower(c); }); trusted = trustedQQ(fromQQ); + fwdMsg(); + if (fromChat.second == msgtype::Private) isCalled = true; isBotOff = (console["DisabledGlobal"] && (trusted < 4 || !isCalled)) || (!(isCalled && console["DisabledListenAt"]) && (groupset(fromGroup, "ָͣ") > 0)); if (DiceReply()) { diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index a4c64f85..5c06529d 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -21,46 +21,24 @@ class FromMsg :public DiceJobDetail { public: string strLowerMessage; long long fromGroup = 0; + long long fromSession; Chat* pGrp = nullptr; string strReply; - FromMsg(std::string message, long long qq) :DiceJobDetail(qq, { qq,CQ::msgtype::Private }, message){} - FromMsg(std::string message, long long fromGroup, CQ::msgtype msgType, long long qq) :DiceJobDetail(qq, { fromGroup,msgType }, message), fromGroup(fromGroup) { + FromMsg(std::string message, long long qq) :DiceJobDetail(qq, { qq,CQ::msgtype::Private }, message){ + fromSession = ~fromQQ; + } + FromMsg(std::string message, long long fromGroup, CQ::msgtype msgType, long long qq) :DiceJobDetail(qq, { fromGroup,msgType }, message), fromGroup(fromGroup), fromSession(fromGroup){ pGrp = &chat(fromGroup); } bool isBlock = false; - void reply(std::string strReply, bool isFormat) - { - isAns = true; - if (isFormat) - AddMsgToQueue(format(strReply, GlobalMsg, strVar), fromChat); - else AddMsgToQueue(strReply, fromChat); - } + void reply(std::string strReply, bool isFormat); void reply(std::string strReply, const std::initializer_list replace_str = {}, - bool isFormat = true) - { - isAns = true; - while (isspace(static_cast(strReply[0]))) - strReply.erase(strReply.begin()); - if (!isFormat) - { - AddMsgToQueue(strReply, fromChat); - return; - } - int index = 0; - for (const auto& s : replace_str) - { - strVar[to_string(index++)] = s; - } - AddMsgToQueue(format(strReply, GlobalMsg, strVar), fromChat); - } + bool isFormat = true); - void reply() - { - reply(strReply); - } + void reply(); //֪ͨ void note(std::string strMsg, int note_lv = 0b1) @@ -118,7 +96,7 @@ class FromMsg :public DiceJobDetail { } return -2; } - +public: //ո void readSkipSpace() { diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 5be2a09a..51085149 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -410,12 +410,13 @@ void log_put(DiceJob& job) { "ap-southeast-1"); if (job["ret"] == "SUCCESS") { job.echo(getMsg("strLogUpSuccess", job.strVar)); - return; } - else if (++job.cntExec > 5) { - job.echo(getMsg("strLogUpFailure",job.strVar)); + else if (job.cntExec++ > 1) { + job.echo(getMsg("strLogUpFailureEnd",job.strVar)); } else { + job["retry"] = to_string(job.cntExec); + job.echo(getMsg("strLogUpFailure", job.strVar)); sch.add_job_for(2 * 60, job); } } diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index b0ef9a83..5aa8e8cf 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -59,7 +59,7 @@ std::condition_variable cvJob; std::condition_variable cvJobWaited; //ʱ using waited_job = pair; -std::priority_queue> queueJobWaited; +std::priority_queue,std::greater> queueJobWaited; std::mutex mtJobWaited; void jobHandle() { @@ -111,12 +111,12 @@ void DiceScheduler::push_job(const char* job_name) { void DiceScheduler::add_job_for(unsigned int waited, const DiceJobDetail& job) { if (!Enabled)return; std::unique_lock lock_queue(mtJobWaited); - queueJobWaited.emplace(time(NULL) + waited, job); + queueJobWaited.emplace(time(nullptr) + waited, job); } void DiceScheduler::add_job_for(unsigned int waited, const char* job_name) { if (!Enabled)return; std::unique_lock lock_queue(mtJobWaited); - queueJobWaited.emplace(time(NULL) + waited, job_name); + queueJobWaited.emplace(time(nullptr) + waited, job_name); } void DiceScheduler::add_job_until(time_t cloc, const DiceJobDetail& job) { @@ -180,9 +180,9 @@ void DiceToday::load() { } string printTTime(time_t tt) { - char timestamp[20]; + char tm_buffer[20]; tm t{}; - if (!tt || localtime_s(&t, &tt))return "1970-00-00 00:00:00"; - sprintf_s(timestamp, "%04d-%02d-%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); - return timestamp; + if (!tt || localtime_s(&t, &tt))return "1970-00-00 00:00:00"; + strftime(tm_buffer, 20, "%Y-%m-%d %H:%M:%S", &t); + return tm_buffer; } \ No newline at end of file diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index cb571c6b..a00464ec 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -18,6 +18,7 @@ struct DiceJobDetail { string cmd_key; string strMsg; time_t fromTime = time(nullptr); + size_t cntExec{ 0 }; //ʱ map strVar = {}; DiceJobDetail(const char* cmd, bool isFromSelf = false):cmd_key(cmd){ @@ -26,7 +27,7 @@ struct DiceJobDetail { DiceJobDetail(long long qq, chatType ct, std::string msg = "", const char* cmd = "") :fromQQ(qq), fromChat(ct), strMsg(msg),cmd_key(cmd) { } - string operator[](const char* key){ + string& operator[](const char* key){ return strVar[key]; } bool operator<(const DiceJobDetail& other)const { @@ -39,7 +40,6 @@ class DiceJob : public DiceJobDetail { public: DiceJob(DiceJobDetail detail) :DiceJobDetail(detail) {} Renum ren = Renum::NIL; - size_t cntExec{ 0 }; void exec(); void echo(const std::string&); void note(const std::string&, int); diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index 05227646..bde8790d 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2019-2020 String.Empty */ #include +#include #include "Jsonio.h" #include "DiceSession.h" #include "MsgFormat.h" @@ -45,8 +46,8 @@ void DiceSession::ob_enter(FromMsg* msg) { sOB.insert(msg->fromQQ); msg->reply(GlobalMsg["strObEnter"]); + update(); } - update(); } void DiceSession::ob_exit(FromMsg* msg) @@ -92,10 +93,13 @@ void DiceSession::log_new(FromMsg* msg) { mkDir(DiceDir + logger.dirLog); logger.tStart = time(nullptr); logger.isLogging = true; - logger.fileLog = "group_" + to_string(msg->fromGroup) + "_" + to_string(time(nullptr)) + ".txt"; + logger.fileLog = (type == "solo") + ? ("qq_" + to_string(msg->fromQQ) + "_" + to_string(logger.tStart) + ".txt") + : ("group_" + to_string(msg->fromGroup) + "_" + to_string(logger.tStart) + ".txt"); + logger.pathLog = DiceDir + LogInfo::dirLog + "\\" + logger.fileLog; //ȷϢ msg->reply(GlobalMsg["strLogNew"]); - LogList.insert(msg->fromGroup); + LogList.insert(room); update(); } void DiceSession::log_on(FromMsg* msg) { @@ -109,7 +113,7 @@ void DiceSession::log_on(FromMsg* msg) { } logger.isLogging = true; msg->reply(GlobalMsg["strLogOn"]); - LogList.insert(msg->fromGroup); + LogList.insert(room); update(); } void DiceSession::log_off(FromMsg* msg) { @@ -123,7 +127,7 @@ void DiceSession::log_off(FromMsg* msg) { } logger.isLogging = false; //ȲϢ - LogList.erase(msg->fromGroup); + LogList.erase(room); msg->reply(GlobalMsg["strLogOff"]); update(); } @@ -132,30 +136,111 @@ void DiceSession::log_end(FromMsg* msg) { msg->reply(GlobalMsg["strLogNullErr"]); return; } + LogList.erase(room); logger.isLogging = false; - logger.tStart = time(0); + logger.tStart = 0; + if (std::filesystem::path pathFile(log_path()); !std::filesystem::exists(pathFile)) { + msg->reply(GlobalMsg["strLogEndEmpty"]); + return; + } msg->strVar["log_file"] = logger.fileLog; - LogList.erase(msg->fromGroup); + msg->strVar["log_path"] = log_path(); msg->reply(GlobalMsg["strLogEnd"]); update(); msg->cmd_key = "uplog"; - msg->strVar["log_path"] = log_path(); + sch.push_job(*msg); } -string DiceSession::log_path() { - return DiceDir + LogInfo::dirLog + "\\" + logger.fileLog; +string DiceSession::log_path()const { + return logger.pathLog; +} + + +void DiceSession::link_new(FromMsg* msg) { + string strType = msg->readPara(); + string strID = msg->readDigit(); + if (strID.empty()) { + msg->reply(GlobalMsg["strLinkNotFound"]); + return; + } + long long id = stoll(strID); + if (strType == "qq" || strType == "q") { + id = ~id; + } + else if (!ChatList.count(id)) { + msg->reply(GlobalMsg["strLinkNotFound"]); + return; + } + linker.linkFwd = id; + //Ѵڵ + if (linker.isLinking) { + LinkList.erase(room); + LinkList.erase(linker.linkFwd); + } + //ռ߲ + if (LinkList.count(room)) { + msg->reply(GlobalMsg["strLinkedAlready"]); + } + else if (LinkList.count(linker.linkFwd)) { + msg->reply(GlobalMsg["strLinkBusy"]); + } + else { + linker.typeLink = msg->strVar["option"]; + LinkList[room] = { linker.linkFwd ,linker.typeLink != "from" }; + LinkList[linker.linkFwd] = { room ,linker.typeLink != "to" }; + linker.isLinking = true; + msg->reply(GlobalMsg["strLinked"]); + update(); + } +} +void DiceSession::link_start(FromMsg* msg) { + if (linker.linkFwd) { + if (LinkList.count(room)) { + msg->reply(GlobalMsg["strLinkingAlready"]); + } + else if (LinkList.count(linker.linkFwd)) { + msg->reply(GlobalMsg["strLinkBusy"]); + } + else { + LinkList[room] = { linker.linkFwd ,linker.typeLink != "from" }; + LinkList[linker.linkFwd] = { room ,linker.typeLink != "to" }; + linker.isLinking = true; + msg->reply(GlobalMsg["strLinked"]); + update(); + } + } + else { + msg->reply(GlobalMsg["strLinkNotFound"]); + } +} +void DiceSession::link_close(FromMsg* msg) { + if (auto link = LinkList.find(room); link != LinkList.end()) { + linker.isLinking = false; + if (gm->mSession.count(link->second.first)) + gm->session(link->second.first).linker.isLinking = false; + LinkList.erase(link->second.first); + LinkList.erase(link); + msg->reply(GlobalMsg["strLinkClose"]); + update(); + } + else { + msg->reply(GlobalMsg["strLinkCloseAlready"]); + } } void DiceSession::save() const { mkDir(DiceDir + "\\user\\session"); - ofstream fout(DiceDir + R"(\user\session\)" + to_string(room) + ".json"); + string pathFile = (type == "solo") + ? (DiceDir + R"(\user\session\Q)" + to_string(~room) + ".json" ) + : (DiceDir + R"(\user\session\)" + to_string(room) + ".json"); + ofstream fout(pathFile); if (!fout) { - console.log("ȺϢʧ:" + DiceDir + R"(\user\session\)" + to_string(room), 1); + console.log("Ϣʧ:" + DiceDir + R"(\user\session\)" + to_string(room), 1); return; } nlohmann::json jData; - jData["type"] = "Simple"; + jData["type"] = type; jData["room"] = room; jData["create_time"] = tCreate; jData["update_time"] = tUpdate; @@ -169,12 +254,20 @@ void DiceSession::save() const jData["tables"][strTable][GBKtoUTF8(item)] = val; } } - if (logger.tStart) { - json& jLog = jData["log"]; + if (logger.tStart || !logger.fileLog.empty()) { + json jLog; jLog["start"] = logger.tStart; jLog["lastMsg"] = logger.tLastMsg; jLog["file"] = logger.fileLog; jLog["logging"] = logger.isLogging; + jData["log"] = jLog; + } + if (linker.linkFwd) { + json jLink; + jLink["type"] = linker.typeLink; + jLink["target"] = linker.linkFwd; + jLink["linking"] = linker.isLinking; + jData["link"] = jLink; } fout << jData.dump(1); } @@ -184,7 +277,8 @@ Session& DiceTableMaster::session(long long group) if (!mSession.count(group)) { std::unique_lock lock(sessionMutex); - mSession.emplace(group, std::make_shared(group)); + if (group < 0)mSession.emplace(group, std::make_shared(group, "solo")); + else mSession.emplace(group, std::make_shared(group)); } return *mSession[group]; } @@ -198,6 +292,7 @@ void DiceTableMaster::session_end(long long group) const enumap mSMTag{"type", "room", "gm", "log", "player", "observer", "tables"}; +/* void DiceTableMaster::save() { mkDir(DiceDir + "\\user\\session"); @@ -207,6 +302,7 @@ void DiceTableMaster::save() pSession->save(); } } +*/ int DiceTableMaster::load() { @@ -223,10 +319,33 @@ int DiceTableMaster::load() cnt--; continue; } + auto pSession(std::make_shared(j["room"])); + j["type"].get_to(pSession->type); + pSession->create(j["create_time"]).update(j["update_time"]); + if (j.count("log")) { + json& jLog = j["log"]; + jLog["start"].get_to(pSession->logger.tStart); + jLog["lastMsg"].get_to(pSession->logger.tLastMsg); + jLog["file"].get_to(pSession->logger.fileLog); + jLog["logging"].get_to(pSession->logger.isLogging); + pSession->logger.update(); + pSession->logger.pathLog = DiceDir + LogInfo::dirLog + "\\" + pSession->logger.fileLog; + if (pSession->logger.isLogging) { + LogList.insert(pSession->room); + } + } + if (j.count("link")) { + json& jLink = j["link"]; + jLink["type"].get_to(pSession->linker.typeLink); + jLink["target"].get_to(pSession->linker.linkFwd); + jLink["linking"].get_to(pSession->linker.isLinking); + if (pSession->linker.isLinking) { + LinkList[pSession->room] = { pSession->linker.linkFwd,pSession->linker.typeLink != "from" }; + LinkList[pSession->linker.linkFwd] = { pSession->room,pSession->linker.typeLink != "to" }; + } + } if (j["type"] == "simple") { - auto pSession(std::make_shared(j["room"])); - pSession->create(j["create_time"]).update(j["update_time"]); if (j.count("observer")) pSession->sOB = j["observer"].get>(); if (j.count("tables")) { @@ -239,14 +358,8 @@ int DiceTableMaster::load() } } } - if (j.count("log")) { - json& jLog = j["log"]; - jLog["start"].get_to(pSession->logger.tStart); - jLog["lastMsg"].get_to(pSession->logger.tLastMsg); - jLog["file"].get_to(pSession->logger.fileLog); - jLog["logging"].get_to(pSession->logger.isLogging); - } - } + } + mSession[pSession->room] = std::move(pSession); } return cnt; } diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index 7252af6c..5e86e37d 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -22,7 +22,19 @@ struct LogInfo{ //ʱ䣬Ϊ0򲻴 time_t tStart{ 0 }; time_t tLastMsg{ 0 }; - string fileLog{ false }; + string fileLog; + //·棬ʼʱ + string pathLog; + void update() { + tLastMsg = time(nullptr); + } +}; + +struct LinkInfo { + bool isLinking{ false }; + string typeLink; + //󴰿ڣΪ0򲻴 + long long linkFwd{ 0 }; }; class DiceSession @@ -33,11 +45,14 @@ class DiceSession set sOB; //־ LogInfo logger; + // + LinkInfo linker; public: + string type; //Ⱥ long long room; - DiceSession(long long group) : room(group) + DiceSession(long long group, string t = "simple") : room(group),type(t) { tUpdate = tCreate = time(nullptr); } @@ -59,7 +74,6 @@ class DiceSession DiceSession& update(time_t tt) { tUpdate = tt; - save(); return *this; } @@ -93,7 +107,14 @@ class DiceSession void log_on(FromMsg*); void log_off(FromMsg*); void log_end(FromMsg*); - string log_path(); + [[nodiscard]] string log_path()const; + [[nodiscard]] bool is_logging() const { return logger.isLogging; } + + //linkָ + void link_new(FromMsg*); + void link_start(FromMsg*); + void link_close(FromMsg*); + [[nodiscard]] bool is_linking() const { return linker.isLinking; } void save() const; }; @@ -112,9 +133,11 @@ class DiceTableMaster map> mSession; Session& session(long long group); void session_end(long long group); - void save(); + //void save(); int load(); }; inline std::unique_ptr gm; inline setLogList; +//ֹŽӵȻڲ +inline map>LinkList; diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index ea65df25..7c43d183 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -53,9 +53,11 @@ std::map GlobalMsg {"strLogOff","{self}ͣ־¼\nʹ.log onָ¼"}, {"strLogOffAlready","{self}Ѿͣ¼"}, {"strLogEnd","{self}־¼\nϴ־ļ{log_file}"}, + {"strLogEndEmpty","{self}ѽ¼\n־"}, {"strLogNullErr","{self}־¼ѽ"}, {"strLogUpSuccess","{self}־ϴ\n https://logpainter.kokona.tech/?s3={log_file} Բ鿴¼"}, - {"strLogUpFailure","ź{self}޷ɹϴ־ļȡϵMaster:{master_QQ}\nԭ {ret}"}, + {"strLogUpFailure","{self}ϴ־ļʧܣڵ{retry}ԡ{ret}"}, + {"strLogUpFailureEnd","ź{self}޷ɹϴ־ļ\n{ret}\nȡϵMaster:{master_QQ}\nļ:{log_file}"}, {"strGMTableShow","{self}¼{table_name}б"}, {"strGMTableNotExist","{self}ûб{table_name}¼"}, {"strUserTrustShow","{user}{self}μΪ{trust}"}, @@ -156,10 +158,13 @@ std::map GlobalMsg {"strDefaultCOCClr","Ĭϼ춨"}, {"strDefaultCOCNotFound","Ĭϼ춨治ڡ"}, {"strDefaultCOCSet","Ĭϼ춨:"}, - {"strLinkLoss","{self}ʱѶϿ"}, - {"strLinked","{self}Ѵʱš"}, - {"strLinkWarning","Դʱţ֤ܷͨ"}, - {"strLinkNotFound","ʱҪͨ򲻿״ĵطˡ"}, + {"strLinked","{self}Ϊӡ"}, + {"strLinkClose","{self}ѶϿӡ" }, + {"strLinkBusy","{nick}ĿѾж\n{self}ֶ֧߹ϵ" }, + {"strLinkedAlready","{self}ڱӡ\n{nick}ȶϾǰϵ" }, + {"strLinkingAlready","{self}Ѿ!" }, + {"strLinkCloseAlready","{self}Ͽʧܣ{nick}ǰûж" }, + {"strLinkNotFound","{self}Ҳ{nick}Ķ"}, {"strNotMaster","㲻{self}masterʲô"}, {"strNotAdmin","㲻{self}ĹԱ"}, {"strAdminDismiss","{strDismiss}"}, //ԱָȺĻִ @@ -393,7 +398,7 @@ const std::map HelpDoc = { .log on ʼ¼ .log off ͣ¼ .log end ɼ¼־ļ -)"}, +־ϴʧܿܣʱϵ̨ȡ)"}, {"deck","ָĬƶѣʹ.drawָƶʱʹôƶѡƶѲŻֱһźϴơ\n.deck set ƶ Ĭƶ\n.deck set 1-100 ָȵ\n.deck show 鿴ʣ࿨\n.deck reset ʣ࿨\n.deck new Զƶѣÿո|ָ޶\n.deck new е|޵|޵|޵|޵|޵\nshowȺڲҪȨ"}, {"Ⱥ","&dismiss"}, {"Ⱥָ","&dismiss"}, diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 2cf490bb..f21b825a 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -79,3 +79,27 @@ std::string to_binary(int b) } return res.dot("+").show(); } + +std::string ResList::show()const { + std::string s, strHead, strSepa; + unsigned int lenPage(0), cntPage(0); + if (intMaxLen > intLineLen || isLineBreak) { + strHead = "\n"; + strSepa = strLongSepa; + } + else { + strSepa = sDot; + } + for (auto it = vRes.begin(); it != vRes.end(); it++) { + //޺ҳ + if (lenPage > intPageLen) { + if (cntPage++ == 0)s = "\f[" + std::to_string(cntPage++) + "ҳ]" + (strHead.empty() ? "\n" : "") + s; + s += "\f[" + std::to_string(cntPage) + "ҳ]\n" + *it; + lenPage = 0; + } + else if (it == vRes.begin())s = strHead + *it; + else s += strSepa + *it; + lenPage += it->length(); + } + return s; +} \ No newline at end of file diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index 3c25b428..4bc529da 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -113,31 +113,7 @@ class ResList return *this; } - std::string show() - { - std::string s, strHead, strSepa; - unsigned int lenPage(0),cntPage(1); - if (intMaxLen > intLineLen || isLineBreak) - { - strHead = "\n"; - strSepa = strLongSepa; - } - else - { - strSepa = sDot; - } - for (auto it = vRes.begin(); it != vRes.end(); it++) { - //޺ҳ - if (lenPage > intPageLen) { - s += "\f[" + std::to_string(++cntPage) + "ҳ]\n" + *it; - lenPage = 0; - } - else if (it == vRes.begin())s = strHead + *it; - else s += strSepa + *it; - lenPage += it->length(); - } - return s; - } + std::string show()const; ResList& dot(string s) { diff --git a/Dice/RD.h b/Dice/RD.h index 3d58ab4c..403a4a69 100644 --- a/Dice/RD.h +++ b/Dice/RD.h @@ -482,15 +482,12 @@ class RD while (strDice.find("D-") != std::string::npos) strDice.insert(strDice.find("D-") + 1, std::to_string(defaultDice)); - while (strDice.find("D*") != std::string::npos) - strDice.insert(strDice.find("D-") + 1, + while (strDice.find("DX") != std::string::npos) + strDice.insert(strDice.find("DX") + 1, std::to_string(defaultDice)); while (strDice.find("D/") != std::string::npos) - strDice.insert(strDice.find("D-") + 1, + strDice.insert(strDice.find("D/") + 1, std::to_string(defaultDice)); - while (strDice.find("DX") != std::string::npos) - strDice.insert(strDice.find("DX") + 1, - std::to_string(defaultDice)); while (strDice.find("DK") != std::string::npos) strDice.insert(strDice.find("DK") + 1, std::to_string(defaultDice)); diff --git a/Dice/S3PutObject.cpp b/Dice/S3PutObject.cpp index 0b5617fe..de670288 100644 --- a/Dice/S3PutObject.cpp +++ b/Dice/S3PutObject.cpp @@ -17,6 +17,8 @@ // ֹWINAPIAWSSDKͻ #undef GetMessage +// Aws SDK +Aws::SDKOptions options; Aws::Auth::AWSCredentials awsCredentials("", ""); // жļǷ @@ -37,19 +39,16 @@ std::string put_s3_object(const Aws::String& s3_bucket_name, { // Verify file_name exists if (!file_exists(file_name)) { - return "־ļ!"; + return "ERROR: File Not Found"; } - // If region is specified, use it Aws::Client::ClientConfiguration clientConfig; - if (!region.empty()) + //if (!region.empty()) clientConfig.region = region; clientConfig.endpointOverride = "s3-accelerate.amazonaws.com"; - // Set up request Aws::S3::S3Client s3_client(awsCredentials, clientConfig); Aws::S3::Model::PutObjectRequest object_request; - object_request.SetBucket(s3_bucket_name); object_request.SetKey(s3_object_name); const std::shared_ptr input_data = @@ -57,7 +56,6 @@ std::string put_s3_object(const Aws::String& s3_bucket_name, file_name.c_str(), std::ios_base::in | std::ios_base::binary); object_request.SetBody(input_data); - // Put the object auto put_object_outcome = s3_client.PutObject(object_request); if (!put_object_outcome.IsSuccess()) { diff --git a/Dice/S3PutObject.h b/Dice/S3PutObject.h index 4ac1c950..2d4b6870 100644 --- a/Dice/S3PutObject.h +++ b/Dice/S3PutObject.h @@ -4,6 +4,7 @@ #include #include +extern Aws::SDKOptions options; // ϴļS3, S3-accelerate // ɹʱ"SUCCESS", 򷵻شϢ std::string put_s3_object(const Aws::String& s3_bucket_name, diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index 40d3224d..c7c51a28 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -59,7 +59,7 @@ string printDuringTime(long long seconds) else if (seconds < 60) { return std::to_string(seconds) + ""; } - int mins = seconds / 60; + int mins = int(seconds / 60); seconds = seconds % 60; if (mins < 60) { return std::to_string(mins) + "" + std::to_string(seconds) + ""; From 0748e491cbad72742510ad61191768d8ffd6f8ac Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 7 Aug 2020 20:24:28 +0800 Subject: [PATCH 021/171] fix blacklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改变常量{FormFeed}的意义,以满足现实需要 使用列表展示blackqq和blackgroup,以免长度根本不可能被发出去 --- Dice/BlackListManager.cpp | 10 ++++++++-- Dice/DiceEvent.cpp | 15 +++++++-------- Dice/GlobalVar.cpp | 2 ++ Dice/MsgFormat.cpp | 6 +++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index e14063cb..db138b59 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -1050,7 +1050,10 @@ void DDBlackManager::add_black_group(long long llgroup, FromMsg* msg) DDBlackMark mark{0, llgroup}; mark.danger = 1; mark.note = msg->strVar["note"]; - if (!mark.note.empty())mark.danger = 2; + if (!mark.note.empty()) { + mark.danger = 2; + mark.type = "other"; + } if (mark.danger < get_qq_danger(llgroup)) { msg->reply(GlobalMsg["strSelfName"] + "Ⱥ" + to_string(llgroup) + ""); @@ -1074,7 +1077,10 @@ void DDBlackManager::add_black_qq(long long llqq, FromMsg* msg) DDBlackMark mark{llqq, 0}; mark.danger = 1; mark.note = msg->strVar["note"]; - if (!mark.note.empty())mark.danger = 2; + if (!mark.note.empty()) { + mark.danger = 2; + mark.type = "other"; + } if (mark.danger < get_qq_danger(llqq)) { msg->reply(GlobalMsg["strSelfName"] + "û" + printQQ(llqq) + ""); diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 1e80db92..2a078696 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -543,12 +543,11 @@ int FromMsg::AdminEvent(const string& strOption) { if (llTargetID == 0) { - strReply = "ǰȺб"; - for (auto [each, danger] : blacklist->mGroupDanger) - { - strReply += "\n" + to_string(each); + ResList res; + for (auto [each, danger] : blacklist->mGroupDanger) { + res << printGroup(each) + ":" + to_string(danger); } - reply(); + reply(res.show(), false); return 1; } strVar["time"] = printSTNow(); @@ -621,12 +620,12 @@ int FromMsg::AdminEvent(const string& strOption) { if (llTargetID == 0) { - strReply = "ǰûб"; + ResList res; for (auto [each, danger] : blacklist->mQQDanger) { - strReply += "\n" + printQQ(each); + res << printQQ(each) + ":" + to_string(danger); } - reply(); + reply(res.show(), false); return 1; } strVar["time"] = printSTNow(); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 7c43d183..a6ac15cf 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -491,6 +491,8 @@ const std::map HelpDoc = { "draw", "ƣ.draw [ƶ] ([])\t//鵽ƲŻأܳƶ\nǰƶѣӲ/Ա/\nԱְҵ/Ա/Ӣ츳/ú//˼/Ҫ֮/Ҫ֮/Ƿ֮/֮/Աص/ʱ֢״/֢ܽ״/־֢״/֢״/\nӪ/޻ɫ/ðյ/\nżʾ/ż/żƬ/ż/\nAMGC/AMGC/AMGCר/AMGC/AMGCװ/AMGC/AMGCؼ1/AMGCؼ2/AMGCؼ3\n///ռ//ʥ/Ҫ/Сʮ/â/ʮ\n.helpλ+򣩿ɻȡƽ\nչƶ:{չƶ}" }, + { "չƶ","{list_extern_deck}" }, + { "ȫƶб","{list_all_deck}" }, {"ȹ", "&ri"}, {"ri", "ȹȺ޶.ri([ֵ])([dz])\n.ri -1 ijpc\t//Զȹб\n.ri +5 boss"}, {"ȹб", "&init"}, diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index f21b825a..3281cdbf 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -28,13 +28,13 @@ using std::string; std::map GlobalChar{ - {"FormFeed","\f"}, + {"FormFeed","\f"}, }; std::map strFuncs{ {"master_QQ",print_master}, - {"չƶ",list_extern_deck}, - {"ȫƶб",list_deck}, + {"list_extern_deck",list_extern_deck}, + {"list_all_deck",list_deck}, }; std::string format(std::string str, const std::initializer_list& replace_str) From 156ad80460a4eb140e621ecc5f41a743e46f95d8 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sun, 9 Aug 2020 16:26:27 +0800 Subject: [PATCH 022/171] Fix bug --- Dice/DiceConsole.cpp | 2 +- Dice/DiceSession.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index bf1192f2..1756415a 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -182,7 +182,7 @@ void Console::loadNotice() if (loadFile(DiceDir + "\\conf\\NoticeList.txt", NoticeList) < 1) { std::set sChat; - if (loadFile(static_cast(getAppDirectory()) + "MonitorList.RDconf", sChat) > 0) + if (loadFile(std::string(getAppDirectory()) + "MonitorList.RDconf", sChat) > 0) for (const auto& it : sChat) { console.setNotice(it, 0b100000); diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index bde8790d..3f006d2b 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -352,7 +352,7 @@ int DiceTableMaster::load() for (nlohmann::json::iterator itTable = j["tables"].begin(); itTable != j["tables"].end(); ++itTable) { string strTable = UTF8toGBK(itTable.key()); - for (nlohmann::json::iterator itItem = itTable.value().begin(); itItem != j.end(); ++itItem) + for (nlohmann::json::iterator itItem = itTable.value().begin(); itItem != itTable.value().end(); ++itItem) { pSession->mTable[strTable].emplace(UTF8toGBK(itItem.key()), itItem.value()); } From 9b469d2219c953522c0c5b479b20fd3b3dfa4c04 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sun, 9 Aug 2020 16:46:29 +0800 Subject: [PATCH 023/171] fix {pc} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重写了获取{pc}的方式 --- Dice/CharacterCard.cpp | 7 ++++--- Dice/CharacterCard.h | 4 +++- Dice/Dice.cpp | 1 + Dice/DiceEvent.cpp | 4 ++-- Dice/DiceJob.cpp | 8 ++++---- Dice/DiceSchedule.cpp | 1 + Dice/{app.json => com.w4123.dice.json} | 0 7 files changed, 15 insertions(+), 10 deletions(-) rename Dice/{app.json => com.w4123.dice.json} (100%) diff --git a/Dice/CharacterCard.cpp b/Dice/CharacterCard.cpp index 7981f6fe..d9ab233d 100644 --- a/Dice/CharacterCard.cpp +++ b/Dice/CharacterCard.cpp @@ -33,8 +33,9 @@ Player& getPlayer(long long qq) return PList[qq]; } -string getPCName(long long qq, long long group) +void getPCName(FromMsg& msg) { - if (PList.count(qq) && PList[qq][group].Name != "ɫ")return PList[qq][group].Name; - return "{nick}"; + msg["pc"] = (PList.count(msg.fromQQ) && PList[msg.fromQQ][msg.fromGroup].Name != "ɫ") + ? PList[msg.fromQQ][msg.fromGroup].Name + : msg["nick"]; } diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 9185090c..f503bc11 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -19,6 +19,8 @@ #include "ManagerSystem.h" #include "MsgFormat.h" #include "CardDeck.h" +#include "DiceEvent.h" + using std::string; using std::to_string; using std::vector; @@ -947,4 +949,4 @@ inline map PList; Player& getPlayer(long long qq); -string getPCName(long long qq, long long group); +void getPCName(FromMsg&); diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index d380aa5e..d71848ec 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -715,6 +715,7 @@ EVE_System_GroupMemberIncrease(eventGroupMemberIncrease) AddMsgToQueue(blacklist->list_self_qq_warning(beingOperateQQ), fromGroup, msgtype::Group); if (grp.isset(""))strNote += "Ⱥ壩"; else if (grp.isset(""))strNote += "Ⱥڣ"; + else if (grp.isset("ЭЧ"))strNote += "ȺЭЧ"; else if (getGroupMemberInfo(fromGroup, console.DiceMaid).permissions > 1)strNote += "ȺȨޣ"; else if (console["LeaveBlackQQ"]) { diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 2a078696..0d190b6b 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -752,7 +752,7 @@ int FromMsg::DiceReply() while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; strVar["nick"] = getName(fromQQ, fromGroup); - strVar["pc"] = getPCName(fromQQ, fromGroup); + getPCName(*this); strVar["at"] = intT ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions > 1 || pGrp->inviter == fromQQ; //ָƥ @@ -4182,7 +4182,7 @@ int FromMsg::CustomReply() if (strVar.empty()) { strVar["nick"] = getName(fromQQ, fromGroup); - strVar["pc"] = getPCName(fromQQ, fromGroup); + getPCName(*this); strVar["at"] = fromChat.second != msgtype::Private ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; } reply(CardDeck::drawCard(deck->second, true)); diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 51085149..3b6b102c 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -241,7 +241,7 @@ void clear_group(DiceJob& job) { std::mapstrVar; if (job.strVar["clear_mode"] == "unpower") { for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; if (grp.isGroup && getGroupMemberInfo(id, console.DiceMaid).permissions == 1) { res << printGroup(id); grp.leave(getMsg("strLeaveNoPower")); @@ -256,7 +256,7 @@ void clear_group(DiceJob& job) { string strDayLim = to_string(intDayLim); time_t tNow = time(NULL); for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; time_t tLast = grp.tLastMsg; if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0 && tLMT > tLast)tLast = tLMT; if (!tLast)continue; @@ -275,7 +275,7 @@ void clear_group(DiceJob& job) { try { for (auto& [id, grp_name] : getGroupList()) { Chat& grp = chat(id).group().name(grp_name); - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset(""))continue; + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("") || grp.isset("ЭЧ"))continue; if (blacklist->get_group_danger(id)) { res << printGroup(id) + "" + "Ⱥ"; if (console["LeaveBlackGroup"])grp.leave(getMsg("strBlackGroup")); @@ -314,7 +314,7 @@ void clear_group(DiceJob& job) { } else if (job["clear_mode"] == "preserve") { for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("ʹ") || grp.isset(""))continue; + if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("ʹ") || grp.isset("") || grp.isset("ЭЧ"))continue; if (grp.isGroup && getGroupMemberInfo(id, console.master()).permissions) { grp.set("ʹ"); continue; diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 5aa8e8cf..6790db6e 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -154,6 +154,7 @@ void DiceToday::daily_clear() { if (stToday.wDay != stNow.wDay) { stToday = stNow; cntGlobal.clear(); + cntUser.clear(); } } diff --git a/Dice/app.json b/Dice/com.w4123.dice.json similarity index 100% rename from Dice/app.json rename to Dice/com.w4123.dice.json From 48364a19c2ec536eb5fb4288d690052202db5084 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 10 Aug 2020 23:57:11 +0800 Subject: [PATCH 024/171] fix GroupMemberInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 允许邀请者视为拥有群权限 优化群成员是否获取成功的检查 --- Dice/BlackListManager.cpp | 17 +++++++++++------ Dice/Dice.cpp | 8 +++++++- Dice/DiceEvent.cpp | 2 +- Dice/ManagerSystem.cpp | 4 ++++ Dice/ManagerSystem.h | 5 +++-- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index db138b59..f6280633 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -46,22 +46,27 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) string strNotice; for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || !grp.isGroup)continue; - if (getGroupMemberInfo(id, llQQ).QQID == llQQ) + if (grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ") || !grp.isGroup)continue; + if (GroupMemberInfo member = getGroupMemberInfo(id, llQQ); member.QQID == llQQ) { strNotice = printGroup(id); - if (grp.isset("")) + if (grp.isset("ЭЧ")) { + strNotice += "ȺЭЧ"; + } + else if (grp.isset("")) { if (mark.isSource(console.DiceMaid) && !mark.isType("local"))sendGroupMsg(id, mark.warning()); strNotice += "Ⱥ"; } - else if (getGroupMemberInfo(id, llQQ).permissions < getGroupMemberInfo(id, getLoginQQ()).permissions) - { + else if (GroupMemberInfo self = getGroupMemberInfo(id, console.DiceMaid); !self.permissions) { + continue; + } + else if (member.permissions < self.permissions) { if (mark.isSource(console.DiceMaid && !mark.isType("local")))AddMsgToQueue( mark.warning(), id, msgtype::Group); strNotice += "ԷȺȨ޽ϵ"; } - else if (getGroupMemberInfo(id, llQQ).permissions > getGroupMemberInfo(id, getLoginQQ()).permissions) + else if (member.permissions > self.permissions) { sendGroupMsg(id, mark.warning()); grp.leave("Ա" + printQQ(llQQ) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index d71848ec..5c1944bd 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -474,7 +474,13 @@ bool eve_GroupAdd(Chat& grp) if (ChatList.size() == 1 && !console.isMasterMode)sendGroupMsg(grp.ID, msgInit); } GroupInfo ginf(grp.ID); - grp.Name = ginf.strGroupName; + //ȺϢǷȡɹ + if (ginf.llGroup) { + grp.Name = ginf.strGroupName; + } + else { + ginf.llGroup = grp.ID; + } if (grp.boolConf.empty() && ginf.nGroupSize > 499) { grp.set("ЭЧ"); } diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 0d190b6b..50654112 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -754,7 +754,7 @@ int FromMsg::DiceReply() strVar["nick"] = getName(fromQQ, fromGroup); getPCName(*this); strVar["at"] = intT ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; - isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions > 1 || pGrp->inviter == fromQQ; + isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions != 1 || pGrp->inviter == fromQQ; //ָƥ if (strLowerMessage.substr(intMsgCnt, 9) == "authorize") { diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index c47dbaf5..4b8d5748 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -144,6 +144,10 @@ Chat& Chat::id(long long grp) { return *this; } +bool Chat::is_except()const { + return boolConf.count("") || boolConf.count("ЭЧ"); +} + int groupset(long long id, string st) { if (!ChatList.count(id))return -1; diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index e912f1b0..9c9c9f8f 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -251,8 +251,7 @@ class Chat else CQ::sendDiscussMsg(ID, msg); Sleep(500); } - if (isGroup)CQ::setGroupLeave(ID); - else CQ::setDiscussLeave(ID); + isGroup ? CQ::setGroupLeave(ID) : CQ::setDiscussLeave(ID); set(""); } @@ -261,6 +260,8 @@ class Chat return boolConf.count(key) || intConf.count(key) || strConf.count(key); } + bool is_except()const; + void setConf(const string& key, int val) { intConf[key] = val; From cad96085a0b86561306391ded44eb44f608ef8a2 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 15 Aug 2020 16:15:00 +0800 Subject: [PATCH 025/171] update 566 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增help查询建议及记录 新增.groups 查看群 暂时移除DiceList 优化指令频度显示 移除部分菜单 --- Dice/BlackListManager.cpp | 23 +++--- Dice/BlackListManager.h | 2 +- Dice/Dice.cpp | 53 ++++++------- Dice/Dice.vcxproj | 6 +- Dice/Dice.vcxproj.filters | 12 +-- Dice/DiceConsole.cpp | 155 +------------------------------------- Dice/DiceConsole.h | 4 +- Dice/DiceEvent.cpp | 64 ++++++++++------ Dice/DiceEvent.h | 6 +- Dice/DiceJob.cpp | 56 +++++++++++--- Dice/DiceJob.h | 2 + Dice/DiceMod.cpp | 55 +++++++++++++- Dice/DiceMod.h | 9 ++- Dice/DiceSchedule.cpp | 31 +++++--- Dice/DiceSchedule.h | 17 +++-- Dice/DiceUpdate.cpp | 92 ---------------------- Dice/EncodingConvert.cpp | 1 + Dice/GlobalVar.cpp | 16 ++-- Dice/GlobalVar.h | 16 ++-- Dice/Jsonio.h | 37 +++------ Dice/ManagerSystem.cpp | 11 ++- Dice/ManagerSystem.h | 2 +- Dice/MsgFormat.cpp | 7 +- Dice/MsgFormat.h | 20 +++-- Dice/MsgMonitor.cpp | 47 ++++++++++++ Dice/MsgMonitor.h | 51 +------------ Dice/SHKQuerier.h | 100 ++++++++++++++++++++++++ Dice/com.w4123.dice.json | 20 +---- 28 files changed, 450 insertions(+), 465 deletions(-) delete mode 100644 Dice/DiceUpdate.cpp create mode 100644 Dice/SHKQuerier.h diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index f6280633..61e21eca 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -47,7 +47,7 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ") || !grp.isGroup)continue; - if (GroupMemberInfo member = getGroupMemberInfo(id, llQQ); member.QQID == llQQ) + if (GroupMemberInfo member = getGroupMemberInfo(id, llQQ); member.QQID == llQQ && member.Group == id) { strNotice = printGroup(id); if (grp.isset("ЭЧ")) @@ -61,6 +61,9 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) else if (GroupMemberInfo self = getGroupMemberInfo(id, console.DiceMaid); !self.permissions) { continue; } + else if (!member.permissions) { + strNotice += "ԷȺȨ޻ȡʧ"; + } else if (member.permissions < self.permissions) { if (mark.isSource(console.DiceMaid && !mark.isType("local")))AddMsgToQueue( mark.warning(), id, msgtype::Group); @@ -333,7 +336,7 @@ void DDBlackMark::fill_note() } else if (type == "spam") { - note = time + " " + (fromQQ.first ? printQQ(fromQQ.first) : "") + "" + printQQ(DiceMaid) + "ˢ"; + note = time + " " + (fromQQ.first ? printQQ(fromQQ.first) : "") + "" + printQQ(DiceMaid) + "Ƶָ"; } } @@ -411,6 +414,9 @@ void DDBlackMark::upload() } else if (isdigit(static_cast(temp[0])))wid = stoi(temp); else if (temp == "denied")erase(); + else if (temp.find("error") == 0) { + console.log("ϴ¼ܾ:" + temp, 0b10); + } } int DDBlackMark::check_cloud() @@ -586,7 +592,7 @@ DDBlackMark& DDBlackMark::operator<<(const DDBlackMark& mark) return *this; } -void DDBlackManager::insert(DDBlackMark& ex_mark) +bool DDBlackManager::insert(DDBlackMark& ex_mark) { std::lock_guard lock_queue(blacklistMutex); unsigned id = vBlackList.size(); @@ -637,6 +643,7 @@ void DDBlackManager::insert(DDBlackMark& ex_mark) } } if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + return !mark.isClear; } bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) @@ -856,6 +863,7 @@ bool DDBlackManager::up_group_danger(long long llgroup, DDBlackMark& mark) return false; } if (mGroupDanger.count(llgroup) && mGroupDanger[llgroup] >= mark.danger)return false; + mGroupDanger[llgroup] = mark.danger; if (Enabled) { if (ChatList.count(llgroup) && !chat(llgroup).isset(""))chat(llgroup).leave( @@ -863,7 +871,6 @@ bool DDBlackManager::up_group_danger(long long llgroup, DDBlackMark& mark) if(!isLoadingExtern)console.log(GlobalMsg["strSelfName"] + "ѽ" + printGroup(llgroup) + "Σյȼ" + to_string(mark.danger), 0b10, printSTNow()); } - mGroupDanger[llgroup] = mark.danger; return true; } @@ -877,6 +884,7 @@ bool DDBlackManager::up_qq_danger(long long llqq, DDBlackMark& mark) return false; } if (mQQDanger.count(llqq) && mQQDanger[llqq] >= mark.danger)return false; + mQQDanger[llqq] = mark.danger; if (Enabled && mark.danger > 1) { if (!mQQDanger.count(llqq) && UserList.count(llqq) && mark.danger == 2) @@ -891,7 +899,6 @@ bool DDBlackManager::up_qq_danger(long long llqq, DDBlackMark& mark) checkGroupWithBlackQQ(mark, llqq); } } - mQQDanger[llqq] = mark.danger; return true; } @@ -1265,7 +1272,7 @@ int DDBlackManager::loadJson(string strPath, bool isExtern) isLoadingExtern = true; if (int res = find(mark); res < 0) { - insert(mark); + if(insert(mark))cnt++; } else { @@ -1273,10 +1280,9 @@ int DDBlackManager::loadJson(string strPath, bool isExtern) if (mark.isSource(console.DiceMaid))continue; //漰IJ if (mark.danger != vBlackList[res].danger)continue; //ΣյȼرĶIJ } - update(mark, res); + if (update(mark, res))cnt++; } isLoadingExtern = false; - cnt++; } if (isExtern) { filesystem::remove(strPath); @@ -1288,7 +1294,6 @@ int DDBlackManager::loadJson(string strPath, bool isExtern) int DDBlackManager::loadHistory(const string& strLoc) { - console.log("ʼʼʷ", 0, printSTNow()); long long id; std::ifstream fgroup(strLoc + "BlackGroup.RDconf"); if (fgroup) diff --git a/Dice/BlackListManager.h b/Dice/BlackListManager.h index 35d83a43..0fd4da7a 100644 --- a/Dice/BlackListManager.h +++ b/Dice/BlackListManager.h @@ -89,7 +89,7 @@ class DDBlackManager //ָͬļ¼ int find(const DDBlackMark&); //¼¼ - void insert(DDBlackMark&); + bool insert(DDBlackMark&); bool update(DDBlackMark&, unsigned int, int); void reset_group_danger(long long); void reset_qq_danger(long long); diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 5c1944bd..504b0221 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -145,7 +145,7 @@ void dataInit() } } ifINIT.close(); - console.log("ʼԹȹ¼" + to_string(gm->mSession.size()) + "", 1); + if(gm->mSession.size())console.log("ʼԹȹ¼" + to_string(gm->mSession.size()) + "", 1); } today = make_unique(DiceDir + "/user/DiceToday.json"); } @@ -156,11 +156,6 @@ void dataBackUp() mkDir(DiceDir + "\\conf"); mkDir(DiceDir + "\\user"); mkDir(DiceDir + "\\audit"); - //濨 - saveJMap(strFileLoc + "GroupDeck.json", CardDeck::mGroupDeck); - saveJMap(strFileLoc + "GroupDeckTmp.json", CardDeck::mGroupDeckTmp); - saveJMap(strFileLoc + "PrivateDeck.json", CardDeck::mPrivateDeck); - saveJMap(strFileLoc + "PrivateDeckTmp.json", CardDeck::mPrivateDeckTmp); //б saveBFile(DiceDir + "\\user\\PlayerCards.RDconf", PList); saveFile(DiceDir + "\\user\\ChatList.txt", ChatList); @@ -176,32 +171,34 @@ EVE_Enable(eventEnable) GetModuleFileNameA(nullptr, path, MAX_PATH); std::string pathStr(path); strModulePath = pathStr; - pathStr = pathStr.substr(pathStr.rfind("\\") + 1); + string pathExe = pathStr.substr(pathStr.rfind("\\") + 1); std::transform(pathStr.begin(), pathStr.end(), pathStr.begin(), [](unsigned char c) { return tolower(c); }); - if (pathStr.substr(0, 4) == "java") + if (pathExe.substr(0, 4) == "java") { - Mirai = true; + frame = QQFrame::Mirai; Dice_Full_Ver_For = Dice_Full_Ver + " For Mirai]"; - char buffer[MAX_PATH]; - const DWORD length = GetModuleFileNameA(nullptr, buffer, sizeof buffer); - std::string pathSelf(buffer, length); - dirExe = pathSelf.substr(0, pathSelf.find("jre\\bin\\java.exe")); - DiceDir = "Dice" + to_string(getLoginQQ()); - filesystem::path pathDir(dirExe + DiceDir); - DiceDir = dirExe + DiceDir; + dirExe = pathStr.substr(0, pathStr.find("jre\\bin\\java.exe")); + this_thread::sleep_for(3s); //ȷMirai첽Ϣִ + } + else if (pathExe.substr(0, 4) == "") { + frame = QQFrame::XianQu; + dirExe = pathStr.substr(0, pathStr.find_last_of('\\') + 1); + } + { + DiceDir = dirExe + "Dice" + to_string(getLoginQQ()); + filesystem::path pathDir(DiceDir); if (!exists(pathDir)) { filesystem::path pathDirOld(dirExe + "DiceData"); if (exists(pathDirOld))rename(pathDirOld, pathDir); else filesystem::create_directory(pathDir); } - this_thread::sleep_for(3s); //ȷMirai첽Ϣִ } console.setPath(DiceDir + "\\conf\\Console.xml"); strFileLoc = getAppDirectory(); mkDir(strFileLoc); // MiraiԶļ console.DiceMaid = getLoginQQ(); GlobalMsg["strSelfName"] = getLoginNick(); - if (GlobalMsg["strSelfName"].empty() && Mirai) + if (GlobalMsg["strSelfName"].empty()) { GlobalMsg["strSelfName"] = "[" + toString(console.DiceMaid % 1000, 4) + "]"; } @@ -285,7 +282,7 @@ EVE_Enable(eventEnable) getUser(qq).create(NEWYEAR).trust(4); } if (console.master())getUser(console.master()).create(NEWYEAR).trust(5); - console.log("ʼû¼" + to_string(UserList.size()) + "", 1); + if (UserList.size())console.log("ʼû¼" + to_string(UserList.size()) + "", 1); } if (loadBFile(DiceDir + "\\user\\ChatConf.RDconf", ChatList) < 1) { @@ -390,7 +387,7 @@ EVE_Enable(eventEnable) chat(it.first).group().inviter = it.second; } } - console.log("ʼȺ¼" + to_string(ChatList.size()) + "", 1); + if(ChatList.size())console.log("ʼȺ¼" + to_string(ChatList.size()) + "", 1); } for (auto& [gid,gname] : getGroupList()) { @@ -401,8 +398,10 @@ EVE_Enable(eventEnable) { blacklist->loadJson(strFileLoc + "BlackMarks.json"); int cnt = blacklist->loadHistory(strFileLoc); - blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); - console.log("ʼ¼" + to_string(cnt) + "", 1); + if (cnt) { + blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + console.log("ʼ¼" + to_string(cnt) + "", 1); + } } else { blacklist->loadJson(DiceDir + "\\conf\\BlackListEx.json", true); @@ -439,11 +438,6 @@ EVE_Enable(eventEnable) { if (!UserList.count(pl.first))getUser(pl.first).create(NEWYEAR); } - //ȡ - loadJMap(strFileLoc + "GroupDeck.json", CardDeck::mGroupDeck); - loadJMap(strFileLoc + "GroupDeckTmp.json", CardDeck::mGroupDeckTmp); - loadJMap(strFileLoc + "PrivateDeck.json", CardDeck::mPrivateDeck); - loadJMap(strFileLoc + "PrivateDeckTmp.json", CardDeck::mPrivateDeckTmp); dataInit(); // ȷִ߳н while (msgSendThreadRunning)Sleep(10); @@ -469,7 +463,7 @@ bool eve_GroupAdd(Chat& grp) { { unique_lock lock_queue(GroupAddMutex); - if (grp.isset("δ") || grp.isset(""))grp.reset("δ").reset(""); + if (grp.lastmsg(time(nullptr)).isset("δ") || grp.isset(""))grp.reset("δ").reset(""); else return false; if (ChatList.size() == 1 && !console.isMasterMode)sendGroupMsg(grp.ID, msgInit); } @@ -733,7 +727,8 @@ EVE_System_GroupMemberIncrease(eventGroupMemberIncrease) } else { - return eve_GroupAdd(grp.set("δ")); + if (!grp.tLastMsg)grp.set("δ"); + return eve_GroupAdd(grp); } return 0; } diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index c7431611..adeeb0a7 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -157,7 +157,6 @@ - @@ -223,13 +222,14 @@ + - + @@ -251,7 +251,7 @@ - + diff --git a/Dice/Dice.vcxproj.filters b/Dice/Dice.vcxproj.filters index ef399fb4..487f3af4 100644 --- a/Dice/Dice.vcxproj.filters +++ b/Dice/Dice.vcxproj.filters @@ -72,9 +72,6 @@ 源文件 - - 源文件 - 源文件 @@ -290,13 +287,16 @@ 头文件 + + 头文件 + - - 资源文件 - + + 资源文件 + diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 1756415a..f51ba5a1 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -32,6 +32,7 @@ #include "DiceCloud.h" #include "Jsonio.h" #include "BlackListManager.h" +#include "DiceSchedule.h" using namespace std; using namespace CQ; @@ -302,9 +303,6 @@ std::string printSTime(const SYSTEMTIME st) //ȡб void getDiceList() { - std::string list; - if (Network::GET("shiki.stringempty.xyz", "/DiceList/", 80, list)) - readJson(list, mDiceList); } //ȡб void getExceptGroup() { @@ -359,8 +357,9 @@ bool operator<(const Console::Clock clock, const SYSTEMTIME& st) console.log(GlobalMsg["strSelfName"] + "ʱɡ", 1, printSTime(stTmp)); break; case ClockEvent::clear: - if (clearGroup("black")) - console.log(GlobalMsg["strSelfName"] + "ʱȺɡ", 1, printSTNow()); + sch.push_job("clrgroup", true, { + {"clear_mode","black"} + }); break; default: break; } @@ -370,152 +369,6 @@ bool operator<(const Console::Clock clock, const SYSTEMTIME& st) } } -//һ -int clearGroup(string strPara, long long fromQQ) -{ - int intCnt = 0; - string strReply; - ResList res; - std::map strVar; - if (strPara == "unpower" || strPara.empty()) - { - for (auto& [id, grp] : ChatList) - { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; - if (grp.isGroup && getGroupMemberInfo(id, console.DiceMaid).permissions == 1) - { - res << printGroup(id); - grp.leave(getMsg("strLeaveNoPower")); - intCnt++; - this_thread::sleep_for(3s); - } - } - strReply = GlobalMsg["strSelfName"] + "ɸȺȨȺ" + to_string(intCnt) + ":" + res.show(); - console.log(strReply, 0b10, printSTNow()); - } - else if (isdigit(static_cast(strPara[0]))) - { - const int intDayLim = stoi(strPara); - const string strDayLim = to_string(intDayLim); - const time_t tNow = time(nullptr); - for (auto& [id, grp] : ChatList) - { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset(""))continue; - time_t tLast = grp.tLastMsg; - if (grp.isGroup) - { - const int tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime; - if (tLMT > 0) - tLast = tLMT; - } - if (!tLast)continue; - const int intDay = static_cast(tNow - tLast) / 86400; - if (intDay > intDayLim) - { - strVar["day"] = to_string(intDay); - res << printGroup(id) + ":" + to_string(intDay) + "\n"; - grp.leave(getMsg("strLeaveUnused", strVar)); - intCnt++; - this_thread::sleep_for(2s); - } - } - strReply += GlobalMsg["strSelfName"] + "ɸDZˮ" + strDayLim + "Ⱥ" + to_string(intCnt) + "" + res.show(); - console.log(strReply, 0b10, printSTNow()); - } - else if (strPara == "black") - { - try - { - for (auto& [id, grp_name] : getGroupList()) - { - Chat& grp = chat(id).group().name(grp_name); - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset(""))continue - ; - if (blacklist->get_group_danger(id)) - { - res << printGroup(id) + "" + "Ⱥ"; - grp.leave(getMsg("strBlackGroup")); - } - vector MemberList = getGroupMemberList(id); - for (const auto& eachQQ : MemberList) - { - if (blacklist->get_qq_danger(eachQQ.QQID) > 1) - { - if (eachQQ.permissions < getGroupMemberInfo(id, getLoginQQ()).permissions) - { - continue; - } - if (eachQQ.permissions > getGroupMemberInfo(id, getLoginQQ()).permissions) - { - res << printChat(grp) + "" + printQQ(eachQQ.QQID) + "ԷȺȨ޽ϸ"; - grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); - intCnt++; - break; - } - if (console["LeaveBlackQQ"]) - { - res << printChat(grp) + "" + printQQ(eachQQ.QQID); - grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); - intCnt++; - break; - } - } - } - } - } - catch (...) - { - console.log(strReply, 0b10, "ѣ" + GlobalMsg["strSelfName"] + "Ⱥʱ"); - } - if (intCnt) - { - strReply = GlobalMsg["strSelfName"] + "ѰȺ" + to_string(intCnt) + "" + res.show(); - console.log(strReply, 0b10, printSTNow()); - } - else if (fromQQ) - { - console.log(strReply, 1, printSTNow()); - } - } - else if (strPara == "preserve") - { - for (auto& [id,grp] : ChatList) - { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("ʹ") || grp.isset(""))continue; - if (grp.isGroup && getGroupMemberInfo(id, console.master()).permissions) - { - grp.set("ʹ"); - continue; - } - res << printChat(grp); - grp.leave(getMsg("strPreserve")); - intCnt++; - this_thread::sleep_for(3s); - } - strReply = GlobalMsg["strSelfName"] + "ɸȺ" + to_string(intCnt) + "" + res.show(); - console.log(strReply, 1, printSTNow()); - } - else - AddMsgToQueue("޷ʶɸѡ", fromQQ); - return intCnt; -} - - -EVE_Menu(eventClearGroupUnpower) -{ - const int intGroupCnt = clearGroup("unpower"); - const string strReply = "ȨȺ" + to_string(intGroupCnt) + ""; - MessageBoxA(nullptr, strReply.c_str(), "һ", MB_OK | MB_ICONINFORMATION); - return 0; -} - -EVE_Menu(eventClearGroup30) -{ - const int intGroupCnt = clearGroup("30"); - const string strReply = "30δʹȺ" + to_string(intGroupCnt) + ""; - MessageBoxA(nullptr, strReply.c_str(), "һ", MB_OK | MB_ICONINFORMATION); - return 0; -} EVE_Menu(eventGlobalSwitch) { diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index 422b7039..dd9fffe3 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -34,6 +34,7 @@ class Console bool isMasterMode = false; long long masterQQ = 0; long long DiceMaid = 0; + bool is_self(long long qq)const { return masterQQ == qq || DiceMaid == qq; } friend void ConsoleTimer(); friend class FromMsg; friend class DiceJob; @@ -182,9 +183,6 @@ void getExceptGroup(); }; }; - //֪ͨ - //һ - extern int clearGroup(std::string strPara = "unpower", long long fromQQ = 0); //ʱ extern long long llStartTime; //ǰʱ diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 50654112..f1b90ad0 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -658,8 +658,9 @@ int FromMsg::MasterSet() } if (strOption == "groupclr") { - const std::string strPara = readRest(); - clearGroup(strPara, fromQQ); + strVar["clear_mode"] = readRest(); + cmd_key = "clrgroup"; + sch.push_job(*this); return 1; } if (strOption == "delete") @@ -983,7 +984,7 @@ int FromMsg::DiceReply() } return 1; } - if (isBotOff) + if (isDisabled || (!isCalled || !console["DisabledListenAt"]) && (groupset(fromGroup, "ָͣ") > 0)) { if (intT == PrivateT) { @@ -1038,16 +1039,16 @@ int FromMsg::DiceReply() intMsgCnt += 4; while (strLowerMessage[intMsgCnt] == ' ') intMsgCnt++; - const string strOption = readRest(); + strVar["help_word"] = readRest(); if (intT) { - if (!isAuth && (strOption == "on" || strOption == "off")) + if (!isAuth && (strVar["help_word"] == "on" || strVar["help_word"] == "off")) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } strVar["option"] = "help"; - if (strOption == "off") + if (strVar["help_word"] == "off") { if (groupset(fromGroup, strVar["option"]) < 1) { @@ -1060,7 +1061,7 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "on") + if (strVar["help_word"] == "on") { if (groupset(fromGroup, strVar["option"]) > 0) { @@ -1079,14 +1080,7 @@ int FromMsg::DiceReply() return 1; } } - if (strOption.empty()) - { - reply(string(Dice_Short_Ver) + "\n" + GlobalMsg["strHlpMsg"]); - } - else - { - reply(fmt->get_help(strOption)); - } + fmt->get_help(this); return true; } else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && @@ -1135,7 +1129,20 @@ int FromMsg::DiceReply() } return 1; } - if (strLowerMessage.substr(intMsgCnt, 6) == "setcoc") + else if (strLowerMessage.substr(intMsgCnt, 6) == "groups") { + if (trusted < 4) { + reply(GlobalMsg["strNotAdmin"]); + return 1; + } + intMsgCnt += 6; + string strOption = readPara(); + if (strOption == "list") { + strVar["list_mode"] = readPara(); + cmd_key = "lsgroup"; + sch.push_job(*this); + } + } + else if (strLowerMessage.substr(intMsgCnt, 6) == "setcoc") { if (!isAuth) { @@ -1184,7 +1191,7 @@ int FromMsg::DiceReply() else getUser(fromQQ).setConf("rc", intRule); return 1; } - if (strLowerMessage.substr(intMsgCnt, 6) == "system") + else if (strLowerMessage.substr(intMsgCnt, 6) == "system") { intMsgCnt += 6; if (trusted < 4) @@ -1223,9 +1230,9 @@ int FromMsg::DiceReply() } if (strOption == "clrimg") { - if (Mirai) + if (frame != QQFrame::CoolQ) { - reply("MiraiҪ˹"); + reply("ǿQܲҪ˹"); return -1; } if (trusted < 5) @@ -1244,7 +1251,7 @@ int FromMsg::DiceReply() reply(GlobalMsg["strNotMaster"]); return -1; } - cmd_key = Mirai ? "reload" : "remake"; + cmd_key = (frame == QQFrame::Mirai) ? "reload" : "remake"; sch.push_job(*this); return 1; } @@ -2419,7 +2426,14 @@ int FromMsg::DiceReply() if (!llTargetID) { reply(GlobalMsg["strQQIDEmpty"]); } - else blacklist->add_black_qq(llTargetID, this); + else if (trustedQQ(llTargetID) >= trusted) { + reply(GlobalMsg["strUserTrustDenied"]); + } + else { + blacklist->add_black_qq(llTargetID, this); + UserList.erase(llTargetID); + PList.erase(llTargetID); + } return 1; } if (strOption == "kill") @@ -4177,7 +4191,7 @@ int FromMsg::CustomReply() { const string strKey = readRest(); if (auto deck = CardDeck::mReplyDeck.find(strKey); deck != CardDeck::mReplyDeck.end() - || (!isBotOff && (deck = CardDeck::mReplyDeck.find(strMsg)) != CardDeck::mReplyDeck.end())) + || (!isDisabled && (deck = CardDeck::mReplyDeck.find(strMsg)) != CardDeck::mReplyDeck.end())) { if (strVar.empty()) { @@ -4228,7 +4242,7 @@ bool FromMsg::DiceFilter() trusted = trustedQQ(fromQQ); fwdMsg(); if (fromChat.second == msgtype::Private) isCalled = true; - isBotOff = (console["DisabledGlobal"] && (trusted < 4 || !isCalled)) || (!(isCalled && console["DisabledListenAt"]) && (groupset(fromGroup, "ָͣ") > 0)); + isDisabled = (console["DisabledGlobal"] && (trusted < 4 || !isCalled)) || (!(isCalled && console["DisabledListenAt"]) && groupset(fromGroup, "ЭЧ") > 0); if (DiceReply()) { if (isAns) @@ -4239,8 +4253,8 @@ bool FromMsg::DiceFilter() if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); return 1; } - if (groupset(fromGroup, "ûظ") < 1 && CustomReply())return true; - if (isBotOff)return console["DisabledBlock"]; + if (groupset(fromGroup, "ûظ") < 1 && !isDisabled && CustomReply())return true; + if (isDisabled)return console["DisabledBlock"]; return false; } diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 5c06529d..8771c308 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -33,9 +33,9 @@ class FromMsg :public DiceJobDetail { bool isBlock = false; - void reply(std::string strReply, bool isFormat); + void reply(std::string strReply, bool isFormat = true)override; - void reply(std::string strReply, const std::initializer_list replace_str = {}, + void reply(std::string strReply, const std::initializer_list replace_str, bool isFormat = true); void reply(); @@ -81,7 +81,7 @@ class FromMsg :public DiceJobDetail { //ǷӦ bool isAns = false; unsigned int intMsgCnt = 0; - bool isBotOff = false; + bool isDisabled = false; bool isCalled = false; bool isAuth = false; diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 3b6b102c..8e74f707 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -68,7 +68,7 @@ void cq_restart(DiceJob& job) { } BOOL bResult = Process32First(hProcessSnap, &pe32); int ppid(0); - if (Mirai) { + if (frame == QQFrame::Mirai) { char buffer[MAX_PATH]; const DWORD length = GetModuleFileNameA(nullptr, buffer, sizeof buffer); std::string pathSelf(buffer, length); @@ -104,7 +104,7 @@ void cq_restart(DiceJob& job) { return; } string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + to_string(console.DiceMaid); - if (Mirai) command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart " + dirExe + "MiraiOK.exe"; + if (frame == QQFrame::Mirai) command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart " + dirExe + "MiraiOK.exe"; ofstream fout("reload.bat"); fout << command << std::endl; fout.close(); @@ -138,7 +138,10 @@ void mirai_reload(DiceJob& job){ job.note("MiraiNativeʧܡ\nʹ˹ɻCQP.dll\n뱣֤汾MiraiNativeɾCQP.dll", 0b10); return; } - cq_reload(getAuthCode()); + if(cq_reload(getAuthCode())) + job.note("" + getMsg("self") + "ɡ", 1); + else + job.note("" + getMsg("self") + "ʧܡ", 0b10); FreeLibrary(hModule); } @@ -238,7 +241,6 @@ void clear_image(DiceJob& job) { void clear_group(DiceJob& job) { int intCnt = 0; ResList res; - std::mapstrVar; if (job.strVar["clear_mode"] == "unpower") { for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; @@ -257,14 +259,14 @@ void clear_group(DiceJob& job) { time_t tNow = time(NULL); for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; - time_t tLast = grp.tLastMsg; + time_t tLast = grp.tUpdated; if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0 && tLMT > tLast)tLast = tLMT; if (!tLast)continue; int intDay = (int)(tNow - tLast) / 86400; if (intDay > intDayLim) { - strVar["day"] = to_string(intDay); + job["day"] = to_string(intDay); res << printGroup(id) + ":" + to_string(intDay) + "\n"; - grp.leave(getMsg("strLeaveUnused", strVar)); + grp.leave(getMsg("strLeaveUnused", job.strVar)); intCnt++; this_thread::sleep_for(2s); } @@ -329,6 +331,40 @@ void clear_group(DiceJob& job) { else job.echo("޷ʶɸѡ"); } +void list_group(DiceJob& job) { + if (mChatConf.count(job["list_mode"])) { + ResList res; + for (auto& [id, grp] : ChatList) { + if (grp.isset(job["list_mode"])) { + res << printChat(grp); + } + } + job.reply("{self}" + job["list_mode"] + "Ⱥ¼" + to_string(res.size()) + "" + res.head(":").show()); + } + else if (job["list_mode"] == "idle") { + std::priority_queue> qDiver; + time_t tNow = time(NULL); + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("δ"))continue; + time_t tLast = grp.tUpdated; + if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0 && tLMT > tLast)tLast = tLMT; + if (!tLast)continue; + int intDay = (int)(tNow - tLast) / 86400; + qDiver.emplace(intDay, printGroup(id)); + } + if (qDiver.empty()) { + job.reply("{self}ȺĻȺϢʧܣ"); + } + size_t intCnt(0); + ResList res; + while (!qDiver.empty()) { + res << qDiver.top().second + to_string(qDiver.top().first) + ""; + qDiver.pop(); + if (++intCnt > 64 || intCnt > qDiver.size() || qDiver.top().first < 7)break; + } + job.reply("{self}Ⱥб:" + res.show()); + } +} // void cloud_beat(DiceJob& job) { @@ -338,8 +374,8 @@ void cloud_beat(DiceJob& job) { void dice_update(DiceJob& job) { job.note("ʼDice\n汾:" + job.strVar["ver"], 1); - if (Mirai) { - mkDir("plugins/MiraiNative/pluginsnew"); + if (frame == QQFrame::Mirai) { + mkDir(dirExe + "plugins/MiraiNative/pluginsnew"); char pathDll[] = "plugins/MiraiNative/pluginsnew/com.w4123.dice.dll"; char pathJson[] = "plugins/MiraiNative/pluginsnew/com.w4123.dice.json"; string urlDll("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "/com.w4123.dice.dll?" + to_string(job.fromTime)); @@ -401,6 +437,8 @@ void dice_cloudblack(DiceJob& job) { job.note("ͬƲ¼ɹ" + getMsg("self") + "ʼȡ", 1); blacklist->loadJson(DiceDir + "/conf/CloudBlackList.json", true); } + if (console["CloudBlackShare"]) + sch.add_job_for(24 * 60 * 60, "cloudblack"); } void log_put(DiceJob& job) { diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 40905c10..6117377e 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -16,6 +16,8 @@ void clear_image(DiceJob& job); void clear_group(DiceJob& job); +void list_group(DiceJob& job); + void cloud_beat(DiceJob& job); void dice_update(DiceJob& job); void dice_cloudblack(DiceJob& job); diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 72e2f24c..c4bdc6ab 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -59,8 +59,51 @@ string DiceModManager::get_help(const string& key) const return "{strHlpNotFound}"; } +struct help_sorter { + bool operator()(const string& _Left, const string& _Right) const { + if (fmt->cntHelp.count(_Right) && !fmt->cntHelp.count(_Left)) + return true; + else if (fmt->cntHelp.count(_Left) && !fmt->cntHelp.count(_Right)) + return false; + else if (fmt->cntHelp.count(_Left) && fmt->cntHelp.count(_Right) && fmt->cntHelp[_Left] != fmt->cntHelp[_Right]) + return fmt->cntHelp[_Left] < fmt->cntHelp[_Right]; + else if (_Left.length() != _Right.length()) { + return _Left.length() > _Right.length(); + } + else return _Left > _Right; + } +}; + +void DiceModManager::get_help(DiceJobDetail* job) { + if ((*job)["help_word"].empty()) { + job->reply(string(Dice_Short_Ver) + "\n" + GlobalMsg["strHlpMsg"]); + return; + } + else if (const auto it = helpdoc.find((*job)["help_word"]); it != helpdoc.end()) { + job->reply(format(it->second, helpdoc)); + } + else if (unordered_set keys = querier.search((*job)["help_word"]);!keys.empty()) { + std::priority_queue, help_sorter> qKey; + for (auto key : keys) { + qKey.emplace(key); + } + ResList res; + while (!qKey.empty()) { + res << qKey.top(); + qKey.pop(); + if (res.size() > 20)break; + } + (*job)["res"] = res.dot("/").show(1); + job->reply("{strHelpSuggestion}"); + } + else job->reply("{strHelpNotFound}"); + cntHelp[(*job)["help_word"]] += 1; + saveJMap(DiceDir + "\\user\\HelpStatic.json",cntHelp); +} + void DiceModManager::set_help(const string& key, const string& val) { + if (!helpdoc.count(key))querier.insert(key); helpdoc[key] = val; } @@ -108,9 +151,19 @@ int DiceModManager::load(string& strLog) strLog += it + "\n"; } } + std::thread factory(&DiceModManager::init,this); + factory.detach(); + cntHelp.reserve(helpdoc.size()); + loadJMap(DiceDir + "\\user\\HelpStatic.json", cntHelp); return cntFile; } - +void DiceModManager::init() { + isIniting = true; + for (auto& [key, word] : helpdoc) { + querier.insert(key); + } + isIniting = false; +} void DiceModManager::clear() { helpdoc.clear(); diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index 877b2504..52989af6 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -1,6 +1,6 @@ /* * Դģ - * Copyright (C) 2019 String.Empty + * Copyright (C) 2019-2020 String.Empty */ #pragma once #include @@ -9,6 +9,8 @@ #include #include #include "STLExtern.hpp" +#include "SHKQuerier.h" +#include "DiceSchedule.h" using std::string; using std::vector; using std::map; @@ -71,14 +73,19 @@ class DiceModManager { map mNameIndex; map helpdoc; + WordQuerier querier; public: DiceModManager(); friend void loadData(); + bool isIniting{ false }; string format(string, const map&, const char*) const; + unordered_mapcntHelp; [[nodiscard]] string get_help(const string&) const; + void get_help(DiceJobDetail*); void set_help(const string&, const string&); void rm_help(const string&); int load(string&); + void init(); void clear(); }; diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 6790db6e..1cb039a9 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -11,6 +11,8 @@ unordered_map mCommand = { {"syscheck",check_system}, {"autosave",auto_save}, {"clrimage",clear_image}, + {"clrgroup",clear_group}, + {"lsgroup",list_group}, {"reload",mirai_reload}, {"remake",cq_restart}, {"die",cq_exit}, @@ -20,6 +22,9 @@ unordered_map mCommand = { {"uplog",log_put} }; +DiceJobDetail::DiceJobDetail(const char* cmd, bool isFromSelf, unordered_map vars) :cmd_key(cmd), strVar(vars) { + if (isFromSelf)fromQQ = console.DiceMaid; +} void DiceJob::exec() { if (auto it = mCommand.find(cmd_key); it != mCommand.end()) { @@ -28,6 +33,7 @@ void DiceJob::exec() { else return; } void DiceJob::echo(const std::string& msg) { + if (!fromChat.first)return; switch (fromChat.second) { case CQ::msgtype::Private: CQ::sendPrivateMsg(fromQQ, msg); @@ -40,6 +46,9 @@ void DiceJob::echo(const std::string& msg) { break; } } +void DiceJob::reply(const std::string& msg) { + AddMsgToQueue(format(msg, GlobalMsg, strVar), fromChat); +} void DiceJob::note(const std::string& strMsg, int note_lv = 0b1) { ofstream fout(DiceDir + "/audit/log" + to_string(console.DiceMaid) + "_" + printDate() + ".txt", ios::out | ios::app); fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; @@ -90,6 +99,7 @@ void jobWait() { today->daily_clear(); } } + //ִж void DiceScheduler::push_job(const DiceJobDetail& job) { if (!Enabled)return; @@ -99,11 +109,11 @@ void DiceScheduler::push_job(const DiceJobDetail& job) { } cvJob.notify_one(); } -void DiceScheduler::push_job(const char* job_name) { +void DiceScheduler::push_job(const char* job_name, bool isSelf, unordered_mapvars) { if (!Enabled)return; { std::unique_lock lock_queue(mtQueueJob); - queueJob.emplace(job_name); + queueJob.emplace(job_name, isSelf, vars); } cvJob.notify_one(); } @@ -151,8 +161,8 @@ void DiceScheduler::end() { void DiceToday::daily_clear() { GetLocalTime(&stNow); - if (stToday.wDay != stNow.wDay) { - stToday = stNow; + if (stToday.tm_mday != stNow.wDay) { + stToday.tm_mday = stNow.wDay; cntGlobal.clear(); cntUser.clear(); } @@ -160,7 +170,7 @@ void DiceToday::daily_clear() { void DiceToday::save() { json jFile; - jFile["date"] = { stToday.wYear,stToday.wMonth,stToday.wDay }; + jFile["date"] = { stToday.tm_year + 1900,stToday.tm_mon + 1,stToday.tm_mday }; jFile["global"] = cntGlobal; jFile["user_cnt"] = cntUser; fwriteJson(pathFile, jFile); @@ -168,13 +178,16 @@ void DiceToday::save() { void DiceToday::load() { json jFile = freadJson(pathFile); if (jFile.is_null()) { - GetLocalTime(&stToday); + time_t tt = time(nullptr); + stToday = *localtime(&tt); return; } if (jFile.count("date")) { - jFile["date"][0].get_to(stToday.wYear); - jFile["date"][1].get_to(stToday.wMonth); - jFile["date"][2].get_to(stToday.wDay); + jFile["date"][0].get_to(stToday.tm_year); + stToday.tm_year -= 1900; + jFile["date"][1].get_to(stToday.tm_mon); + stToday.tm_mon -= 1; + jFile["date"][2].get_to(stToday.tm_mday); } if (jFile.count("global")) { jFile["global"].get_to(cntGlobal); } if (jFile.count("user_cnt")) { jFile["user_cnt"].get_to(cntUser); } diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index a00464ec..7777486c 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -6,11 +6,14 @@ #pragma once #include #include -#include "DiceConsole.h" +#include +#include "DiceMsgSend.h" #include "Jsonio.h" #include "json.hpp" + using std::string; using std::map; +using std::unordered_map; struct DiceJobDetail { long long fromQQ = 0; @@ -20,13 +23,12 @@ struct DiceJobDetail { time_t fromTime = time(nullptr); size_t cntExec{ 0 }; //ʱ - map strVar = {}; - DiceJobDetail(const char* cmd, bool isFromSelf = false):cmd_key(cmd){ - if(isFromSelf)fromQQ = console.DiceMaid; - } + unordered_map strVar = {}; + DiceJobDetail(const char* cmd, bool isFromSelf = false, unordered_map vars = {}); DiceJobDetail(long long qq, chatType ct, std::string msg = "", const char* cmd = "") :fromQQ(qq), fromChat(ct), strMsg(msg),cmd_key(cmd) { } + virtual void reply(string, bool = true) {} string& operator[](const char* key){ return strVar[key]; } @@ -42,6 +44,7 @@ class DiceJob : public DiceJobDetail { Renum ren = Renum::NIL; void exec(); void echo(const std::string&); + void reply(const std::string&); void note(const std::string&, int); }; @@ -52,7 +55,7 @@ class DiceScheduler { void start(); void end(); void push_job(const DiceJobDetail&); - void push_job(const char*); + void push_job(const char*, bool = false, unordered_map = {}); void add_job_for(unsigned int, const DiceJobDetail&); void add_job_for(unsigned int, const char*); void add_job_until(time_t, const DiceJobDetail&); @@ -66,7 +69,7 @@ typedef void (*cmd)(DiceJob&); //ռ¼ class DiceToday { - SYSTEMTIME stToday; + tm stToday; string pathFile; unordered_mapcntGlobal; unordered_map>cntUser; diff --git a/Dice/DiceUpdate.cpp b/Dice/DiceUpdate.cpp deleted file mode 100644 index 6171b1a6..00000000 --- a/Dice/DiceUpdate.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * _______ ________ ________ ________ __ - * | __ \ |__ __| | _____| | _____| | | - * | | | | | | | | | |_____ | | - * | | | | | | | | | _____| |__| - * | |__| | __| |__ | |_____ | |_____ __ - * |_______/ |________| |________| |________| |__| - * - * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2019 w4123 - * Copyright (C) 2019-2020 String.Empty - * - * This program is free software: you can redistribute it and/or modify it under the terms - * of the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include "CQEVE_ALL.h" -#include "EncodingConvert.h" -#include "DiceNetwork.h" -#include "GlobalVar.h" - - -EVE_Menu(eventDiceUpdate) -{ - std::string ver; - //if (!Network::GET("api.kokona.tech", "/getExpVer", 5555, ver) && !Network::GET("shiki.stringempty.xyz", "/DiceVer", 80, ver)) { - if (!Network::GET("shiki.stringempty.xyz", "/DiceVer", 80, ver)) - { - MessageBoxA(nullptr, ("ʱ: \n" + ver).c_str(), "Dice!´", MB_OK | MB_ICONWARNING); - return -1; - } - - ver = UTF8toGBK(ver); - const std::string serverBuild = ver.substr(ver.find('(') + 1, ver.find(')') - ver.find('(') - 1); - if (Dice_Build >= stoi(serverBuild)) - { - MessageBoxA(nullptr, "ʹõIJΪ°汾!", "Dice!", MB_OK | MB_ICONINFORMATION); - return 0; - } - - const std::string updateNotice = std::string("°汾! \n\nǰ汾: ") + std::string(Dice_Ver) + "\n°汾: " + ver + - "\n\n: ʹõDice!Ϊ޸İ, ˸»Ὣ串Ϊԭ!\nȷʼ, ȡȡ"; - - if (MessageBoxA(nullptr, updateNotice.c_str(), "Dice!", MB_OKCANCEL | MB_ICONINFORMATION) != IDOK) - { - MessageBoxA(nullptr, "ȡ", "Dice!", MB_OK | MB_ICONINFORMATION); - return 0; - } - - char buffer[MAX_PATH]; - const DWORD length = GetModuleFileNameA(nullptr, buffer, sizeof buffer); - - if (length == MAX_PATH || length == 0) - { - MessageBoxA(nullptr, "·ȡʧ!", "Dice!´", MB_OK | MB_ICONERROR); - return -1; - } - - std::string filePath(buffer, length); - filePath = filePath.substr(0, filePath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; - - std::string fileContent; - //if (!Network::GET("api.kokona.tech", "/getExpDice", 5555, fileContent) && !Network::GET("shiki.stringempty.xyz", "/download/com.w4123.dice.cpk", 80, fileContent)) - if (!Network::GET("shiki.stringempty.xyz", "/download/com.w4123.dice.cpk", 80, fileContent)) - { - MessageBoxA(nullptr, ("°汾ļʧ! ״̬! Ϣ: " + fileContent).c_str(), "Dice!´", MB_OK | MB_ICONERROR); - return -1; - } - - std::ofstream streamUpdate(filePath, std::ios::trunc | std::ios::binary | std::ios::out); - if (!streamUpdate) - { - MessageBoxA(nullptr, "ļʧ!", "Dice!´", MB_OK | MB_ICONERROR); - return -1; - } - - streamUpdate << fileContent; - streamUpdate.close(); - MessageBoxA(nullptr, "Dice!Ѹ, Ӧ!", "Dice!", MB_OK | MB_ICONINFORMATION); - return 0; -} diff --git a/Dice/EncodingConvert.cpp b/Dice/EncodingConvert.cpp index 7d3d6d8f..05d6b5f3 100644 --- a/Dice/EncodingConvert.cpp +++ b/Dice/EncodingConvert.cpp @@ -29,6 +29,7 @@ #include #include #include +#include // GBK std::string GBKtoUTF8(const std::string& strGBK) diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index a6ac15cf..33ccb731 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -25,11 +25,10 @@ #include "CQLogger.h" #include "GlobalVar.h" #include "MsgFormat.h" -#include bool Enabled = false; -bool Mirai = false; +QQFrame frame{ QQFrame::CoolQ }; std::string Dice_Full_Ver_For = Dice_Full_Ver + " For CoolQ]"; @@ -172,7 +171,8 @@ std::map GlobalMsg {"strHlpSet","Ϊ{key}ô"}, {"strHlpReset","{key}Ĵ"}, {"strHlpNameEmpty","MasterҪԶʲôѽ"}, - {"strHlpNotFound","{self}δҵָİϢ"}, + {"strHelpNotFound","{self}δҵΪ{help_word}Ĵ"}, + {"strHelpSuggestion","{self}{nick}Ҫҵ:{res}"}, {"strClockToWork","{self}Ѱʱá"}, {"strClockOffWork","{self}Ѱʱرա"}, {"strNameGenerator","{pc}ƣ{res}"}, @@ -332,6 +332,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +566:.helpѯ 565:.log־¼ 564:๦Żƶѵ 563:Żָ @@ -361,7 +362,9 @@ const std::map HelpDoc = { ָҪӲ.helpӦָ ȡϸϢ.help jrrp ָ: .dismiss Ⱥ -.bot +.bot 汾Ϣ +.bot on ָ +.bot off ָͣ .group Ⱥ .authorize Ȩ .send ̨Ϣ)" @@ -369,6 +372,7 @@ const std::map HelpDoc = { R"([ڶҳ]ָ .rules ٲ .r +.log ־¼ .ob Թģʽ .set Ĭ .coc COC @@ -404,7 +408,7 @@ const std::map HelpDoc = { {"Ⱥָ","&dismiss"}, {"dismiss","ָҪȺԱȨޣʹú˳Ⱥ\n!dismiss [ĿQQ(ĩλ)]ָȺ\n!dismissú;Ĭ״ֻ̬ҪЧ"}, {"Ȩ","&authoize"}, -{"authorize","Ȩ(ûʹʱתΪ)\n!authorize (+[Ⱥ]) ([])\nȺԭطͿʡȺţ޷ԶȨʱͬɷ"}, +{"authorize","Ȩ(ûʹʱתΪ)\n!authorize (+[Ⱥ]) ([])\nȺԭطͿʡȺţ޷ԶȨʱͬɷ\nĬϸʽΪ:!authorize ;:[ **д** ] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[ **дָ** ]úʹ[ **дָ** ]ͳȺ"}, {"","&bot"}, {"bot",".bot on/off/ĬӣȺ\n.botӾĬ״ֻ̬ҪҲںЧ"}, {"ٲ","&rules"}, @@ -581,7 +585,7 @@ const std::map HelpDoc = { {"λ", "δɡʧܡ׼㡢äĿܡһʱ˳;ϡǷϡ״̬ı̬ȲǢܴ졣"}, }; -std::string getMsg(const std::string& key, const std::map& maptmp) +std::string getMsg(const std::string& key, const std::unordered_map& maptmp) { const auto it = GlobalMsg.find(key); if (it != GlobalMsg.end())return format(it->second, GlobalMsg, maptmp); diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index f606da3b..6a48f58f 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -28,6 +28,7 @@ #include #include "CQLogger.h" #include +#include #include "STLExtern.hpp" /* @@ -35,11 +36,11 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 565u; -inline const std::string Dice_Ver_Without_Build = "2.4.1beta1"; -constexpr auto DiceRequestHeader = "Dice/2.4.0"; +const unsigned short Dice_Build = 566u; +inline const std::string Dice_Ver_Without_Build = "2.4.1beta2"; +constexpr auto DiceRequestHeader = "Dice/2.4.1"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; -inline const std::string Dice_Short_Ver = "Dice! by Shiki Ver " + Dice_Ver; +inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; #ifdef __clang__ @@ -70,8 +71,9 @@ extern HMODULE hDllModule; // ӦǷ extern bool Enabled; -// ǷMirai -extern bool Mirai; +// л +enum class QQFrame { CoolQ, Mirai, XianQu }; +extern QQFrame frame; // Diceİ汾ַ extern std::string Dice_Full_Ver_For; @@ -93,6 +95,6 @@ extern std::map EditedMsg; extern const std::map HelpDoc; // ޸ĺİĵ inline std::map CustomHelp; -std::string getMsg(const std::string& key, const std::map& tmp = {}); +std::string getMsg(const std::string& key, const std::unordered_map& tmp = {}); #endif /*DICE_GLOBAL_VAR*/ diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index e80dd14c..8f64fbe9 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -61,17 +61,15 @@ nlohmann::json freadJson(const std::string& strPath); nlohmann::json freadJson(const std::filesystem::path& path); void fwriteJson(std::string strPath, const json& j); -template -int readJMap(const nlohmann::json& j, std::map& mapTmp) +template +int readJMap(const nlohmann::json& j, Map& mapTmp) { int intCnt = 0; for (auto it = j.cbegin(); it != j.cend(); ++it) { - T1 tKey = readJKey(it.key()); - T2 tVal = it.value(); - - tVal = UTF8toGBK(tVal); - mapTmp[tKey] = tVal; + std::string key = UTF8toGBK(it.key()); + it.value().get_to(mapTmp[key]); + mapTmp[key] = UTF8toGBK(mapTmp[key]); intCnt++; } return intCnt; @@ -100,8 +98,8 @@ int readJson(const std::string& strJson, std::map& mapTmp) } } -template -int loadJMap(const std::string& strLoc, std::map &mapTmp) { +template +int loadJMap(const std::string& strLoc, Map& mapTmp) { nlohmann::json j = freadJson(strLoc); if (j.is_null())return -2; try @@ -114,29 +112,18 @@ int loadJMap(const std::string& strLoc, std::map &mapTmp) { } } -template -std::string writeJKey(std::enable_if_t, T> strJson) -{ - return GBKtoUTF8(strJson); -} - -template -std::string writeJKey(std::enable_if_t, T> llJson) -{ - return std::to_string(llJson); -} - -template -int saveJMap(const std::string& strLoc, std::map mapTmp) +//template +template +int saveJMap(const std::string& strLoc, const C& mapTmp) { if (mapTmp.empty())return 0; std::ofstream fout(strLoc); if (fout) { nlohmann::json j; - for (auto it : mapTmp) + for (auto& [key,val] : mapTmp) { - j[writeJKey(it.first)] = GBKtoUTF8(it.second); + j[GBKtoUTF8(key)] = GBKtoUTF8(val); } fout << j.dump(2); fout.close(); diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 4b8d5748..99c64f5a 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -16,7 +16,7 @@ string DiceDir = "DiceData"; unordered_set sReferencedImage; const map mChatConf{ - //0-ȺԱ2-23-34-Ա5-ϵͳ + //0-ȺԱ2-23-34-Ա5-ϵͳ {"", 4}, {"Ϣ", 0}, {"ָͣ", 0}, @@ -30,7 +30,7 @@ const map mChatConf{ {"δ", 1}, {"", 2}, {"", 4}, - {"ЭЧ", 4}, + {"ЭЧ", 3}, {"δ", 5}, {"", 5} }; @@ -133,14 +133,17 @@ Chat& chat(long long id) } Chat& Chat::id(long long grp) { ID = grp; - if (CQ::getGroupList().count(grp)) { - CQ::GroupInfo ginfo(grp); + if (CQ::GroupInfo ginfo(grp); ginfo.nGroupSize || CQ::getGroupList().count(grp)) { + Name = ginfo.strGroupName; isGroup = true; if (ExceptGroups.count(grp) || ginfo.nGroupSize > 499) { boolConf.insert("ЭЧ"); } } + else { + boolConf.insert("δ"); + } return *this; } diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index 9c9c9f8f..28d2f192 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -21,7 +21,7 @@ using std::vector; using std::unordered_map; constexpr auto CQ_IMAGE = "[CQ:image,file="; constexpr auto CQ_AT = "[CQ:at,qq="; -constexpr time_t NEWYEAR = 1593532800; +constexpr time_t NEWYEAR = 1596211200; // void loadData(); diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 3281cdbf..73231ac6 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -80,11 +80,12 @@ std::string to_binary(int b) return res.dot("+").show(); } -std::string ResList::show()const { +std::string ResList::show(size_t limPage)const { + if (empty())return {}; std::string s, strHead, strSepa; unsigned int lenPage(0), cntPage(0); if (intMaxLen > intLineLen || isLineBreak) { - strHead = "\n"; + strHead = sHead + "\n"; strSepa = strLongSepa; } else { @@ -93,6 +94,8 @@ std::string ResList::show()const { for (auto it = vRes.begin(); it != vRes.end(); it++) { //޺ҳ if (lenPage > intPageLen) { + if (limPage && limPage <= cntPage + 1) + return s; if (cntPage++ == 0)s = "\f[" + std::to_string(cntPage++) + "ҳ]" + (strHead.empty() ? "\n" : "") + s; s += "\f[" + std::to_string(cntPage) + "ҳ]\n" + *it; lenPage = 0; diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index 4bc529da..3c84593f 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -25,6 +25,7 @@ #define DICE_MSG_FORMAT #include #include +#include #include #include using std::string; @@ -37,7 +38,7 @@ std::string format(std::string str, const std::initializer_list std::string format(std::string s, const std::map& replace_str, - const std::map& str_tmp = {}) + const std::unordered_map& str_tmp = {}) { if (s[0] == '&') { @@ -47,9 +48,9 @@ std::string format(std::string s, const std::map { return format(it->second, replace_str, str_tmp); } - if ((it = str_tmp.find(key)) != str_tmp.end()) + if (auto uit = str_tmp.find(key); uit != str_tmp.end()) { - return it->second; + return uit->second; } } int l = 0, r = 0; @@ -70,10 +71,10 @@ std::string format(std::string s, const std::map { val = it->second; } - else if ((it = str_tmp.find(key)) != str_tmp.end()) + else if (auto uit = str_tmp.find(key); uit != str_tmp.end()) { - if (key == "res")val = format(it->second, replace_str, str_tmp); - else val = it->second; + if (key == "res")val = format(uit->second, replace_str, str_tmp); + else val = uit->second; } else if (auto func = strFuncs.find(key); func != strFuncs.end()) { @@ -93,6 +94,7 @@ class ResList bool isLineBreak = false; unsigned int intLineLen = 16; unsigned int intPageLen = 512; + string sHead = ""; string sDot = " "; string strLongSepa = "\n"; public: @@ -113,8 +115,12 @@ class ResList return *this; } - std::string show()const; + std::string show(size_t = 0)const; + ResList& head(string s) { + sHead = s; + return *this; + } ResList& dot(string s) { sDot = std::move(s); diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index c19177c0..5c39ae0d 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -10,6 +10,7 @@ std::atomic FrqMonitor::sumFrqTotal = 0; std::map FrqMonitor::mFrequence = {}; +std::map FrqMonitor::mCntOrder = {}; std::map FrqMonitor::mWarnLevel = {}; std::queue EarlyMsgQueue; @@ -72,6 +73,52 @@ void frqHandler() } } + +FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromTime(TT) { + if (mFrequence.count(fromQQ)) { + mFrequence[fromQQ] += 10; + mCntOrder[fromQQ] += 1; + if (!console["ListenSpam"] || trustedQQ(fromQQ) > 1 || console.is_self(QQ))return; + if (mFrequence[fromQQ] > 60 && mWarnLevel[fromQQ] < 60) { + mWarnLevel[fromQQ] = mFrequence[fromQQ]; + const std::string strMsg = "ѣ\n" + (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + + "⵽" + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + + (mCntOrder[fromQQ] > 18 ? "/5min" + : (mCntOrder[fromQQ] > 8 ? "/min" : "/30s")); + AddMsgToQueue(getMsg("strSpamFirstWarning"), CT); + console.log(strMsg, 1, printSTNow()); + } + else if (mFrequence[fromQQ] > 120 && mWarnLevel[fromQQ] < 120) { + mWarnLevel[fromQQ] = mFrequence[fromQQ]; + const std::string strMsg = "棺\n" + (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + + (mCntOrder[fromQQ] > 36 ? "/5min" + : (mCntOrder[fromQQ] > 15 ? "/min" : "/30s")); + console.log(strMsg, 0b10, printSTNow()); + } + else if (mFrequence[fromQQ] > 200 && mWarnLevel[fromQQ] < 200) { + mWarnLevel[fromQQ] = mFrequence[fromQQ]; + std::string strNow = printSTNow(); + std::string strNote = (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + + printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + + (mCntOrder[fromQQ] > 60 ? "/5min" + : (mCntOrder[fromQQ] > 25 ? "/min" : "/30s")); + if (mDiceList.count(fromQQ)) { + console.log(strNote, 0b1000, strNow); + } + else { + DDBlackMarkFactory mark{ fromQQ, 0 }; + mark.sign().type("spam").time(strNow).note(strNow + " " + strNote); + blacklist->create(mark.product()); + } + } + } + else { + mFrequence[fromQQ] = 10; + mWarnLevel[fromQQ] = 0; + } +} + int FrqMonitor::getFrqTotal() { return EarlyMsgQueue.size() + EarlierMsgQueue.size() / 2 + EarliestMsgQueue.size() / 10; diff --git a/Dice/MsgMonitor.h b/Dice/MsgMonitor.h index 5c38e44a..731d30ae 100644 --- a/Dice/MsgMonitor.h +++ b/Dice/MsgMonitor.h @@ -21,59 +21,13 @@ class FrqMonitor //Ƶʼ¼ static std::mapmFrequence; static std::mapmWarnLevel; - static std::mapmDailyFrq; + static std::mapmCntOrder; static std::atomic sumFrqTotal; static int getFrqTotal(); long long fromQQ = 0; time_t fromTime = 0; - FrqMonitor(long long QQ, time_t TT, chatType CT): fromQQ(QQ), fromTime(TT) - { - if (mFrequence.count(fromQQ)) - { - mFrequence[fromQQ] += 10; - if (!console["ListenSpam"] || trustedQQ(fromQQ) > 1)return; - if (mFrequence[fromQQ] > 60 && mWarnLevel[fromQQ] < 60) - { - mWarnLevel[fromQQ] = mFrequence[fromQQ]; - const std::string strMsg = "ѣ\n" + (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + - "⵽" + printQQ(fromQQ) + "ָƵȴﵽ" + std::to_string(mFrequence[fromQQ] / 10); - AddMsgToQueue(getMsg("strSpamFirstWarning"), CT.first, CT.second); - console.log(strMsg, 1, printSTNow()); - } - else if (mFrequence[fromQQ] > 120 && mWarnLevel[fromQQ] < 120) - { - mWarnLevel[fromQQ] = mFrequence[fromQQ]; - const std::string strMsg = "棺\n" + (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + - printQQ(fromQQ) + "ָƵȴﵽ" + std::to_string(mFrequence[fromQQ] / 10); - AddMsgToQueue(getMsg("strSpamFinalWarning"), CT.first, CT.second); - console.log(strMsg, 0b10, printSTNow()); - } - else if (mFrequence[fromQQ] > 200 && mWarnLevel[fromQQ] < 200) - { - mWarnLevel[fromQQ] = mFrequence[fromQQ]; - std::string strNow = printSTNow(); - std::string strNote = (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + - printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "30sָƵȴ" + std::to_string( - mFrequence[fromQQ] / 10); - if (mDiceList.count(fromQQ)) - { - console.log(strNote, 0b1000, strNow); - } - else - { - DDBlackMarkFactory mark{fromQQ, 0}; - mark.sign().type("spam").time(strNow).note(strNow + " " + strNote); - blacklist->create(mark.product()); - } - } - } - else - { - mFrequence[fromQQ] = 10; - mWarnLevel[fromQQ] = 0; - } - } + FrqMonitor(long long QQ, time_t TT, chatType CT); ~FrqMonitor() { @@ -85,6 +39,7 @@ class FrqMonitor if (time(nullptr) - fromTime > earliestTime) { mFrequence[fromQQ] -= 2; + mCntOrder[fromQQ] -= 1; delete this; return true; } diff --git a/Dice/SHKQuerier.h b/Dice/SHKQuerier.h new file mode 100644 index 00000000..2e190a3e --- /dev/null +++ b/Dice/SHKQuerier.h @@ -0,0 +1,100 @@ +/* + * ѯ + * ڵʵֶԴѯƥ + * Copyright (C) 2020 String.Empty + */ +#pragma once +#include +#include +#include +#include +using std::string; +using std::vector; +using std::unordered_map; +using std::unordered_set; + +struct WordNode { + //ýڵķִʣ֡ĸGBK + string word; + unordered_setkeys; + unordered_map>next; +}; + +class WordQuerier { + unordered_map word_list; +public: + static vector cutter(const string& title) { + static const char* dot{ "~!@#$%^&*()-=`_+[]\\{}|;':\",./<>?" }; + vector res; + string word; + for (size_t pos = 0; pos < title.length(); pos++) { + if (title[pos] < 0) { + if (!word.empty())res.push_back(word); + res.push_back(title.substr(pos++, 2)); + word.clear(); + } + else if (isspace(title[pos]) || strchr(dot, title[pos])) { + if (!word.empty()) { + res.push_back(word); + word.clear(); + } + } + else { + word += tolower(title[pos]); + } + } + if (!word.empty())res.push_back(word); + return res; + } + void insert(const string& key) { + vector words = cutter(key); + if (words.empty())return; + word_list[words[0]].keys.insert(key); + size_t idx(1); + while (idx < words.size()) { + word_list[words[idx - 1]].next[words[idx]].insert(key); + word_list[words[idx++]].keys.insert(key); + } + } + unordered_set search(const string& key)const{ + vector words = cutter(key); + if (words.empty())return{}; + unordered_set res; + unordered_set sInter; + const WordNode* last_word = nullptr; + for (auto& word : words) { + //޸ôʣԹ + if (!word_list.count(word))continue; + //׺ + if (last_word && !last_word->next.find(word)->second.empty()) { + for (auto& w : last_word->next.find(word)->second) { + if (res.count(w))sInter.insert(w); + } + if (!sInter.empty()) { + res = sInter; + sInter.clear(); + last_word = &word_list.find(word)->second; + continue; + } + } + // + if (res.empty()) { + res = word_list.find(word)->second.keys; + last_word = &word_list.find(word)->second; + continue; + } + for (auto& w : word_list.find(word)->second.keys) { + if (res.count(w))sInter.insert(w); + } + if (!sInter.empty()) { + res = sInter; + sInter.clear(); + last_word = &word_list.find(word)->second; + } + } + return res; + } + void clear() { + word_list.clear(); + } +}; \ No newline at end of file diff --git a/Dice/com.w4123.dice.json b/Dice/com.w4123.dice.json index f2ad4dc9..58beaa5e 100644 --- a/Dice/com.w4123.dice.json +++ b/Dice/com.w4123.dice.json @@ -2,10 +2,10 @@ "ret": 1, "apiver": 9, "name": "Dice!", - "version": "2.4.0", - "version_id": 564, - "author": "w4123溯洄 Shiki", - "description": "跑团用骰子 本程序使用AGPLv3开源协议授权 Copyright (c) 2018-2019 w4123溯洄 Shiki", + "version": "2.4.1", + "version_id": 566, + "author": "w4123溯洄 & Shiki", + "description": "跑团用骰子 本程序使用AGPLv3开源协议授权 Copyright (c) 2018-2020 w4123溯洄 & Shiki", "event": [ { "id": 1, @@ -93,22 +93,10 @@ } ], "menu": [ - { - "name": "更新Dice!", - "function": "eventDiceUpdate" - }, { "name": "Master模式切换", "function": "eventMasterMode" }, - { - "name": "清退非管理群聊", - "function": "eventClearGroupUnpower" - }, - { - "name": "清退不活跃群聊", - "function": "eventClearGroup30" - }, { "name": "全局开关", "function": "eventGlobalSwitch" From 7d48197c0605e9d3e9776d8a9218c1979a101ad1 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 21 Aug 2020 23:49:14 +0800 Subject: [PATCH 026/171] update help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化.help查询建议 添加.help link 优化ResList字长统计 --- Dice/Dice.cpp | 1 + Dice/DiceEvent.cpp | 3 +++ Dice/DiceJob.cpp | 38 ++++++++++++++++++++++++++++++++------ Dice/DiceMod.cpp | 4 ++-- Dice/DiceMsgSend.cpp | 3 +++ Dice/GlobalVar.cpp | 19 +++++++++++++++---- Dice/ManagerSystem.cpp | 2 +- Dice/MsgFormat.cpp | 15 +++++++++++---- Dice/MsgFormat.h | 13 +++---------- Dice/StrExtern.hpp | 2 ++ Dice/strExtern.cpp | 3 +++ 11 files changed, 76 insertions(+), 27 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 504b0221..c34bf740 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -182,6 +182,7 @@ EVE_Enable(eventEnable) } else if (pathExe.substr(0, 4) == "") { frame = QQFrame::XianQu; + Dice_Full_Ver_For = Dice_Full_Ver + " For CQXQ]"; dirExe = pathStr.substr(0, pathStr.find_last_of('\\') + 1); } { diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index f1b90ad0..0934e7e9 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -2271,6 +2271,9 @@ int FromMsg::DiceReply() else if (strVar["option"] == "with" || strVar["option"] == "from" || strVar["option"] == "to") { gm->session(fromSession).link_new(this); } + else { + reply(fmt->get_help("link")); + } return 1; } else if (strLowerMessage.substr(intMsgCnt, 4) == "name") diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 8e74f707..3cd1c2c4 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -69,13 +69,14 @@ void cq_restart(DiceJob& job) { BOOL bResult = Process32First(hProcessSnap, &pe32); int ppid(0); if (frame == QQFrame::Mirai) { + strSelfName = "MiraiOK.exe"; char buffer[MAX_PATH]; const DWORD length = GetModuleFileNameA(nullptr, buffer, sizeof buffer); std::string pathSelf(buffer, length); - pathSelf = pathSelf.substr(0, pathSelf.find("jre\\bin\\java.exe")) + "MiraiOK.exe"; + pathSelf = pathSelf.substr(0, pathSelf.find("jre\\bin\\java.exe")) + strSelfName; char pathFull[MAX_PATH]; while (bResult) { - if (strcmp(pe32.szExeFile, "MiraiOK.exe") == 0) { + if (strSelfName == pe32.szExeFile) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); GetModuleFileNameEx(hProcess, NULL, pathFull, sizeof(pathFull)); if (pathSelf != pathFull)continue; @@ -85,6 +86,10 @@ void cq_restart(DiceJob& job) { } bResult = Process32Next(hProcessSnap, &pe32); } + if (!ppid) { + job.echo("δҵ" + pathSelf); + return; + } } else { int pid = _getpid(); @@ -103,9 +108,9 @@ void cq_restart(DiceJob& job) { job.note("ʧܣδҵ̣", 1); return; } - string command = "taskkill /f /pid " + to_string(ppid) + "\nstart .\\" + strSelfName + " /account " + to_string(console.DiceMaid); - if (frame == QQFrame::Mirai) command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart " + dirExe + "MiraiOK.exe"; - ofstream fout("reload.bat"); + string command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart .\\" + strSelfName; + if (frame == QQFrame::CoolQ) command += " /account " + to_string(console.DiceMaid); + ofstream fout("remake.bat"); fout << command << std::endl; fout.close(); job.note(command, 0); @@ -113,7 +118,7 @@ void cq_restart(DiceJob& job) { Enabled = false; dataBackUp(); std::this_thread::sleep_for(3s); - switch (UINT res = -1; res = WinExec(".\\reload.bat", SW_SHOW)) { + switch (UINT res = -1; res = WinExec(".\\remake.bat", SW_SHOW)) { case 0: job.note("ʧܣڴԴѺľ", 1); break; @@ -364,6 +369,27 @@ void list_group(DiceJob& job) { } job.reply("{self}Ⱥб:" + res.show()); } + else if (job["list_mode"] == "size") { + std::priority_queue> qSize; + time_t tNow = time(NULL); + for (auto& [id, grp] : ChatList) { + if (grp.isset("") || grp.isset("δ") || !grp.isGroup)continue; + GroupInfo ginfo(id); + if (!ginfo.nGroupSize)continue; + qSize.emplace(ginfo.nGroupSize, printGroup(id)); + } + if (qSize.empty()) { + job.reply("{self}ȺĻȺϢʧܣ"); + } + size_t intCnt(0); + ResList res; + while (!qSize.empty()) { + res << qSize.top().second + "[" + to_string(qSize.top().first) + "]"; + qSize.pop(); + if (++intCnt > 64 || intCnt > qSize.size() || qSize.top().first < 7)break; + } + job.reply("{self}ڴȺб:" + res.show(1)); + } } // diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index c4bdc6ab..2ab3e9bf 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -56,7 +56,7 @@ string DiceModManager::get_help(const string& key) const { return format(it->second, helpdoc); } - return "{strHlpNotFound}"; + return "{strHelpNotFound}"; } struct help_sorter { @@ -85,7 +85,7 @@ void DiceModManager::get_help(DiceJobDetail* job) { else if (unordered_set keys = querier.search((*job)["help_word"]);!keys.empty()) { std::priority_queue, help_sorter> qKey; for (auto key : keys) { - qKey.emplace(key); + qKey.emplace(".help " + key); } ResList res; while (!qKey.empty()) { diff --git a/Dice/DiceMsgSend.cpp b/Dice/DiceMsgSend.cpp index f387ee47..b0739374 100644 --- a/Dice/DiceMsgSend.cpp +++ b/Dice/DiceMsgSend.cpp @@ -80,6 +80,9 @@ void SendMsg() } if (!msg.msg.empty()) { + if (int pos = msg.msg.find_first_not_of(" \t\r\n"); pos && pos != string::npos) { + msg.msg = msg.msg.substr(pos); + } if (int pos = msg.msg.find('\f'); pos != string::npos) { AddMsgToQueue(msg.msg.substr(pos + 1), msg.target_id, msg.msg_type); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 33ccb731..c8280f06 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -171,7 +171,7 @@ std::map GlobalMsg {"strHlpSet","Ϊ{key}ô"}, {"strHlpReset","{key}Ĵ"}, {"strHlpNameEmpty","MasterҪԶʲôѽ"}, - {"strHelpNotFound","{self}δҵΪ{help_word}Ĵ"}, + {"strHelpNotFound","{self}δҵ{help_word}صĴ"}, {"strHelpSuggestion","{self}{nick}Ҫҵ:{res}"}, {"strClockToWork","{self}Ѱʱá"}, {"strClockOffWork","{self}Ѱʱرա"}, @@ -292,7 +292,7 @@ std::map GlobalMsg {"strAddFriendWhiteQQ", "{strAddFriend}"}, //ûӺʱظ˾ { "strAddFriend", - R"(ӭʹ{strSelfName} + R"(ӭѡ{strSelfName} .helpЭ ȷϷЭ .helpָ 鿴ָб .help趨 ȷ趨 @@ -301,7 +301,7 @@ std::map GlobalMsg }, //ͬӺʱⷢ͵ { "strAddGroup", - R"(ӭʹ{strSelfName} + R"(ӭѡ{strSelfName} ʹ.dismiss QQţλ ʹ{self}Ⱥ .bot on/off QQţλ //رָ .group +/-ûظ //ûûظ @@ -354,8 +354,9 @@ const std::map HelpDoc = { 537:.send)"}, {"Э","0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, {"","Dice!̳: https://kokona.tech \n Dice!̳: https://forum.kokona.tech"}, -{"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ\n{}\nûȺ:{ûȺ}\nٷ(ˮ)Ⱥ: 882747577\n˽Ⱥ863062599 192499947\nȺ1029435374"}, +{"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ{}\n{}\nûȺ:{ûȺ}\nٷ(ˮ)Ⱥ: 882747577\n˽Ⱥ863062599 192499947\nȺ1029435374"}, {"ûȺ","δá"}, +{"",""}, {"","δá"}, {"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, {"ָ",R"(atָָﵥӦat.bot off @@ -534,7 +535,17 @@ const std::map HelpDoc = { :.group +ûظ //رձȺԶظ Ⱥܴ:ָͣ/ûظ/jrrp/draw/me/help/ob/Ϣ/ʹ//)" }, + {"Ϣ","&link"}, + {"link",R"(Ϣ.link +.link [ת] [󴰿] 󴰿ڵת +.link close ر +.link start ϴιرյ +[ת]:to=תϢ󴰿;from=ת󴰿Ϣ;with=˫ת +[󴰿]:Ⱥ/=[Ⱥ];˽Ĵ=q[QQ] +:.link with q1605271653 //˫˽ +.link from 754494359 //ĿȺϢת)"}, {"", "ǧ֮ĸó֮˫׶Եļ໤ˣһйصĿߣףʱ̨ߣת֮"}, + {"Ͷι","&Ͷʳ"}, {"Ͷʳ", "ͶʳShikiѡhttps://afdian.net/@dice_shiki\nͶʳ䧣ѡhttps://afdian.net/@suhuiw4123\nͶʳ{self}ѡ񡭡䳬᣿"}, {"λ", "ȻĵطĿĵǰСϲսĴˡõ롣"}, { diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 99c64f5a..c5a2348d 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -133,7 +133,7 @@ Chat& chat(long long id) } Chat& Chat::id(long long grp) { ID = grp; - if (CQ::GroupInfo ginfo(grp); ginfo.nGroupSize || CQ::getGroupList().count(grp)) { + if (CQ::GroupInfo ginfo(grp); ginfo.llGroup || CQ::getGroupList().count(grp)) { Name = ginfo.strGroupName; isGroup = true; diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 73231ac6..5a781d81 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -25,6 +25,7 @@ #include // #include "DiceJob.h" #include "EncodingConvert.h" +#include "StrExtern.hpp" using std::string; std::map GlobalChar{ @@ -80,10 +81,18 @@ std::string to_binary(int b) return res.dot("+").show(); } +unsigned int ResList::intPageLen = 255; +ResList& ResList::operator<<(std::string s) { + while (isspace(static_cast(s[0])))s.erase(s.begin()); + if (s.empty())return *this; + vRes.push_back(s); + if (size_t len = wstrlen(s.c_str());len > intMaxLen)intMaxLen = len; + return *this; +} std::string ResList::show(size_t limPage)const { if (empty())return {}; std::string s, strHead, strSepa; - unsigned int lenPage(0), cntPage(0); + unsigned int cntPage(0); if (intMaxLen > intLineLen || isLineBreak) { strHead = sHead + "\n"; strSepa = strLongSepa; @@ -93,16 +102,14 @@ std::string ResList::show(size_t limPage)const { } for (auto it = vRes.begin(); it != vRes.end(); it++) { //޺ҳ - if (lenPage > intPageLen) { + if (wstrlen(s.c_str()) + wstrlen(it->c_str()) > intPageLen && !s.empty()) { if (limPage && limPage <= cntPage + 1) return s; if (cntPage++ == 0)s = "\f[" + std::to_string(cntPage++) + "ҳ]" + (strHead.empty() ? "\n" : "") + s; s += "\f[" + std::to_string(cntPage) + "ҳ]\n" + *it; - lenPage = 0; } else if (it == vRes.begin())s = strHead + *it; else s += strSepa + *it; - lenPage += it->length(); } return s; } \ No newline at end of file diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index 3c84593f..c00f7888 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -92,8 +92,8 @@ class ResList std::vector vRes; unsigned int intMaxLen = 0; bool isLineBreak = false; - unsigned int intLineLen = 16; - unsigned int intPageLen = 512; + unsigned int intLineLen = 10; + static unsigned int intPageLen; string sHead = ""; string sDot = " "; string strLongSepa = "\n"; @@ -106,14 +106,7 @@ class ResList intMaxLen = s.length(); } - ResList& operator<<(std::string s) - { - while (isspace(static_cast(s[0])))s.erase(s.begin()); - if (s.empty())return *this; - vRes.push_back(s); - if (s.length() > intMaxLen)intMaxLen = s.length(); - return *this; - } + ResList& operator<<(std::string s); std::string show(size_t = 0)const; diff --git a/Dice/StrExtern.hpp b/Dice/StrExtern.hpp index 115bab34..11fbe3c4 100644 --- a/Dice/StrExtern.hpp +++ b/Dice/StrExtern.hpp @@ -41,3 +41,5 @@ string convert_w2a(const wchar_t* wch); wstring convert_a2w(const char* ch); string printDuringTime(long long); + +size_t wstrlen(const char*); diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index c7c51a28..a1cfc6ec 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -50,6 +50,9 @@ wstring convert_a2w(const char* ch) delete[] m_char; return wstr; } +size_t wstrlen(const char* ch) { + return MultiByteToWideChar(CP_GBK, 0, ch, -1, nullptr, 0); +} string printDuringTime(long long seconds) { From dea7bbc6235d0ede03fdd8f6cc00478ad04f86f3 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 29 Aug 2020 10:25:53 +0800 Subject: [PATCH 027/171] fix ResList MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复分页bug --- Dice/Dice.cpp | 5 +++-- Dice/MsgFormat.cpp | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index c34bf740..8b76decb 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -184,9 +184,11 @@ EVE_Enable(eventEnable) frame = QQFrame::XianQu; Dice_Full_Ver_For = Dice_Full_Ver + " For CQXQ]"; dirExe = pathStr.substr(0, pathStr.find_last_of('\\') + 1); + this_thread::sleep_for(3s); } + if (console.DiceMaid = getLoginQQ()) { - DiceDir = dirExe + "Dice" + to_string(getLoginQQ()); + DiceDir = dirExe + "Dice" + to_string(console.DiceMaid); filesystem::path pathDir(DiceDir); if (!exists(pathDir)) { filesystem::path pathDirOld(dirExe + "DiceData"); @@ -197,7 +199,6 @@ EVE_Enable(eventEnable) console.setPath(DiceDir + "\\conf\\Console.xml"); strFileLoc = getAppDirectory(); mkDir(strFileLoc); // MiraiԶļ - console.DiceMaid = getLoginQQ(); GlobalMsg["strSelfName"] = getLoginNick(); if (GlobalMsg["strSelfName"].empty()) { diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 5a781d81..aeaa9efd 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -92,7 +92,7 @@ ResList& ResList::operator<<(std::string s) { std::string ResList::show(size_t limPage)const { if (empty())return {}; std::string s, strHead, strSepa; - unsigned int cntPage(0); + unsigned int lenPage(0), cntPage(0), lenItem(0); if (intMaxLen > intLineLen || isLineBreak) { strHead = sHead + "\n"; strSepa = strLongSepa; @@ -101,15 +101,18 @@ std::string ResList::show(size_t limPage)const { strSepa = sDot; } for (auto it = vRes.begin(); it != vRes.end(); it++) { + lenItem = wstrlen(it->c_str()); //޺ҳ - if (wstrlen(s.c_str()) + wstrlen(it->c_str()) > intPageLen && !s.empty()) { + if (lenPage + lenItem > intPageLen && !s.empty()) { if (limPage && limPage <= cntPage + 1) return s; if (cntPage++ == 0)s = "\f[" + std::to_string(cntPage++) + "ҳ]" + (strHead.empty() ? "\n" : "") + s; s += "\f[" + std::to_string(cntPage) + "ҳ]\n" + *it; + lenPage = 0; } else if (it == vRes.begin())s = strHead + *it; else s += strSepa + *it; + lenPage += lenItem; } return s; } \ No newline at end of file From 0bf8790eb4fe56c76cc44b43dece1603337a7418 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 1 Sep 2020 22:06:19 +0800 Subject: [PATCH 028/171] Bug fix --- Dice/CharacterCard.h | 16 ++++++++++++---- Dice/Dice.cpp | 6 +++--- Dice/DiceSchedule.cpp | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index f503bc11..1c082cd6 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -780,9 +780,17 @@ class Player std::lock_guard lock_queue(cardMutex); if (!mNameIndex.count(name))return -5; if (!mNameIndex[name])return -7; - for (auto it : mGroupIndex) + auto it = mGroupIndex.cbegin(); + while (it != mGroupIndex.cend()) { - if (it.second == mNameIndex[name])mGroupIndex.erase(it.first); + if (it->second == mNameIndex[name]) + { + it = mGroupIndex.erase(it); + } + else + { + ++it; + } } mCardList.erase(mNameIndex[name]); while (!mCardList.count(indexMax))indexMax--; @@ -832,7 +840,7 @@ class Player string listMap() { ResList Res; - for (auto it : mGroupIndex) + for (const auto& it : mGroupIndex) { if (!it.first)Res << "default:" + mCardList[it.second].Name; else Res << "(" + to_string(it.first) + ")" + mCardList[it.second].Name; @@ -917,7 +925,7 @@ class Player } pack.add(cards); Unpack groups; - for (auto it : mGroupIndex) + for (const auto& it : mGroupIndex) { groups.add(static_cast(it.first)); groups.add(it.second); diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 8b76decb..48ebd621 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -321,7 +321,7 @@ EVE_Enable(eventEnable) GroupList.clear(); map mDefault; if (loadFile(strFileLoc + "DefaultCOC.MYmap", mDefault) > 0) - for (auto it : mDefault) + for (const auto& it : mDefault) { if (it.first.second == msgtype::Private)getUser(it.first.first) .create(NEWYEAR).setConf("rc", it.second); @@ -376,7 +376,7 @@ EVE_Enable(eventEnable) if (loadFile(DiceDir + "\\user\\ChatList.txt", ChatList) < 1) { map mLastMsgList; - for (auto it : mLastMsgList) + for (const auto& it : mLastMsgList) { if (it.first.second == msgtype::Private)getUser(it.first.first).create(it.second); else chat(it.first.first).create(it.second).lastmsg(it.second).isGroup = 2 - int(it.first.second); @@ -384,7 +384,7 @@ EVE_Enable(eventEnable) std::map mGroupInviter; if (loadFile(strFileLoc + "GroupInviter.RDconf", mGroupInviter) < 1) { - for (auto it : mGroupInviter) + for (const auto& it : mGroupInviter) { chat(it.first).group().inviter = it.second; } diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 1cb039a9..1da63faf 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -179,7 +179,7 @@ void DiceToday::load() { json jFile = freadJson(pathFile); if (jFile.is_null()) { time_t tt = time(nullptr); - stToday = *localtime(&tt); + localtime_s(&stToday, &tt); return; } if (jFile.count("date")) { From e0ae88b790b04c5cf31051127c7394b7dbaad6a7 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 4 Sep 2020 10:40:47 +0800 Subject: [PATCH 029/171] fix Bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 去除json文件的注释 修复readNum 修复GroupInfo --- CQSDKCPP/CQAPI_EX.cpp | 4 +++- Dice/BlackListManager.cpp | 12 ++++++++++- Dice/DiceEvent.cpp | 1 + Dice/com.w4123.dice.json | 43 +++++++++++++++++++-------------------- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/CQSDKCPP/CQAPI_EX.cpp b/CQSDKCPP/CQAPI_EX.cpp index 7e5be953..9c2fafc2 100644 --- a/CQSDKCPP/CQAPI_EX.cpp +++ b/CQSDKCPP/CQAPI_EX.cpp @@ -45,7 +45,9 @@ void GroupInfo::setdata(Unpack& u) GroupInfo::GroupInfo(long long group) { - Unpack pack(base64_decode(CQ_getGroupInfo(getAuthCode(), group, true))); + const char* data = CQ_getGroupInfo(getAuthCode(), group, true); + if (!data || data[0] == '\0')return; + Unpack pack(base64_decode(data)); if (!pack.len())return; setdata(pack); } diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 61e21eca..690ad6ba 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -49,6 +49,16 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) if (grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ") || !grp.isGroup)continue; if (GroupMemberInfo member = getGroupMemberInfo(id, llQQ); member.QQID == llQQ && member.Group == id) { + if (frame == QQFrame::XianQu) { + bool isValid = false; + for (auto& mem : getGroupMemberList(id)) { + if (mem.QQID == llQQ) { + isValid = true; + break; + } + } + if (!isValid)continue; + } strNotice = printGroup(id); if (grp.isset("ЭЧ")) { @@ -61,7 +71,7 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) else if (GroupMemberInfo self = getGroupMemberInfo(id, console.DiceMaid); !self.permissions) { continue; } - else if (!member.permissions) { + else if (member.permissions < 1 || member.permissions > 3) { strNotice += "ԷȺȨ޻ȡʧ"; } else if (member.permissions < self.permissions) { diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 0934e7e9..ddab6e28 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -4277,6 +4277,7 @@ int FromMsg::readNum(int& num) intMsgCnt++; } if (strNum.length() > 9)return -2; + if (strNum.empty() || strNum == "-")return -3; num = stoi(strNum); return 0; } diff --git a/Dice/com.w4123.dice.json b/Dice/com.w4123.dice.json index 58beaa5e..c514a60d 100644 --- a/Dice/com.w4123.dice.json +++ b/Dice/com.w4123.dice.json @@ -106,7 +106,7 @@ "function": "eventGUI" } ], - "status": [ // 悬浮窗状态(将应用运行状态等显示在酷Q悬浮窗) + "status": [ { "id": 1, "name": "指令频度", @@ -116,26 +116,25 @@ } ], "auth": [ - //20, //[敏感]取Cookies getCookies - 101, //发送群消息 sendGroupMsg - 103, //发送讨论组消息 sendDiscussMsg - 106, //发送私聊消息 sendPrivateMsg - //110, //发送赞 sendLike - 120, //置群成员移出 setGroupKick - 121, //置群成员禁言 setGroupBan - 122, //置群管理员 setGroupAdmin - 123, //置全群禁言 setGroupWholeBan - 126, //置群成员名片 setGroupCard - 127, //[敏感]置群退出 setGroupLeave - 128, //置群成员专属头衔 setGroupSpecialTitle - 130, //取群成员信息 getGroupMemberInfoV2 / getGroupMemberInfo - 131, //取陌生人信息 getStrangerInfo - 132, //取群信息 getGroupInfo - 140, //置讨论组退出 setDiscussLeave - 150, //置好友添加请求 setFriendAddRequest - 151, //置群添加请求 setGroupAddRequest - 160, //取群成员列表 getGroupMemberList - 161, //取群列表 getGroupList - 162 //取好友列表 getFriendList + 101, + 103, + 106, + 110, + 120, + 121, + 122, + 123, + 126, + 127, + 128, + 130, + 131, + 132, + 140, + 150, + 151, + 160, + 161, + 162 ] } \ No newline at end of file From e579638897e3326f54c5b2a54ff50004ac62ff44 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 25 Sep 2020 14:20:00 +0800 Subject: [PATCH 030/171] update censor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增敏感词审查机制 加载时遍历读取/conf/censor中文件 接收指令前匹配敏感词并通报 --- Dice/BlackListManager.cpp | 2 +- Dice/Dice.cpp | 4 ++ Dice/Dice.vcxproj | 5 +- Dice/Dice.vcxproj.filters | 15 +++--- Dice/DiceCensor.cpp | 62 +++++++++++++++++++++ Dice/DiceCensor.h | 29 ++++++++++ Dice/DiceEvent.cpp | 54 ++++++++++++++----- Dice/DiceJob.cpp | 10 ++-- Dice/DiceJob.h | 2 +- Dice/DiceMod.cpp | 2 +- Dice/DiceMod.h | 4 +- Dice/DiceSchedule.cpp | 2 +- Dice/GlobalVar.cpp | 7 ++- Dice/GlobalVar.h | 4 +- Dice/ManagerSystem.cpp | 1 + Dice/SHKTrie.h | 110 ++++++++++++++++++++++++++++++++++++++ Dice/STLExtern.hpp | 5 +- Dice/StorageBase.cpp | 28 ---------- Dice/StorageBase.h | 42 --------------- Dice/com.w4123.dice.json | 2 +- 20 files changed, 284 insertions(+), 106 deletions(-) create mode 100644 Dice/DiceCensor.cpp create mode 100644 Dice/DiceCensor.h create mode 100644 Dice/SHKTrie.h delete mode 100644 Dice/StorageBase.cpp delete mode 100644 Dice/StorageBase.h diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 690ad6ba..cde8eb36 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -46,7 +46,7 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) string strNotice; for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ") || !grp.isGroup)continue; + if (grp.isset("") || grp.isset("δ") || grp.isset("") || !grp.isGroup)continue; if (GroupMemberInfo member = getGroupMemberInfo(id, llQQ); member.QQID == llQQ && member.Group == id) { if (frame == QQFrame::XianQu) { diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 48ebd621..d472d2a2 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -50,6 +50,7 @@ #include "DiceSession.h" #include "DiceGUI.h" #include "S3PutObject.h" +#include "DiceCensor.h" #pragma warning(disable:4996) #pragma warning(disable:6031) @@ -111,6 +112,9 @@ void loadData() ifstreamHelpDoc.close(); } map_merge(fmt->helpdoc, CustomHelp); + //ȡдʿ + loadDir(load_words, DiceDir + "\\conf\\censor\\", censor, strLog, true); + censor.build(); if (!strLog.empty()) { strLog += "չöȡϡ"; diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index adeeb0a7..1343bd52 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -146,6 +146,7 @@ + @@ -168,7 +169,6 @@ - @@ -197,6 +197,7 @@ + @@ -208,6 +209,7 @@ + @@ -224,7 +226,6 @@ - diff --git a/Dice/Dice.vcxproj.filters b/Dice/Dice.vcxproj.filters index 487f3af4..75088b7c 100644 --- a/Dice/Dice.vcxproj.filters +++ b/Dice/Dice.vcxproj.filters @@ -33,9 +33,6 @@ 源文件 - - 源文件 - 源文件 @@ -126,6 +123,9 @@ 源文件 + + 源文件 + @@ -137,9 +137,6 @@ 头文件 - - 头文件 - 头文件 @@ -290,6 +287,12 @@ 头文件 + + 头文件 + + + 头文件 + diff --git a/Dice/DiceCensor.cpp b/Dice/DiceCensor.cpp new file mode 100644 index 00000000..8aafff92 --- /dev/null +++ b/Dice/DiceCensor.cpp @@ -0,0 +1,62 @@ +#include "DiceCensor.h" +#include "SHKTrie.h" +#include "DiceFile.hpp" +#include "STLExtern.hpp" +#include "DiceConsole.h" +#include "EncodingConvert.h" +#include + +TrieG wordG; + +enumap sens{ "Ignore","Notice","Caution","Warning","Danger" }; + +int load_words(const string& filename, Censor& cens) { + int cnt(0); + ifstream fin(filename); + if (!fin)return -1; + bool isUTF8{ false }; + string word; + Censor::Level danger = Censor::Level::Warning; + while (getline(fin, word)) { + if (word.empty())break; + if (word[0] == '#') { + word = word.substr(1); + //עеȼ + if (sens.count(word)) { + danger = (Censor::Level)sens[word]; + } + //עļ + else if (word == "UTF8") { + isUTF8 = true; + } + } + else { + if (isUTF8)word = UTF8toGBK(word); + cens.insert(word, danger); + cnt++; + } + } + return cnt; +} + +void Censor::insert(const string& word, Level danger = Level::Warning) { + words[word] = danger; +} + +void Censor::build() { + wordG.build(words); +} + +int Censor::search(const string& text, unordered_set& res) { + std::bitset<6> sens; + wordG.search(text, res); + for (auto& word : res) { + sens.set((size_t)words[word]); + } + return sens[5] ? 5 + : sens[4] ? 4 + : sens[3] ? 3 + : sens[2] ? 2 + : sens[1] ? 1 + : 0; +} \ No newline at end of file diff --git a/Dice/DiceCensor.h b/Dice/DiceCensor.h new file mode 100644 index 00000000..051be308 --- /dev/null +++ b/Dice/DiceCensor.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include "STLExtern.hpp" +#include +using std::string; +using std::unordered_set; + +class Censor { + string dirWords; +public: + enum class Level :size_t { + Ignore, // + Notice, //0֪ͨ + Caution, //1֪ͨ + Warning, //ûҾܾƺ1֪ͨ + Danger, //ûҾָܾ3֪ͨ + Critical, //ռλ + }; + map words; + void insert(const string& word, Level); + //void load(); + void build(); + size_t size()const { return words.size(); } + int search(const string& text, unordered_set& res); +}; + +inline Censor censor; + +int load_words(const string& filename, Censor& cens); \ No newline at end of file diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index ddab6e28..23d3c384 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -2,6 +2,7 @@ #include "DiceEvent.h" #include "Jsonio.h" #include "MsgFormat.h" +#include "DiceCensor.h" #include "DiceMod.h" #include "ManagerSystem.h" #include "BlackListManager.h" @@ -106,7 +107,8 @@ int FromMsg::AdminEvent(const string& strOption) << "û" + to_string(today->cnt()) << (!PList.empty() ? "ɫ¼" + to_string(PList.size()) : "") << "û" + to_string(blacklist->mQQDanger.size()) - << "Ⱥ" + to_string(blacklist->mGroupDanger.size()); + << "Ⱥ" + to_string(blacklist->mGroupDanger.size()) + << (censor.size() ? "дʿģ" + to_string(censor.size()) : ""); reply(GlobalMsg["strSelfName"] + "ĵǰ" + res.show()); return 1; } @@ -993,15 +995,39 @@ int FromMsg::DiceReply() } return 0; } - /*switch(console.DSens.find(strLowerMessage, fromQQ, fromChat)) { - case 0:break; - case 1: - reply(GlobalMsg["strSensNote"]); - break; - case 2: - reply(GlobalMsg["strSensWarn"]); - return 1; - }*/ + //С4ûдʼ + if (trusted < 4) { + unordered_setsens_words; + switch (int danger = censor.search(strMsg, sens_words) - 1) { + case 3: + if (trusted < danger++) { + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b1000, + printTTime(fromTime)); + reply(GlobalMsg["strCensorDanger"]); + return 1; + } + case 2: + if (trusted < danger++) { + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b10, + printTTime(fromTime)); + reply(GlobalMsg["strCensorWarning"]); + break; + } + case 1: + if (trusted < danger++) { + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b10, + printTTime(fromTime)); + reply(GlobalMsg["strCensorCaution"]); + break; + } + case 0: + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 1, + printTTime(fromTime)); + break; + default: + break; + } + } if (strLowerMessage.substr(intMsgCnt, 7) == "helpdoc" && trusted > 3) { intMsgCnt += 7; @@ -1080,7 +1106,11 @@ int FromMsg::DiceReply() return 1; } } - fmt->get_help(this); + if (frame == QQFrame::Mirai) { + std::thread th(&DiceModManager::_help, fmt.get(), this); + th.detach(); + } + else fmt->_help(this); return true; } else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && @@ -1251,7 +1281,7 @@ int FromMsg::DiceReply() reply(GlobalMsg["strNotMaster"]); return -1; } - cmd_key = (frame == QQFrame::Mirai) ? "reload" : "remake"; + cmd_key = (frame == QQFrame::CoolQ) ? "remake" : "reload"; sch.push_job(*this); return 1; } diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 3cd1c2c4..8a8ba054 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -135,19 +135,21 @@ void cq_restart(DiceJob& job) { } } -void mirai_reload(DiceJob& job){ +void frame_reload(DiceJob& job){ using cq_reload_type = int(__stdcall*)(int32_t); - HMODULE hModule = LoadLibraryA("CQP.dll"); + HMODULE hModule = GetModuleHandleA("CQP.dll"); cq_reload_type cq_reload = (cq_reload_type)GetProcAddress(hModule, "CQ_reload"); if (!cq_reload) { - job.note("MiraiNativeʧܡ\nʹ˹ɻCQP.dll\n뱣֤汾MiraiNativeɾCQP.dll", 0b10); + if (frame == QQFrame::Mirai) + job.note("MiraiNativeʧܡ\nʹ˹ɻCQP.dll\n뱣֤汾MiraiNativeɾCQP.dll", 0b10); + else if (frame == QQFrame::XianQu) + job.note("CQXQʧܡ\n汾ɣ뱣֤汾CQXQ", 0b10); return; } if(cq_reload(getAuthCode())) job.note("" + getMsg("self") + "ɡ", 1); else job.note("" + getMsg("self") + "ʧܡ", 0b10); - FreeLibrary(hModule); } void auto_save(DiceJob& job) { diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 6117377e..0c4ddc6e 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -7,7 +7,7 @@ int sendSelf(const string msg); void cq_exit(DiceJob& job); void cq_restart(DiceJob& job); -void mirai_reload(DiceJob& job); +void frame_reload(DiceJob& job); void auto_save(DiceJob& job); void check_system(DiceJob& job); diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 2ab3e9bf..f0864596 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -74,7 +74,7 @@ struct help_sorter { } }; -void DiceModManager::get_help(DiceJobDetail* job) { +void DiceModManager::_help(DiceJobDetail* job) { if ((*job)["help_word"].empty()) { job->reply(string(Dice_Short_Ver) + "\n" + GlobalMsg["strHlpMsg"]); return; diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index 52989af6..454cc2f2 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -81,7 +81,7 @@ class DiceModManager string format(string, const map&, const char*) const; unordered_mapcntHelp; [[nodiscard]] string get_help(const string&) const; - void get_help(DiceJobDetail*); + void _help(DiceJobDetail*); void set_help(const string&, const string&); void rm_help(const string&); int load(string&); @@ -89,4 +89,4 @@ class DiceModManager void clear(); }; -inline std::unique_ptr fmt; +inline std::shared_ptr fmt; diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 1da63faf..17d8f9c0 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -13,7 +13,7 @@ unordered_map mCommand = { {"clrimage",clear_image}, {"clrgroup",clear_group}, {"lsgroup",list_group}, - {"reload",mirai_reload}, + {"reload",frame_reload}, {"remake",cq_restart}, {"die",cq_exit}, {"heartbeat",cloud_beat}, diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index c8280f06..8d4ee23f 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -111,8 +111,10 @@ std::map GlobalMsg {"strPcInitDelErr","{nick}ijʼɾ"}, {"strPcNoteTooLong","עȲܳ255"}, {"strPcTextTooLong","ıȲܳ48"}, - {"strSensNote","ָддʣ{self}Ѽ¼ϱ"}, - {"strSensWarn","ָддʣ{self}ܾӦϱ"}, + {"strCensorCaution","ѣ{nick}ָдʣ{self}ϱ"}, + {"strCensorWarning","棺{nick}ָдʣ{self}Ѽ¼ϱ"}, + {"strCensorDanger","棺{nick}ָдʣ{self}ָܾϱ"}, + //{"strCensorCritical","棺{nick}ָдʣ{self}Ѽ¼ϱ"}, {"strSpamFirstWarning","ʱڶ{self}ָ࣡ö͸ָˢξ棩"}, {"strSpamFinalWarning","ͣһָƵָ{self}ڣˢվ棩"}, {"strReplySet","{self}Թؼ{key}Ļظá"}, @@ -332,6 +334,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +567:дʼ 566:.helpѯ 565:.log־¼ 564:๦Żƶѵ diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 6a48f58f..81324b0f 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -36,8 +36,8 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 566u; -inline const std::string Dice_Ver_Without_Build = "2.4.1beta2"; +const unsigned short Dice_Build = 567u; +inline const std::string Dice_Ver_Without_Build = "2.4.1beta3"; constexpr auto DiceRequestHeader = "Dice/2.4.1"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index c5a2348d..6483ec43 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -133,6 +133,7 @@ Chat& chat(long long id) } Chat& Chat::id(long long grp) { ID = grp; + if (!Enabled)return *this; if (CQ::GroupInfo ginfo(grp); ginfo.llGroup || CQ::getGroupList().count(grp)) { Name = ginfo.strGroupName; diff --git a/Dice/SHKTrie.h b/Dice/SHKTrie.h new file mode 100644 index 00000000..fe758434 --- /dev/null +++ b/Dice/SHKTrie.h @@ -0,0 +1,110 @@ +/* + * ֵ + * Copyright (C) 2019-2020 String.Empty + */ +#pragma once +#include +#include +#include +using std::map; +using std::unordered_set; +using std::string; + +template +class TrieNode { +public: + map next{}; + TrieNode* fail = nullptr; + bool isleaf = false; + string value{}; +}; + +template +class TrieG { + using Node = TrieNode; + Node root{}; + string chsException; + //ΪĿڵӽڵƥfail + void make_fail(Node& node) { + if (&node == &root) { + for (auto& kid : node.next) { + kid.second.fail = &node; + } + } + else { + for (auto& [ch,kid] : node.next) { + if (auto it = node.fail->next.find(ch); it != node.fail->next.end()) { + kid.fail = &(it->second); + } + else { + kid.fail = &root; + } + } + } + for (auto& kid : node.next) { + make_fail(kid.second); + } + } + static bool ignored(char ch) { + static const char* dot{ "~!@#$%^&*()-=`_+[]\\{}|;':\",./<>?" }; + return isspace(static_cast(ch)) || strchr(dot, ch); + } + void add(const string& s) { + Node* p = &root; + for (auto ch : s) { + if (!p->next.count(ch)) { + p->next[ch] = Node(); + } + p = &(p->next[ch]); + } + p->value = s; + p->isleaf = true; + } +public: + TrieG(){} + template + TrieG(const Con& dir) { + for (const auto& [key,val]: dir) { + add(key); + } + make_fail(root); + } + template + void build(const Con& dir) { + new(this)TrieG(dir); + } + //ǰ׺ƥ + bool match_head(const string& s, unordered_set& res)const { + const Node* p = &root; + for (const auto& ch : s) { + if (ignored(ch))continue; + if (!p->next.count(ch))break; + p = &(p->next.find(ch)->second); + if (p->isleaf) { + res.insert(p->value); + } + } + return res.size(); + } + //λִƥ + bool search(const string& s, unordered_set& res)const { + const Node* p = &root; + for (const auto& ch : s) { + if (ignored(ch))continue; + while (1) { + if (auto it = p->next.find(ch); it != p->next.end()) { + p = &(it->second); + break; + } + if (p == &root) { + break; + } + p = p->fail; + } + if (p->isleaf) { + res.insert(p->value); + } + } + return res.size(); + } +}; diff --git a/Dice/STLExtern.hpp b/Dice/STLExtern.hpp index 5d9a0295..59102364 100644 --- a/Dice/STLExtern.hpp +++ b/Dice/STLExtern.hpp @@ -16,6 +16,9 @@ using std::to_string; struct less_ci { + bool operator()(const char& ch1, const char& ch2) const { + return tolower(static_cast(ch1)) < tolower(static_cast(ch2)); + } bool operator()(const string& str1, const string& str2) const { string::const_iterator it1 = str1.cbegin(), it2 = str2.cbegin(); @@ -53,7 +56,7 @@ class enumap return mVal.find(val) != mVal.end(); } - int operator[](T& val) const + size_t operator[](T& val) const { if (auto it = mVal.find(val); it != mVal.end())return it->second; return -1; diff --git a/Dice/StorageBase.cpp b/Dice/StorageBase.cpp deleted file mode 100644 index 3787adf9..00000000 --- a/Dice/StorageBase.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * _______ ________ ________ ________ __ - * | __ \ |__ __| | _____| | _____| | | - * | | | | | | | | | |_____ | | - * | | | | | | | | | _____| |__| - * | |__| | __| |__ | |_____ | |_____ __ - * |_______/ |________| |________| |________| |__| - * - * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2019 w4123 - * - * This program is free software: you can redistribute it and/or modify it under the terms - * of the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -#include "StorageBase.h" -#include - -StorageBase::StorageBase(std::string FilePath) : FilePath(std::move(FilePath)) -{ -} diff --git a/Dice/StorageBase.h b/Dice/StorageBase.h deleted file mode 100644 index d3051cbb..00000000 --- a/Dice/StorageBase.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * _______ ________ ________ ________ __ - * | __ \ |__ __| | _____| | _____| | | - * | | | | | | | | | |_____ | | - * | | | | | | | | | _____| |__| - * | |__| | __| |__ | |_____ | |_____ __ - * |_______/ |________| |________| |________| |__| - * - * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2019 w4123 - * - * This program is free software: you can redistribute it and/or modify it under the terms - * of the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this - * program. If not, see . - */ -#pragma once -#ifndef DICE_STORAGE_BASE -#define DICE_STORAGE_BASE -#include - -class StorageBase -{ -protected: - const std::string FilePath; -public: - virtual void read() = 0; - virtual void save() const = 0; - StorageBase(std::string FilePath); - StorageBase(const StorageBase&) = delete; - StorageBase(StorageBase&&) = delete; - virtual ~StorageBase() = default; - virtual StorageBase& operator=(const StorageBase&) = delete; - virtual StorageBase& operator=(StorageBase&&) = delete; -}; -#endif /*DICE_STORAGE_BASE*/ diff --git a/Dice/com.w4123.dice.json b/Dice/com.w4123.dice.json index c514a60d..078cd3aa 100644 --- a/Dice/com.w4123.dice.json +++ b/Dice/com.w4123.dice.json @@ -3,7 +3,7 @@ "apiver": 9, "name": "Dice!", "version": "2.4.1", - "version_id": 566, + "version_id": 567, "author": "w4123溯洄 & Shiki", "description": "跑团用骰子 本程序使用AGPLv3开源协议授权 Copyright (c) 2018-2020 w4123溯洄 & Shiki", "event": [ From aeebea246fb5d883ab3ef4788e8f3c946b18bbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Fri, 25 Sep 2020 19:00:38 +0800 Subject: [PATCH 031/171] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3aeba6dc..62292bae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Dice! -QQ Dice Robot For TRPG Based on CoolQ/Mirai +QQ Dice Robot For TRPG Based on CoolQ/Mirai/XQ [![License](https://img.shields.io/github/license/Dice-Developer-Team/Dice.svg)](http://www.gnu.org/licenses) [![Build status](https://ci.appveyor.com/api/projects/status/7uq2qi3348ny1tfv?svg=true)](https://ci.appveyor.com/project/w4123/dice-ovf7o) @@ -9,9 +9,11 @@ QQ Dice Robot For TRPG Based on CoolQ/Mirai ## 简介 -Dice!是一款基于酷Q的QQ跑团掷骰机器人 交流QQ群:882747577或941980833或624807593(已满) +Dice!是一款基于酷Q的QQ跑团掷骰机器人 交流QQ群:882747577或941980833或624807593(已满) 也可在MiraiNative或CQXQ等CQ兼容性况下运行 -主页: +主页: + +论坛: Latest Stable Release: [![GitHub release](https://img.shields.io/github/release/Dice-Developer-Team/dice.svg)](https://github.com/w4123/Dice-Developer-Team/releases) [![GitHub Release Date](https://img.shields.io/github/release-date/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/releases) @@ -25,9 +27,7 @@ Latest Release: [![GitHub release](https://img.shields.io/github/release-pre/Dic ## 编译须知 -从GitHub克隆源码时请不要直接从master分支克隆, 因为所有的更改都会提交到此分支, 很有可能包含最新的测试性更改, 未经过测试无法保证稳定 请选择Tag中最新的Release进行下载 - -请使用最新版Visual Studio 2019或以上版本 (或其独立编译器)进行编译, 项目主文件为Dice.sln, 编译时务必使用Win32模式否则无法编译成功 +请使用最新版Visual Studio 2019或以上版本 (或其独立编译器)进行编译, 项目主文件为Dice.sln 新增: 现在可以用GCC/Clang编译, 只测试了几个版本, 编译出现问题请反馈, 下面列出编译选项, 正在写cmake @@ -35,7 +35,7 @@ Latest Release: [![GitHub release](https://img.shields.io/github/release-pre/Dic - Clang(4+)+MSVC(VS2019+): ` clang-cl --target=i686-pc-windows-msvc /MT /O2 /EHsc /std:c++17 /D "UNICODE" /LD /link "user32.lib" /o com.w4123.dice.dll /I CQSDK\ /I Dice\ CQSDKCPP\*.cpp Dice\*.cpp Dice\CQP.lib -Wno-invalid-source-encoding ` - Clang(4+)+GCC(9.0.0+): ` clang++ --target=i686-pc-windows-gnu -m32 -shared -static -o com.w4123.dice.dll -Xclang -flto-visibility-public-std -Wl,--kill-at -std=c++17 -O2 -I CQSDK\ -I Dice\ CQSDKCPP\*.cpp Dice\*.cpp Dice\CQP.lib -lWinInet -luser32 -pthread -Wno-invalid-source-encoding ` -编译后会得到com.w4123.dice.dll文件, 请勿更改此文件的名称! 请从Releases中下载对应的json文件(或自己编写), 放至酷Q app文件夹下, 并开启开发模式, 在应用管理中合成cpk文件即可正常使用 +编译后会得到com.w4123.dice.dll文件! 请从Releases中下载对应的json文件(或自己编写), 放至酷Q/MN/XQ 对应文件夹下使用 ## Issue提交 From 792ad41c973fa72bd8002271c516c6da4b1e37f3 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 3 Oct 2020 19:35:13 +0800 Subject: [PATCH 032/171] update 2.4.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义敏感词 允许.admin censor设置敏感词 修复link的存档 新增.group diver4kick --- Dice/Dice.cpp | 13 ++++---- Dice/DiceCensor.cpp | 34 +++++++++++++++++++ Dice/DiceCensor.h | 6 +++- Dice/DiceEvent.cpp | 78 +++++++++++++++++++++++++++++++++++++++----- Dice/DiceEvent.h | 8 ++--- Dice/DiceJob.cpp | 26 +++++++++++++-- Dice/DiceMod.cpp | 2 +- Dice/DiceMod.h | 46 ++++++++++++-------------- Dice/DiceSchedule.h | 3 +- Dice/DiceSession.cpp | 4 ++- Dice/GlobalVar.cpp | 31 ++++++++++++++++-- Dice/GlobalVar.h | 2 +- Dice/Jsonio.h | 8 +++-- Dice/STLExtern.hpp | 2 +- 14 files changed, 202 insertions(+), 61 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index d472d2a2..8dc278d4 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -114,6 +114,7 @@ void loadData() map_merge(fmt->helpdoc, CustomHelp); //ȡдʿ loadDir(load_words, DiceDir + "\\conf\\censor\\", censor, strLog, true); + loadJMap(DiceDir + "\\conf\\CustomCensor.json", censor.CustomWords); censor.build(); if (!strLog.empty()) { @@ -630,8 +631,8 @@ bool eve_GroupAdd(Chat& grp) EVE_PrivateMsg_EX(eventPrivateMsg) { if (!Enabled)return; - FromMsg Msg(eve.message, eve.fromQQ); - if (Msg.DiceFilter())eve.message_block(); + shared_ptr Msg(make_shared(eve.message, eve.fromQQ)); + if (Msg->DiceFilter())eve.message_block(); } EVE_GroupMsg_EX(eventGroupMsg) @@ -643,8 +644,8 @@ EVE_GroupMsg_EX(eventGroupMsg) if (grp.isset("δ") || grp.isset(""))eve_GroupAdd(grp); if (!grp.isset("")) { - FromMsg Msg(eve.message, eve.fromGroup, msgtype::Group, eve.fromQQ); - if (Msg.DiceFilter())eve.message_block(); + shared_ptr Msg(make_shared(eve.message, eve.fromGroup, msgtype::Group, eve.fromQQ)); + if (Msg->DiceFilter())eve.message_block(); } if (grp.isset("Ϣ"))eve.message_block(); } @@ -668,8 +669,8 @@ EVE_DiscussMsg_EX(eventDiscussMsg) grp.leave(strMsg); return; } - FromMsg Msg(eve.message, eve.fromDiscuss, msgtype::Discuss, eve.fromQQ); - if (Msg.DiceFilter() || grp.isset("Ϣ"))eve.message_block(); + shared_ptr Msg(make_shared(eve.message, eve.fromDiscuss, msgtype::Discuss, eve.fromQQ)); + if (Msg->DiceFilter() || grp.isset("Ϣ"))eve.message_block(); } EVE_System_GroupMemberIncrease(eventGroupMemberIncrease) diff --git a/Dice/DiceCensor.cpp b/Dice/DiceCensor.cpp index 8aafff92..5706967a 100644 --- a/Dice/DiceCensor.cpp +++ b/Dice/DiceCensor.cpp @@ -1,6 +1,7 @@ #include "DiceCensor.h" #include "SHKTrie.h" #include "DiceFile.hpp" +#include "Jsonio.h" #include "STLExtern.hpp" #include "DiceConsole.h" #include "EncodingConvert.h" @@ -42,10 +43,43 @@ int load_words(const string& filename, Censor& cens) { void Censor::insert(const string& word, Level danger = Level::Warning) { words[word] = danger; } +void Censor::add_word(const string& word, Level danger = Level::Warning) { + CustomWords[word] = danger; + words[word] = danger; + save(); +} +bool Censor::rm_word(const string& word) { + if (!CustomWords.count(word)) { + //޿Ƴ + if (!words.count(word)) { + return false; + } + //Զ壬ʿ + else { + words[word] = Level::Ignore; + CustomWords[word] = Level::Ignore; + } + } + else{ + words[word] = Level::Ignore; + CustomWords.erase(word); + } + save(); + return true; +} + +Censor::Level Censor::get_level(const string& lv) { + if (!sens.count(lv))return Level::Warning; + return (Censor::Level)sens[lv]; +} void Censor::build() { + map_merge(words, CustomWords); wordG.build(words); } +void Censor::save() { + saveJMap(DiceDir + "\\conf\\CustomCensor.json", censor.CustomWords); +} int Censor::search(const string& text, unordered_set& res) { std::bitset<6> sens; diff --git a/Dice/DiceCensor.h b/Dice/DiceCensor.h index 051be308..a802b10c 100644 --- a/Dice/DiceCensor.h +++ b/Dice/DiceCensor.h @@ -17,9 +17,13 @@ class Censor { Critical, //ռλ }; map words; + map CustomWords; + Level get_level(const string&); void insert(const string& word, Level); - //void load(); + void add_word(const string& word, Level); + bool rm_word(const string& word); void build(); + void save(); size_t size()const { return words.size(); } int search(const string& text, unordered_set& res); }; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 23d3c384..67336946 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -12,7 +12,7 @@ #include "CQAPI.h" #include "DiceNetwork.h" #include "DiceCloud.h" -#include +#include using namespace std; using namespace CQ; @@ -172,13 +172,60 @@ int FromMsg::AdminEvent(const string& strOption) { getDiceList(); strReply = "ǰб"; - for (auto it : mDiceList) + for (auto& [diceQQ, masterQQ] : mDiceList) { - strReply += "\n" + printQQ(it.first); + strReply += "\n" + printQQ(diceQQ); } reply(); return 1; } + if (strOption == "censor") { + readSkipSpace(); + if (strMsg[intMsgCnt] == '+') { + intMsgCnt++; + strVar["danger_level"] = readToColon(); + Censor::Level danger_level = censor.get_level(strVar["danger_level"]); + readSkipColon(); + ResList res; + while (intMsgCnt != strMsg.length()) { + string item = readItem(); + if (!item.empty()) { + censor.add_word(item, danger_level); + res << item; + } + } + if (res.empty()) { + reply("{nick}δдʣ"); + } + else { + note("{nick}{danger_level}д" + to_string(res.size()) + ":" + res.show(), 1); + } + } + else if (strMsg[intMsgCnt] == '-') { + intMsgCnt++; + ResList res,resErr; + while (intMsgCnt != strMsg.length()) { + string item = readItem(); + if (!item.empty()) { + if (censor.rm_word(item)) + res << item; + else + resErr << item; + } + } + if (res.empty()) { + reply("{nick}δƳдʣ"); + } + else { + note("{nick}Ƴд" + to_string(res.size()) + ":" + res.show(), 1); + } + if (!resErr.empty()) + reply("{nick}Ƴд" + to_string(resErr.size()) + ":" + resErr.show()); + } + else + reply(fmt->get_help("censor")); + return 1; + } if (strOption == "only") { if (console["Private"]) @@ -1107,10 +1154,10 @@ int FromMsg::DiceReply() } } if (frame == QQFrame::Mirai) { - std::thread th(&DiceModManager::_help, fmt.get(), this); + std::thread th(&DiceModManager::_help, fmt.get(), shared_from_this()); th.detach(); } - else fmt->_help(this); + else fmt->_help(shared_from_this()); return true; } else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && @@ -1568,6 +1615,11 @@ int FromMsg::DiceReply() } if (Command == "diver") { + bool bForKick = false; + if (strLowerMessage.substr(intMsgCnt, 5) == "4kick") { + bForKick = true; + intMsgCnt += 5; + } std::priority_queue> qDiver; time_t tNow = time(nullptr); const int intTDay = 24 * 60 * 60; @@ -1577,7 +1629,8 @@ int FromMsg::DiceReply() time_t intLastMsg = (tNow - each.LastMsgTime) / intTDay; if (!each.LastMsgTime || intLastMsg > 30) { - qDiver.emplace(intLastMsg, each.Nick + "(" + to_string(each.QQID) + ")"); + qDiver.emplace(intLastMsg, (bForKick ? to_string(each.QQID) + : (each.Nick + "(" + to_string(each.QQID) + ")"))); } } if (qDiver.empty()) @@ -1589,11 +1642,13 @@ int FromMsg::DiceReply() ResList res; while (!qDiver.empty()) { - res << qDiver.top().second + to_string(qDiver.top().first) + ""; + res << (bForKick ? qDiver.top().second + : (qDiver.top().second + to_string(qDiver.top().first) + "")); if (++intCnt > 15 && intCnt > intSize / 80)break; qDiver.pop(); } - reply("DZˮԱб:" + res.show()); + bForKick ? reply("(.group " + to_string(llGroup) + " kick " + res.show(1)) + :reply("DZˮԱб:" + res.show(1)); return 1; } if (int intPms = getGroupMemberInfo(llGroup, fromQQ).permissions; Command == "pause") @@ -2210,7 +2265,7 @@ int FromMsg::DiceReply() } if (Command == "off") { - if (getGroupMemberInfo(fromGroup, fromQQ).permissions >= 2) + if (isAuth) { if (groupset(fromGroup, "jrrp") < 1) { @@ -4291,6 +4346,11 @@ bool FromMsg::DiceFilter() return false; } +void FromMsg::readSkipColon() { + readSkipSpace(); + while (intMsgCnt < strMsg.length() && (strMsg[intMsgCnt] == ':' || strMsg[intMsgCnt] == '='))intMsgCnt++; +} + int FromMsg::readNum(int& num) { string strNum; diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 8771c308..5fe2a572 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -17,7 +17,7 @@ using std::string; //Ϣ -class FromMsg :public DiceJobDetail { +class FromMsg : public DiceJobDetail { public: string strLowerMessage; long long fromGroup = 0; @@ -103,11 +103,7 @@ class FromMsg :public DiceJobDetail { while (intMsgCnt < strMsg.length() && isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt++; } - void readSkipColon() - { - readSkipSpace(); - while (intMsgCnt < strMsg.length() && strMsg[intMsgCnt] == ':')intMsgCnt++; - } + void readSkipColon(); string readUntilSpace() { diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 8a8ba054..b53be632 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -10,6 +10,7 @@ #include "BlackListManager.h" #include "GlobalVar.h" #include "CardDeck.h" +#include "DiceMod.h" #include "S3PutObject.h" #pragma warning(disable:28159) @@ -339,6 +340,9 @@ void clear_group(DiceJob& job) { job.echo("޷ʶɸѡ"); } void list_group(DiceJob& job) { + if (job["list_mode"].empty()) { + job.reply(fmt->get_help("groups_list")); + } if (mChatConf.count(job["list_mode"])) { ResList res; for (auto& [id, grp] : ChatList) { @@ -367,9 +371,9 @@ void list_group(DiceJob& job) { while (!qDiver.empty()) { res << qDiver.top().second + to_string(qDiver.top().first) + ""; qDiver.pop(); - if (++intCnt > 64 || intCnt > qDiver.size() || qDiver.top().first < 7)break; + if (++intCnt > 32 || qDiver.top().first < 7)break; } - job.reply("{self}Ⱥб:" + res.show()); + job.reply("{self}Ⱥб:" + res.show(1)); } else if (job["list_mode"] == "size") { std::priority_queue> qSize; @@ -388,7 +392,7 @@ void list_group(DiceJob& job) { while (!qSize.empty()) { res << qSize.top().second + "[" + to_string(qSize.top().first) + "]"; qSize.pop(); - if (++intCnt > 64 || intCnt > qSize.size() || qSize.top().first < 7)break; + if (++intCnt > 32 || qSize.top().first < 7)break; } job.reply("{self}ڴȺб:" + res.show(1)); } @@ -430,6 +434,22 @@ void dice_update(DiceJob& job) { } } } + else if (frame == QQFrame::XianQu) { + mkDir(dirExe + "CQPlugins/"); + char pathDll[] = "CQPlugins/com.w4123.dice.dll"; + string urlDll("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "/com.w4123.dice.dll?" + to_string(job.fromTime)); + switch (Cloud::DownloadFile(urlDll.c_str(), pathDll)) { + case -1: + job.echo("ʧ:" + urlDll); + break; + case -2: + job.note("Diceʧ!dllļδصָλ", 0b1); + break; + case 0: + default: + job.note("Dice!" + job.strVar["ver"] + "ɹ", 1); + } + } else { char** path = new char* (); _get_pgmptr(path); diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index f0864596..6d51d4d8 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -74,7 +74,7 @@ struct help_sorter { } }; -void DiceModManager::_help(DiceJobDetail* job) { +void DiceModManager::_help(const shared_ptr& job) { if ((*job)["help_word"].empty()) { job->reply(string(Dice_Short_Ver) + "\n" + GlobalMsg["strHlpMsg"]); return; diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index 454cc2f2..7657f98f 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -35,38 +35,34 @@ class BaseDeck class DiceMod { +protected: string mod_name; - string auther; - string ver; - unsigned int build; - unsigned int Dice_build; - mapm_helpdoc; - map> m_public_deck; + string mod_author; + string mod_ver; + unsigned int mod_build{ 0 }; + unsigned int mod_Dice_build{ 0 }; + using dir = map; + dir mod_helpdoc; + map> mod_public_deck; /*map m_generator;*/ public: DiceMod() = default; - DiceMod(string name, - maphelpdoc/*, - map> private_deck, - map> public_deck, - map generator*/ - ) : mod_name(std::move(name)), m_helpdoc(std::move(helpdoc)) - /*,m_private_deck(private_deck),m_public_deck(public_deck),m_generator(generator)*/ - { - } friend class DiceModFactory; }; + +#define MOD_BUILD(TYPE, MEM) DiceModFactory& MEM(const TYPE& val){ \ + mod_##MEM = val; \ + return *this; \ + } class DiceModFactory :public DiceMod { - string mod_name; - string auther; - string ver; - unsigned int build; - unsigned int Dice_build; - mapm_helpdoc; - map> m_public_deck; - /*map m_generator;*/ public: - DiceModFactory& name(string strMod){} + DiceModFactory() {} + MOD_BUILD(string, name) + MOD_BUILD(string, author) + MOD_BUILD(string, ver) + MOD_BUILD(unsigned int, build) + MOD_BUILD(unsigned int, Dice_build) + MOD_BUILD(dir, helpdoc) }; class DiceModManager @@ -81,7 +77,7 @@ class DiceModManager string format(string, const map&, const char*) const; unordered_mapcntHelp; [[nodiscard]] string get_help(const string&) const; - void _help(DiceJobDetail*); + void _help(const shared_ptr&); void set_help(const string&, const string&); void rm_help(const string&); int load(string&); diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index 7777486c..2f2b6ebd 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -14,8 +14,9 @@ using std::string; using std::map; using std::unordered_map; +using std::shared_ptr; -struct DiceJobDetail { +struct DiceJobDetail : public std::enable_shared_from_this { long long fromQQ = 0; chatType fromChat; string cmd_key; diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index 3f006d2b..7fa47acf 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -215,8 +215,10 @@ void DiceSession::link_start(FromMsg* msg) { void DiceSession::link_close(FromMsg* msg) { if (auto link = LinkList.find(room); link != LinkList.end()) { linker.isLinking = false; - if (gm->mSession.count(link->second.first)) + if (gm->mSession.count(link->second.first)) { gm->session(link->second.first).linker.isLinking = false; + gm->session(link->second.first).update(); + } LinkList.erase(link->second.first); LinkList.erase(link); msg->reply(GlobalMsg["strLinkClose"]); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 8d4ee23f..bf997d3d 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -286,7 +286,7 @@ std::map GlobalMsg {"strLeaveNoPower", "{self}δȺȺ"}, {"strLeaveUnused", "{self}Ѿﱻ{day}Ͼͻ뿪"}, {"strGlobalOff", "{self}ݼУͣ"}, - {"strPreserve", "{self}˽˽ã\n뷢!authorize +[Ⱥ] []"}, + {"strPreserve", "{self}˽˽ã\n뷢!authorize +[Ⱥ] ;:[ **д** ] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[ **дָ** ]úʹ[ **дָ** ]ͳȺ"}, {"strJrrp", "{nick}Ʒֵ: {res}"}, {"strJrrpErr", "JRRPȡʧ! Ϣ: \n{res}"}, { "strFriendDenyNotUser", "źûʹ{self}ļ¼" }, @@ -434,7 +434,7 @@ const std::map HelpDoc = { {"","&/ͷ"}, {"/ͷ","COCн/ͷǶͶʮλսѡõ/ߵĽִʧܵµȼڸС/ʮλ\n.rb2 2\nrcp 1ͷ"}, {"","&name"}, -{"name",".name (cn/jp/en)([])\n.name 10\t//Ĭ\n.name en\t//cn/jp/en޶//Ӣ\nɸΧ1-10̫׷ĺ"}, +{"name",".name (cn/jp/en)([])\n.name 10\t//Ĭ4\n.name en\t//cn/jp/en/enzh޶//Ӣ/Ӣ\nɸΧ1-10̫׷ĺ"}, {"dz","&nn"}, {"dz","&nn"}, {"nn","dzƣ.nn [dz] / .nn / .nnn(cn/jp/en) \n.nn kp\t//dzǰ./ȷŻᱻԶ\n.nn\t//Ϊɾdz\n.nnn\t//Ϊdz\n.nnn jp\t/޶dz\n˽.nnΪȫdz\nȼȺdz>ȫdz>ȺƬ>QQdz"}, @@ -497,7 +497,7 @@ const std::map HelpDoc = { {"", "&draw"}, { "draw", - "ƣ.draw [ƶ] ([])\t//鵽ƲŻأܳƶ\nǰƶѣӲ/Ա/\nԱְҵ/Ա/Ӣ츳/ú//˼/Ҫ֮/Ҫ֮/Ƿ֮/֮/Աص/ʱ֢״/֢ܽ״/־֢״/֢״/\nӪ/޻ɫ/ðյ/\nżʾ/ż/żƬ/ż/\nAMGC/AMGC/AMGCר/AMGC/AMGCװ/AMGC/AMGCؼ1/AMGCؼ2/AMGCؼ3\n///ռ//ʥ/Ҫ/Сʮ/â/ʮ\n.helpλ+򣩿ɻȡƽ\nչƶ:{չƶ}" + "ƣ.draw [ƶ] ([])\t//鵽ƲŻأܳƶ\nǰѰװƶ:{ȫƶб}" }, { "չƶ","{list_extern_deck}" }, { "ȫƶб","{list_all_deck}" }, @@ -538,6 +538,12 @@ const std::map HelpDoc = { :.group +ûظ //رձȺԶظ Ⱥܴ:ָͣ/ûظ/jrrp/draw/me/help/ob/Ϣ/ʹ//)" }, + { "groups_list", "&ȡȺб" }, + { "ȡȺб", R"(ȡȺб.groups list(޶) +.groups list idle //гȺ +.groups list size //ȺģгȺ +.groups list [Ⱥܴ] //гдȺ +Ⱥܴ:ָͣ/ûظ/jrrp/draw/me/help/ob/Ϣ/ʹ//)" }, {"Ϣ","&link"}, {"link",R"(Ϣ.link .link [ת] [󴰿] 󴰿ڵת @@ -547,6 +553,25 @@ const std::map HelpDoc = { [󴰿]:Ⱥ/=[Ⱥ];˽Ĵ=q[QQ] :.link with q1605271653 //˫˽ .link from 754494359 //ĿȺϢת)"}, + { "дʼ","&censor" }, + {"censor",R"(дʼ.admin censor +.admin censor +([ȼ])=[д0](|[д1]...) //д +.admin censor -[д0](|[д1]...) //Ƴд +:.admin censor +=nmsl //nmslΪWarning +.admin censor +Danger=nnϹ|nn //nnϹnnˡΪDanger +.admin censor -ǹ //Ƴдʡǹ +# ƥ +ģƥָʶ(.)ͷϢдʵߴȼ +ƥԶıеźͿոҴСд +ûӦʹȼ4û +# ȼ +Ignore // +Notice //0֪ͨ +Caution //û1 +Warning //Ĭϵȼû1 +Danger //ûҾָܾ3ھ +*Ϊĸ/ֵдýϸߴȼЩַƥͼƬĿ +# ʿطʽֲ)" }, {"", "ǧ֮ĸó֮˫׶Եļ໤ˣһйصĿߣףʱ̨ߣת֮"}, {"Ͷι","&Ͷʳ"}, {"Ͷʳ", "ͶʳShikiѡhttps://afdian.net/@dice_shiki\nͶʳ䧣ѡhttps://afdian.net/@suhuiw4123\nͶʳ{self}ѡ񡭡䳬᣿"}, diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 81324b0f..4c64aa2d 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -37,7 +37,7 @@ * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ const unsigned short Dice_Build = 567u; -inline const std::string Dice_Ver_Without_Build = "2.4.1beta3"; +inline const std::string Dice_Ver_Without_Build = "2.4.1"; constexpr auto DiceRequestHeader = "Dice/2.4.1"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index 8f64fbe9..d95bf8ce 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -114,9 +114,12 @@ int loadJMap(const std::string& strLoc, Map& mapTmp) { //template template -int saveJMap(const std::string& strLoc, const C& mapTmp) +void saveJMap(const std::string& strLoc, const C& mapTmp) { - if (mapTmp.empty())return 0; + if (mapTmp.empty()) { + remove(strLoc.c_str()); + return; + } std::ofstream fout(strLoc); if (fout) { @@ -128,5 +131,4 @@ int saveJMap(const std::string& strLoc, const C& mapTmp) fout << j.dump(2); fout.close(); } - return 0; } diff --git a/Dice/STLExtern.hpp b/Dice/STLExtern.hpp index 59102364..014fe6dd 100644 --- a/Dice/STLExtern.hpp +++ b/Dice/STLExtern.hpp @@ -56,7 +56,7 @@ class enumap return mVal.find(val) != mVal.end(); } - size_t operator[](T& val) const + size_t operator[](const T& val) const { if (auto it = mVal.find(val); it != mVal.end())return it->second; return -1; From 0fe7b539ec4a7f64d493935a0f4055c54b822e19 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 14 Oct 2020 20:10:15 +0800 Subject: [PATCH 033/171] fix admin censor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复指令添加敏感词后不会自动更新字典的bug --- Dice/DiceCensor.cpp | 1 + Dice/SHKTrie.h | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Dice/DiceCensor.cpp b/Dice/DiceCensor.cpp index 5706967a..85c1d164 100644 --- a/Dice/DiceCensor.cpp +++ b/Dice/DiceCensor.cpp @@ -45,6 +45,7 @@ void Censor::insert(const string& word, Level danger = Level::Warning) { } void Censor::add_word(const string& word, Level danger = Level::Warning) { CustomWords[word] = danger; + if (!words.count(word))wordG.insert(word); words[word] = danger; save(); } diff --git a/Dice/SHKTrie.h b/Dice/SHKTrie.h index fe758434..eae4556a 100644 --- a/Dice/SHKTrie.h +++ b/Dice/SHKTrie.h @@ -27,8 +27,8 @@ class TrieG { //ΪĿڵӽڵƥfail void make_fail(Node& node) { if (&node == &root) { - for (auto& kid : node.next) { - kid.second.fail = &node; + for (auto& [ch, kid] : node.next) { + kid.fail = &node; } } else { @@ -41,8 +41,8 @@ class TrieG { } } } - for (auto& kid : node.next) { - make_fail(kid.second); + for (auto& [ch, kid] : node.next) { + make_fail(kid); } } static bool ignored(char ch) { @@ -73,6 +73,10 @@ class TrieG { void build(const Con& dir) { new(this)TrieG(dir); } + void insert(const string& key) { + add(key); + make_fail(root); + } //ǰ׺ƥ bool match_head(const string& s, unordered_set& res)const { const Node* p = &root; From 8062da2ea5d415ce5339a352c195562a308a6279 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 17 Oct 2020 17:39:12 +0800 Subject: [PATCH 034/171] fix system remake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复先驱框架无法正常重启的bug MiraiOK仍不可用taskkill 允许定时自动重启 --- Dice/DiceConsole.cpp | 4 +- Dice/DiceJob.cpp | 100 ++++++++++++++++++++++++++++++------------- 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index f51ba5a1..10ab443f 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -50,8 +50,8 @@ const std::mapConsole::intDefault{ {"CloudBlackShare",1},{"BelieveDiceList",0},{"CloudVisible",1}, {"SystemAlarmCPU",90},{"SystemAlarmRAM",90},{"SystemAlarmDisk",90}, {"SendIntervalIdle",500},{"SendIntervalBusy",100}, -//Զ¼[min],ԶͼƬ[h] -{"AutoSaveInterval",10},{"AutoClearImage",0} +//Զ¼[min],ԶͼƬ[h],Զܼ[h] +{"AutoSaveInterval",10},{"AutoClearImage",0},{"AutoFrameRemake",0} }; const enumap Console::mClockEvent{"off", "on", "save", "clear"}; diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index b53be632..c38972f5 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -23,32 +23,45 @@ int sendSelf(const string msg) { } void cq_exit(DiceJob& job) { - int pid = _getpid(); - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(pe32); - HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hProcessSnap == INVALID_HANDLE_VALUE) { - job.note("ʧܣ̿մʧܣ", 1); - } - BOOL bResult = Process32First(hProcessSnap, &pe32); - int ppid(0); - while (bResult) { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - break; + job.note("" + getMsg("self") + "5ɱ", 1); + if (frame == QQFrame::CoolQ) { + int pid = _getpid(); + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(pe32); + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) { + job.note("ʧܣ̿մʧܣ", 1); } - bResult = Process32Next(hProcessSnap, &pe32); - } - if (!ppid) { - job.note("ʧܣδҵ̣", 1); + BOOL bResult = Process32First(hProcessSnap, &pe32); + int ppid(0); + while (bResult) { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + break; + } + bResult = Process32Next(hProcessSnap, &pe32); + } + if (!ppid) { + job.note("ʧܣδҵ̣", 1); + } + string strCMD("taskkill /f /pid " + to_string(ppid)); + std::this_thread::sleep_for(5s); + job.echo(strCMD); + Enabled = false; + dataBackUp(); + system(strCMD.c_str()); + } + else if (frame == QQFrame::Mirai) { + std::this_thread::sleep_for(5s); + //Enabled = false; + dataBackUp(); + cout << "stop" << endl; + string cmd = "stop"; + for (auto key : cmd) { + keybd_event(key, 0, 0, 0); + } + keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, 0), 0, 0); } - string strCMD("taskkill /f /pid " + to_string(ppid)); - job.note("" + getMsg("self") + "5ɱ", 1); - std::this_thread::sleep_for(5s); - job.echo(strCMD); - Enabled = false; - dataBackUp(); - system(strCMD.c_str()); } inline PROCESSENTRY32 getProcess(int pid) { @@ -58,8 +71,17 @@ inline PROCESSENTRY32 getProcess(int pid) { Process32First(hParentProcess, &pe32); return pe32; } -void cq_restart(DiceJob& job) { - //string strSelfPath; +void frame_restart(DiceJob& job) { + if (!job.fromQQ) { + if (console["AutoFrameRemake"] <= 0) { + sch.add_job_for(60 * 60, job); + return; + } + else if (int tWait = console["AutoFrameRemake"] * 60 * 60 - (clock() - llStartTime) / 1000; tWait > 0) { + sch.add_job_for(tWait, job); + return; + } + } string strSelfName; PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); @@ -92,7 +114,7 @@ void cq_restart(DiceJob& job) { return; } } - else { + else if (frame == QQFrame::CoolQ) { int pid = _getpid(); while (bResult) { if (pe32.th32ProcessID == pid) { @@ -105,12 +127,25 @@ void cq_restart(DiceJob& job) { bResult = Process32Next(hProcessSnap, &pe32); } } + else { + ppid = _getpid(); + while (bResult) { + if (pe32.th32ProcessID == ppid) { + strSelfName = pe32.szExeFile; + job.echo("ȷϽ" + strSelfName + "\nid:" + to_string(pe32.th32ProcessID)); + break; + } + bResult = Process32Next(hProcessSnap, &pe32); + } + } if (!ppid) { job.note("ʧܣδҵ̣", 1); return; } - string command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart .\\" + strSelfName; + string command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart " + strSelfName; if (frame == QQFrame::CoolQ) command += " /account " + to_string(console.DiceMaid); + //else if (frame == QQFrame::XianQu) command = "start .\\remake.exe " + to_string(ppid) + " " + strSelfName; + else if (frame == QQFrame::XianQu) command = "start .\\.exe\ntaskkill /f /pid " + to_string(ppid); ofstream fout("remake.bat"); fout << command << std::endl; fout.close(); @@ -119,6 +154,11 @@ void cq_restart(DiceJob& job) { Enabled = false; dataBackUp(); std::this_thread::sleep_for(3s); + //if (frame == QQFrame::Mirai) { + // WinExec(("remake.exe " + to_string(ppid) + " " + strSelfName).c_str(), SW_SHOW); + // return; + //} + /* switch (UINT res = -1; res = WinExec(".\\remake.bat", SW_SHOW)) { case 0: job.note("ʧܣڴԴѺľ", 1); @@ -134,6 +174,8 @@ void cq_restart(DiceJob& job) { else job.note("ʧܣδ֪" + to_string(res), 0); break; } + */ + ShellExecute(NULL, "open", "remake.bat", NULL, NULL, SW_SHOWNORMAL); } void frame_reload(DiceJob& job){ @@ -232,8 +274,6 @@ void clear_image(DiceJob& job) { scanImage(HelpDoc, sReferencedImage); scanImage(CardDeck::mPublicDeck, sReferencedImage); scanImage(CardDeck::mReplyDeck, sReferencedImage); - scanImage(CardDeck::mGroupDeck, sReferencedImage); - scanImage(CardDeck::mPrivateDeck, sReferencedImage); for (auto it : ChatList) { scanImage(it.second.strConf, sReferencedImage); } From 13855d3f67b3ea67c3345376ff19fe6247014bfb Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 17 Oct 2020 17:47:21 +0800 Subject: [PATCH 035/171] remove clrimg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除酷Q限定的缓存图片清理 --- Dice/DiceJob.h | 2 +- Dice/DiceSchedule.cpp | 7 ++++--- Dice/DiceSchedule.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 0c4ddc6e..325e6c8f 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -6,7 +6,7 @@ inline time_t tNow = time(NULL); int sendSelf(const string msg); void cq_exit(DiceJob& job); -void cq_restart(DiceJob& job); +void frame_restart(DiceJob& job); void frame_reload(DiceJob& job); void auto_save(DiceJob& job); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 17d8f9c0..634667ff 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -14,7 +14,7 @@ unordered_map mCommand = { {"clrgroup",clear_group}, {"lsgroup",list_group}, {"reload",frame_reload}, - {"remake",cq_restart}, + {"remake",frame_restart}, {"die",cq_exit}, {"heartbeat",cloud_beat}, {"update",dice_update}, @@ -153,8 +153,9 @@ void DiceScheduler::start() { push_job("heartbeat"); push_job("syscheck"); if (console["AutoSaveInterval"] > 0)add_job_for(console["AutoSaveInterval"] * 60, "autosave"); - if (console["AutoClearImage"] > 0)add_job_for(console["AutoClearImage"] * 60 * 60, "clrimage"); - else add_job_for(60 * 60, "clrimage"); + if (console["AutoFrameRemake"] > 0) + add_job_for(console["AutoFrameRemake"] * 60 * 60, "remake"); + else add_job_for(60 * 60, "remake"); } void DiceScheduler::end() { } diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index 2f2b6ebd..f28e3aa5 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -29,7 +29,7 @@ struct DiceJobDetail : public std::enable_shared_from_this { DiceJobDetail(long long qq, chatType ct, std::string msg = "", const char* cmd = "") :fromQQ(qq), fromChat(ct), strMsg(msg),cmd_key(cmd) { } - virtual void reply(string, bool = true) {} + virtual void reply(const string&, bool = true) {} string& operator[](const char* key){ return strVar[key]; } From 95e3b371b934fc8d268c0ea853edaf78681f9b0b Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 24 Oct 2020 18:20:48 +0800 Subject: [PATCH 036/171] update 568 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .deck功能重做 .draw暗抽功能 暗骰回复优化 .init新增del功能 修复Querier查询的恶性bug 自定义作成语句 --- Dice/CardDeck.cpp | 8 - Dice/CardDeck.h | 8 - Dice/Dice.cpp | 1 + Dice/Dice.vcxproj | 5 +- Dice/DiceEvent.cpp | 334 +++++++++++++++------------------------ Dice/DiceEvent.h | 16 +- Dice/DiceJob.cpp | 18 ++- Dice/DiceMod.cpp | 6 +- Dice/DiceSession.cpp | 318 +++++++++++++++++++++++++++++++++++-- Dice/DiceSession.h | 36 ++++- Dice/GlobalVar.cpp | 61 +++++-- Dice/GlobalVar.h | 6 +- Dice/ManagerSystem.cpp | 1 + Dice/MsgFormat.h | 15 ++ Dice/RD.cpp | 2 - Dice/SHKQuerier.h | 8 +- Dice/STLExtern.hpp | 3 - Dice/com.w4123.dice.json | 4 +- 18 files changed, 570 insertions(+), 280 deletions(-) diff --git a/Dice/CardDeck.cpp b/Dice/CardDeck.cpp index 566175df..c9f8fe42 100644 --- a/Dice/CardDeck.cpp +++ b/Dice/CardDeck.cpp @@ -1030,14 +1030,6 @@ namespace CardDeck std::map, less_ci> mReplyDeck = { {".dissmiss", {"diss!diss!diss!Ӣdissǽ!dismiss!"}} }; - //Ⱥƶ - std::map> mGroupDeck; - //Ⱥʱƶ - std::map> mGroupDeckTmp; - //˽ƶ - std::map> mPrivateDeck; - //˽ʱƶ - std::map> mPrivateDeckTmp; std::map PublicComplexDeck{ //{"Ա","{}\n˼{˼}\nҪ֮ˣ{Ҫ֮}\nҪ֮ɣ{Ҫ֮}\nǷ֮أ{Ƿ֮}\n֮{֮}\nص㣺{Աص}"} }; diff --git a/Dice/CardDeck.h b/Dice/CardDeck.h index 85dc44ad..e1dbf9d7 100644 --- a/Dice/CardDeck.h +++ b/Dice/CardDeck.h @@ -16,14 +16,6 @@ namespace CardDeck extern std::map, less_ci> mPublicDeck; extern std::map, less_ci> mExternPublicDeck; extern std::map, less_ci> mReplyDeck; - //Ⱥƶ - extern std::map> mGroupDeck; - //Ⱥʱƶ - extern std::map> mGroupDeckTmp; - //˽ƶ - extern std::map> mPrivateDeck; - //˽ʱƶ - extern std::map> mPrivateDeckTmp; extern std::map PublicComplexDeck; int findDeck(std::string strDeckName); std::string drawCard(std::vector& TempDeck, bool boolBack = false); diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 8dc278d4..89ff52b2 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -929,6 +929,7 @@ void global_exit() { threads = {}; dataBackUp(); sch.end(); + censor = {}; fmt.reset(); gm.reset(); PList.clear(); diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index 1343bd52..1a68a8ba 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -92,7 +92,7 @@ Windows true true - false + true false UseLinkTimeCodeGeneration %(ForceSymbolReferences) @@ -105,7 +105,8 @@ - copy /Y $(TargetPath) "D:\酷Q Air\dev\com.w4123.dice" + + diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 67336946..1bd3f613 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -17,12 +17,36 @@ using namespace std; using namespace CQ; -void FromMsg::reply(std::string strReply, bool isFormat) { +FromMsg& FromMsg::initVar(const std::initializer_list& replace_str) { + int index = 0; + for (const auto& s : replace_str) { + strVar[to_string(index++)] = s; + } + return *this; +} +void FromMsg::formatReply() { + strReply = format(strReply, GlobalMsg, strVar); +} + +void FromMsg::reply(const std::string& msgReply, bool isFormat) { + strReply = msgReply; + reply(isFormat); +} + +void FromMsg::reply(const std::string& msgReply, const std::initializer_list& replace_str) { + initVar(replace_str); + strReply = msgReply; + reply(); +} + +void FromMsg::reply(bool isFormat) { isAns = true; + while (isspace(static_cast(strReply[0]))) + strReply.erase(strReply.begin()); if (isFormat) - strReply = format(strReply, GlobalMsg, strVar); + formatReply(); AddMsgToQueue(strReply, fromChat); - if (LogList.count(fromSession)) { + if (LogList.count(fromSession) && gm->session(fromSession).is_logging()) { filter_CQcode(strReply, fromGroup); ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); logout << GBKtoUTF8(getMsg("strSelfName")) + "(" + to_string(console.DiceMaid) + ") " + printTTime(fromTime) << endl @@ -30,29 +54,30 @@ void FromMsg::reply(std::string strReply, bool isFormat) { } } -void FromMsg::reply(std::string strReply, const std::initializer_list replace_str, - bool isFormat) { +void FromMsg::replyHidden(const std::string& msgReply) { + strReply = msgReply; + replyHidden(); +} +void FromMsg::replyHidden() { isAns = true; while (isspace(static_cast(strReply[0]))) strReply.erase(strReply.begin()); - if (isFormat) { - int index = 0; - for (const auto& s : replace_str) { - strVar[to_string(index++)] = s; - } - strReply = format(strReply, GlobalMsg, strVar); - } - AddMsgToQueue(strReply, fromChat); - if (LogList.count(fromSession)&& gm->session(fromSession).is_logging()) { + formatReply(); + if (LogList.count(fromSession) && gm->session(fromSession).is_logging()) { filter_CQcode(strReply, fromGroup); ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); logout << GBKtoUTF8(getMsg("strSelfName")) + "(" + to_string(console.DiceMaid) + ") " + printTTime(fromTime) << endl - << GBKtoUTF8(strReply) << endl << endl; + << '*' << GBKtoUTF8(strReply) << endl << endl; + } + strReply = "" + printChat(fromChat) + " " + strReply; + AddMsgToQueue(strReply, fromQQ); + if (gm->has_session(fromSession)) { + for (auto qq : gm->session(fromSession).get_ob()) { + if (qq != fromQQ) { + AddMsgToQueue(strReply, qq); + } + } } -} - -void FromMsg::reply() { - reply(strReply); } void FromMsg::fwdMsg() @@ -976,7 +1001,7 @@ int FromMsg::DiceReply() else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ"))))reply(GlobalMsg["strGroupLicenseDeny"]); else if (intT) { - if (isAuth) + if (isAuth || trusted >2) { if (groupset(fromGroup, "ָͣ") > 0) { @@ -998,7 +1023,7 @@ int FromMsg::DiceReply() } else if (Command == "off") { - if (isAuth) + if (isAuth || trusted > 2) { if (groupset(fromGroup, "ָͣ")) { @@ -1958,176 +1983,44 @@ int FromMsg::DiceReply() return 1; } intMsgCnt += 4; - readSkipSpace(); + string strRoom = readDigit(false); + long long llRoom = strRoom.empty() ? fromSession : stoll(strRoom); if (strMsg.length() == intMsgCnt) { reply(fmt->get_help("deck")); return 1; } string strPara = readPara(); - vector *DeckPro = nullptr, *DeckTmp = nullptr; - if (intT != PrivateT && CardDeck::mGroupDeck.count(fromGroup)) - { - DeckPro = &CardDeck::mGroupDeck[fromGroup]; - DeckTmp = &CardDeck::mGroupDeckTmp[fromGroup]; - } - else - { - if (CardDeck::mPrivateDeck.count(fromQQ)) - { - DeckPro = &CardDeck::mPrivateDeck[fromQQ]; - DeckTmp = &CardDeck::mPrivateDeckTmp[fromQQ]; - } - //haichayixiangpanding - } if (strPara == "show") { - if (!DeckTmp) - { - reply(GlobalMsg["strDeckTmpNotFound"]); - return 1; - } - if (DeckTmp->empty()) - { - reply(GlobalMsg["strDeckTmpEmpty"]); - return 1; - } - string strReply = GlobalMsg["strDeckTmpShow"] + "\n"; - for (const auto& it : *DeckTmp) - { - it.length() > 10 ? strReply += it + "\n" : strReply += it + "|"; - } - strReply.erase(strReply.end() - 1); - reply(strReply); + if (gm->has_session(llRoom)) + gm->session(llRoom).deck_show(this); + else reply(GlobalMsg["strDeckListEmpty"]); return 1; } - if (!intT && !isAuth && !trusted) + if ((!isAuth || llRoom != fromSession) && !trusted) { - reply(GlobalMsg["strPermissionDeniedErr"]); - return 1; + reply(GlobalMsg["strWhiteQQDenied"]); } - if (strPara == "set") + else if (strPara == "set") { - strVar["deck_name"] = readAttrName(); - if (strVar["deck_name"].empty())strVar["deck_name"] = readDigit(); - if (strVar["deck_name"].empty()) - { - reply(GlobalMsg["strDeckNameEmpty"]); - return 1; - } - vector DeckSet = {}; - if ((strVar["deck_name"] == "ȺԱ" || strVar["deck_name"] == "member") && intT == GroupT) - { - vector list = getGroupMemberList(fromGroup); - for (auto& each : list) - { - DeckSet.push_back( - (each.GroupNick.empty() ? each.Nick : each.GroupNick) + "(" + to_string(each.QQID) + ")"); - } - CardDeck::mGroupDeck[fromGroup] = DeckSet; - CardDeck::mGroupDeckTmp.erase(fromGroup); - reply(GlobalMsg["strDeckProSet"], {strVar["deck_name"]}); - return 1; - } - switch (CardDeck::findDeck(strVar["deck_name"])) - { - case 1: - if (strVar["deck_name"][0] == '_') - reply(GlobalMsg["strDeckNotFound"]); - else - DeckSet = CardDeck::mPublicDeck[strVar["deck_name"]]; - break; - case 2: - { - int intSize = stoi(strVar["deck_name"]) + 1; - if (intSize == 0) - { - reply(GlobalMsg["strNumCannotBeZero"]); - return 1; - } - strVar["deck_name"] = "1" + strVar["deck_name"]; - while (--intSize) { - DeckSet.push_back(to_string(intSize)); - } - break; - } - case 0: - default: - reply(GlobalMsg["strDeckNotFound"]); - return 1; - } - if (intT == PrivateT) - { - CardDeck::mPrivateDeck[fromQQ] = DeckSet; - } - else - { - CardDeck::mGroupDeck[fromGroup] = DeckSet; - } - reply(GlobalMsg["strDeckProSet"], { strVar["deck_name"] }); - return 1; + gm->session(llRoom).deck_set(this); } - if (strPara == "reset") + else if (strPara == "reset") { - *DeckTmp = vector(*DeckPro); - reply(GlobalMsg["strDeckTmpReset"]); - return 1; + gm->session(llRoom).deck_reset(this); + } + else if (strPara == "del") { + gm->session(llRoom).deck_del(this); } - if (strPara == "clr") + else if (strPara == "clr") { - if (intT == PrivateT) - { - if (CardDeck::mPrivateDeck.count(fromQQ) == 0) - { - reply(GlobalMsg["strDeckProNull"]); - return 1; - } - CardDeck::mPrivateDeck.erase(fromQQ); - if (DeckTmp)DeckTmp->clear(); - reply(GlobalMsg["strDeckProClr"]); - } - else - { - if (CardDeck::mGroupDeck.count(fromGroup) == 0) - { - reply(GlobalMsg["strDeckProNull"]); - return 1; - } - CardDeck::mGroupDeck.erase(fromGroup); - if (DeckTmp)DeckTmp->clear(); - reply(GlobalMsg["strDeckProClr"]); - } - return 1; + gm->session(llRoom).deck_clr(this); } - if (strPara == "new") + else if (strPara == "new") { - if (intT != PrivateT && groupset(fromGroup, "ʹ") == 0) - { - reply(GlobalMsg["strWhiteGroupDenied"]); - return 1; - } - if (intT == PrivateT && trustedQQ(fromQQ) == 0) - { - reply(GlobalMsg["strWhiteQQDenied"]); - return 1; - } - if (intT == PrivateT) - { - CardDeck::mPrivateDeck[fromQQ] = {}; - DeckPro = &CardDeck::mPrivateDeck[fromQQ]; - } - else - { - CardDeck::mGroupDeck[fromGroup] = {}; - DeckPro = &CardDeck::mGroupDeck[fromGroup]; - } - while (intMsgCnt != strMsg.length()) - { - string item = readItem(); - if (!item.empty())DeckPro->push_back(item); - } - reply(GlobalMsg["strDeckProNew"]); - return 1; + gm->session(llRoom).deck_new(this); } + return 1; } else if (strLowerMessage.substr(intMsgCnt, 4) == "draw") { @@ -2147,31 +2040,24 @@ int FromMsg::DiceReply() intMsgCnt++; vector ProDeck; vector* TempDeck = nullptr; - strVar["deck_name"] = readPara(); - if (strVar["deck_name"].empty()) - { - if (intT != PrivateT && CardDeck::mGroupDeck.count(fromGroup)) - { - if (CardDeck::mGroupDeckTmp.count(fromGroup) == 0 || CardDeck::mGroupDeckTmp[fromGroup].empty()) - CardDeck::mGroupDeckTmp[fromGroup] = vector(CardDeck::mGroupDeck[fromGroup]); - TempDeck = &CardDeck::mGroupDeckTmp[fromGroup]; - } - else if (CardDeck::mPrivateDeck.count(fromQQ)) - { - if (CardDeck::mPrivateDeckTmp.count(fromQQ) == 0 || CardDeck::mPrivateDeckTmp[fromQQ].empty()) - CardDeck::mPrivateDeckTmp[fromQQ] = vector(CardDeck::mPrivateDeck[fromQQ]); - TempDeck = &CardDeck::mPrivateDeckTmp[fromQQ]; - } - else - { - reply(GlobalMsg["strDeckNameEmpty"]); - return 1; - } + bool isPrivate(false); + string& key{ strVar["deck_name"] = readAttrName() }; + if (!strVar["deck_name"].empty() && strVar["deck_name"][0] == '_') { + isPrivate = true; + strVar["hidden"]; + strVar["deck_name"].erase(strVar["deck_name"].begin()); + } + if (strVar["deck_name"].empty()){ + reply(fmt->get_help("draw")); + return 1; } else { - //int intFoundRes = CardDeck::findDeck(strVar["deck_name"]); - if (strVar["deck_name"][0] == '_' || CardDeck::findDeck(strVar["deck_name"]) == 0) + if (gm->has_session(fromSession) && gm->session(fromSession).has_deck(key)) { + gm->session(fromSession).deck_draw(this); + return 1; + } + else if (strVar["deck_name"][0] == '_' || CardDeck::findDeck(strVar["deck_name"]) == 0) { strReply = GlobalMsg["strDeckNotFound"]; reply(strReply); @@ -2204,7 +2090,14 @@ int FromMsg::DiceReply() if (TempDeck->empty())break; } strVar["res"] = Res.dot("|").show(); - reply(GlobalMsg["strDrawCard"], {strVar["pc"], strVar["res"]}); + strVar["cnt"] = to_string(Res.size()); + initVar({ strVar["pc"], strVar["res"] }); + if (isPrivate) { + reply(GlobalMsg["strDrawHidden"]); + replyHidden(GlobalMsg["strDrawCard"]); + } + else + reply(GlobalMsg["strDrawCard"]); if (intCardNum > 0) { reply(GlobalMsg["strDeckEmpty"]); @@ -2215,19 +2108,31 @@ int FromMsg::DiceReply() else if (strLowerMessage.substr(intMsgCnt, 4) == "init" && intT) { intMsgCnt += 4; - while (isspace(static_cast(strLowerMessage[intMsgCnt])))intMsgCnt++; - if (strLowerMessage.substr(intMsgCnt, 3) == "clr") - { - if (gm->session(fromSession).table_clr("ȹ")) - reply("ɹȹ¼"); + strVar["table_name"] = "ȹ"; + string strCmd = readPara(); + if (strCmd.empty()) { + reply(fmt->get_help("init")); + } + else if (!gm->has_session(fromSession)|| !gm->session(fromSession).table_count("ȹ")) { + reply(GlobalMsg["strGMTableNotExist"]); + } + else if (strCmd == "show" || strCmd == "list") { + strVar["res"] = gm->session(fromSession).table_prior_show("ȹ"); + reply(GlobalMsg["strGMTableShow"]); + } + else if (strCmd == "del") { + strVar["table_item"] = readRest(); + if (strVar["table_item"].empty()) + reply(GlobalMsg["strGMTableItemEmpty"]); + else if (gm->session(fromSession).table_del("ȹ", strVar["table_item"])) + reply(GlobalMsg["strGMTableItemDel"]); else - reply("бΪգ"); - return 1; + reply(GlobalMsg["strGMTableItemNotFound"]); + } + else if (strCmd == "clr") { + gm->session(fromSession).table_clr("ȹ"); + reply(GlobalMsg["strGMTableClr"]); } - strVar["table_name"] = "ȹ"; - if (gm->session(fromSession).table_count("ȹ")) - reply(GlobalMsg["strGMTableShow"] + gm->session(fromSession).table_prior_show("ȹ")); - else reply(GlobalMsg["strGMTableNotExist"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 4) == "jrrp") @@ -2585,9 +2490,8 @@ int FromMsg::DiceReply() reply(GlobalMsg["strCharacterCannotBeZero"]); return 1; } - string strReply = strVar["pc"]; - COC7(strReply, intNum); - reply(strReply); + COC7(strVar["res"], intNum); + reply(GlobalMsg["strCOCBuild"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 3) == "dnd") @@ -4410,3 +4314,15 @@ int FromMsg::readChat(chatType& ct, bool isReroll) if (isReroll)intMsgCnt = intFormor; return -2; } + +void FromMsg::readItems(vector& vItem) { + while (intMsgCnt != strMsg.length()) { + string strItem; + while (isspace(static_cast(strMsg[intMsgCnt])) || strMsg[intMsgCnt] == '|')intMsgCnt++; + while (strMsg[intMsgCnt] != '|' && intMsgCnt != strMsg.length()) { + strItem += strMsg[intMsgCnt]; + intMsgCnt++; + } + vItem.push_back(strItem); + } +} \ No newline at end of file diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 5fe2a572..bdc8c384 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -33,12 +33,17 @@ class FromMsg : public DiceJobDetail { bool isBlock = false; - void reply(std::string strReply, bool isFormat = true)override; + void formatReply(); - void reply(std::string strReply, const std::initializer_list replace_str, - bool isFormat = true); + void reply(const std::string& strReply, bool isFormat = true)override; - void reply(); + FromMsg& initVar(const std::initializer_list& replace_str); + void reply(const std::string& strReply, const std::initializer_list& replace_str); + void replyHidden(const std::string& strReply); + + void reply(bool isFormat = true); + + void replyHidden(); //֪ͨ void note(std::string strMsg, int note_lv = 0b1) @@ -80,7 +85,6 @@ class FromMsg : public DiceJobDetail { private: //ǷӦ bool isAns = false; - unsigned int intMsgCnt = 0; bool isDisabled = false; bool isCalled = false; bool isAuth = false; @@ -97,6 +101,7 @@ class FromMsg : public DiceJobDetail { return -2; } public: + unsigned int intMsgCnt = 0; //ո void readSkipSpace() { @@ -341,6 +346,7 @@ class FromMsg : public DiceJobDetail { } return strMum; } + void readItems(vector&); }; #endif /*DICE_EVENT*/ diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index c38972f5..126df682 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -11,6 +11,7 @@ #include "GlobalVar.h" #include "CardDeck.h" #include "DiceMod.h" +#include "DiceNetwork.h" #include "S3PutObject.h" #pragma warning(disable:28159) @@ -512,16 +513,29 @@ void dice_update(DiceJob& job) { //ȡƲ¼ void dice_cloudblack(DiceJob& job) { + bool isSuccess(false); job.echo("ʼȡƶ˼¼"); string strURL("https://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); switch (Cloud::DownloadFile(strURL.c_str(), (DiceDir + "/conf/CloudBlackList.json").c_str())) { - case -1: - job.echo("ͬƲ¼ͬʧ:" + strURL); + case -1: { + string des; + if (Network::GET("shiki.stringempty.xyz", "/blacklist/checked.json", 80, des)) { + ofstream fout(DiceDir + "/conf/CloudBlackList.json"); + fout << des << endl; + isSuccess = true; + } + else + job.echo("ͬƲ¼ͬʧ:" + strURL); + } break; case -2: job.echo("ͬƲ¼ͬʧ!ļδҵ"); break; case 0: + default: + break; + } + if (isSuccess) { job.note("ͬƲ¼ɹ" + getMsg("self") + "ʼȡ", 1); blacklist->loadJson(DiceDir + "/conf/CloudBlackList.json", true); } diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 6d51d4d8..376633d6 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -153,8 +153,10 @@ int DiceModManager::load(string& strLog) } std::thread factory(&DiceModManager::init,this); factory.detach(); - cntHelp.reserve(helpdoc.size()); - loadJMap(DiceDir + "\\user\\HelpStatic.json", cntHelp); + if (cntHelp.empty()) { + cntHelp.reserve(helpdoc.size()); + loadJMap(DiceDir + "\\user\\HelpStatic.json", cntHelp); + } return cntFile; } void DiceModManager::init() { diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index 7fa47acf..eae864d8 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -10,22 +10,31 @@ #include "MsgFormat.h" #include "EncodingConvert.h" #include "DiceEvent.h" +#include "CardDeck.h" +#include "RandomGenerator.h" std::shared_mutex sessionMutex; -int DiceSession::table_add(string key, int prior, string item) +bool DiceSession::table_del(const string& tab, const string& item) { + if (!mTable.count(tab) || !mTable[tab].count(item))return false; + mTable[tab].erase(item); + update(); + return true; +} + +int DiceSession::table_add(const string& key, int prior, const string& item) { mTable[key].emplace(item, prior); update(); return 0; } -string DiceSession::table_prior_show(string key) const +string DiceSession::table_prior_show(const string& key) const { return PriorList(mTable.find(key)->second).show(); } -bool DiceSession::table_clr(string key) +bool DiceSession::table_clr(const string& key) { if (const auto it = mTable.find(key); it != mTable.end()) { @@ -229,23 +238,262 @@ void DiceSession::link_close(FromMsg* msg) { } } +DeckInfo::DeckInfo(const vector& deck) :meta(deck) { + init(); +} +void DeckInfo::init() { + idxs.reserve(meta.size()); + for (size_t idx = 0; idx < meta.size(); idx++) { + size_t l, r; + size_t cnt(1); + if ((l = meta[idx].find("::")) != string::npos && (r = meta[idx].find("::", l + 2)) != string::npos) { + string strCnt = CardDeck::draw(meta[idx].substr(l + 2, r - l - 2)); + if (strCnt.length() < 4 && !strCnt.empty() && isdigit(static_cast(strCnt[0])) && strCnt != "0") { + meta[idx].erase(meta[idx].begin(), meta[idx].begin() + r + 2); + cnt = stoi(strCnt); + } + } + while (cnt--) { + idxs.push_back(idx); + } + } + sizRes = idxs.size(); +} +void DeckInfo::reset() { + sizRes = idxs.size(); +} +string DeckInfo::draw() { + if (idxs.empty())return{}; + size_t idx = RandomGenerator::Randint(0, --sizRes); + size_t res = idxs[idx]; + idxs[idx] = idxs[sizRes]; + idxs[sizRes] = res; + return CardDeck::draw(meta[res]); +} + +void DiceSession::deck_set(FromMsg* msg) { + string& key{ msg->strVar["deck_name"] = msg->readAttrName() }; + size_t pos = msg->strMsg.find('=', msg->intMsgCnt); + string& strCiteDeck{ msg->strVar["deck_cited"] = pos == string::npos + ? key + : (++msg->intMsgCnt, msg->readAttrName()) }; + if (key.empty()) { + msg->reply(GlobalMsg["strDeckNameEmpty"]); + } + else if (decks.size() > 9 && !decks.count(key)) { + msg->reply(GlobalMsg["strDeckListFull"]); + } + else { + vector DeckSet = {}; + if ((strCiteDeck == "ȺԱ" || (strCiteDeck == "member" && !(strCiteDeck = "ȺԱ").empty())) && msg->fromChat.second == CQ::msgtype::Group) { + vector list = CQ::getGroupMemberList(msg->fromGroup); + if (list.empty()) { + msg->reply("ȺԱбȡʧܡ"); + } + for (auto& each : list) { + DeckSet.push_back( + (each.GroupNick.empty() ? each.Nick : each.GroupNick) + "(" + to_string(each.QQID) + ")"); + } + decks[key] = DeckSet; + } + else if (strCiteDeck == "range") { + string strL = msg->readDigit(); + string strR = msg->readDigit(); + string strStep = msg->readDigit(); //ַ֧ + if (strL.empty()) { + msg->reply(GlobalMsg["strRangeEmpty"]); + return; + } + else if (strL.length() > 16 || strR.length() > 16) { + msg->reply(GlobalMsg["strOutRange"]); + return; + } + else { + long long llBegin{ stoll(strL) }; + long long llEnd{ strR.empty() ? 0 : stoll(strR) }; + long long llStep{ strStep.empty() ? 1 : stoll(strStep) }; + if (llBegin == llEnd) { + msg->reply(GlobalMsg["strRangeEmpty"]); + return; + } + else if (llBegin > llEnd) { + long long temp = llBegin; + llBegin = llEnd; + llEnd = temp; + } + if ((llEnd - llBegin) > 1000 * llStep) { + msg->reply(GlobalMsg["strOutRange"]); + return; + } + for (long long card = llBegin; card <= llEnd; card += llStep) { + DeckSet.emplace_back(to_string(card)); + } + decks[key] = DeckSet; + strCiteDeck += to_string(llBegin) + "~" + *(--DeckSet.end()); + } + } + else if (!CardDeck::mPublicDeck.count(strCiteDeck) || strCiteDeck[0] == '_') { + msg->reply(GlobalMsg["strDeckCiteNotFound"]); + return; + } + else { + decks[key] = CardDeck::mPublicDeck[strCiteDeck]; + } + if (key == strCiteDeck)msg->reply(GlobalMsg["strDeckSet"], { msg->strVar["deck_name"] }); + else msg->reply(GlobalMsg["strDeckSetRename"]); + update(); + } +} +void DiceSession::deck_new(FromMsg* msg) { + string& key{ msg->strVar["deck_name"] }; + size_t pos = msg->strMsg.find('=', msg->intMsgCnt); + if (pos == string::npos) { + key = "new"; + } + else { + key = msg->readAttrName(); + msg->intMsgCnt = pos + 1; + } + if (decks.size() > 9 && !decks.count(key)) { + msg->reply(GlobalMsg["strDeckListFull"]); + } + else { + vector DeckSet = {}; + msg->readItems(DeckSet); + if (DeckSet.empty()) { + msg->reply(GlobalMsg["strDeckNewEmpty"]); + return; + } + else if (DeckSet.size() > 256) { + msg->reply(GlobalMsg["strDeckOversize"]); + return; + } + DeckInfo deck{ DeckSet }; + if (deck.idxs.size() > 1024) { + msg->reply(GlobalMsg["strDeckOversize"]); + return; + } + decks[key] = std::move(deck); + msg->reply(GlobalMsg["strDeckNew"]); + update(); + } +} +void DiceSession::deck_draw(FromMsg* msg) { + string& key{ msg->strVar["deck_name"] }; + if (key.empty())key = msg->readAttrName(); + DeckInfo& deck = decks[key]; + int intCardNum = 1; + switch (msg->readNum(intCardNum)) { + case 0: + if (intCardNum == 0) { + msg->reply(GlobalMsg["strNumCannotBeZero"]); + return; + } + break; + case -1: break; + case -2: + msg->reply(GlobalMsg["strParaIllegal"]); + console.log(":" + printQQ(msg->fromQQ) + "" + GlobalMsg["strSelfName"] + "ʹ˷Ƿָ\n" + msg->strMsg, 1, + printSTNow()); + return; + } + ResList Res; + while (!deck.idxs.empty()&&intCardNum--) { + Res << deck.draw(); + if (deck.idxs.empty())break; + } + if(!Res.empty()){ + msg->strVar["res"] = Res.dot("|").show(); + msg->strVar["cnt"] = to_string(Res.size()); + msg->initVar({ msg->strVar["pc"], msg->strVar["res"] }); + if (msg->strVar.count("hidden")) { + msg->reply(GlobalMsg["strDrawHidden"]); + msg->replyHidden(GlobalMsg["strDrawCard"]); + } + else + msg->reply(GlobalMsg["strDrawCard"]); + update(); + } + if (deck.idxs.empty()) { + msg->reply(GlobalMsg["strDeckRestEmpty"]); + } +} +void DiceSession::deck_show(FromMsg* msg) { + if (decks.empty()) { + msg->reply(GlobalMsg["strDeckListEmpty"]); + return; + } + string& strDeckName{ msg->strVar["deck_name"] = msg->readAttrName() }; + //Ĭгƶ + if (strDeckName.empty()) { + ResList res; + for (auto& [key, val] : decks) { + res << key + "[" + to_string(val.sizRes) + "/" + to_string(val.idxs.size()) + "]"; + } + msg->strVar["res"] = res.show(); + msg->reply(GlobalMsg["strDeckListShow"]); + } + else { + if (decks.count(strDeckName)) { + DeckInfo& deck{ decks[strDeckName] }; + ResList residxs; + size_t idx(0); + while (idx < deck.sizRes) { + residxs << deck.meta[deck.idxs[idx++]]; + } + msg->strVar["deck_rest"] = residxs.dot(" | ").show(); + msg->reply(GlobalMsg["strDeckRestShow"]); + } + else { + msg->reply(GlobalMsg["strDeckNotFound"]); + } + } +} +void DiceSession::deck_reset(FromMsg* msg) { + string& key{ msg->strVar["deck_name"] = msg->readAttrName() }; + if (key.empty())key = msg->readDigit(); + if (key.empty()) { + msg->reply(GlobalMsg["strDeckNameEmpty"]); + } + else if (!decks.count(key)) { + msg->reply(GlobalMsg["strDeckNotFound"]); + } + else { + decks[key].reset(); + msg->reply(GlobalMsg["strDeckidxsReset"]); + update(); + } +} +void DiceSession::deck_del(FromMsg* msg) { + string& key{ msg->strVar["deck_name"] = msg->readAttrName() }; + if (key.empty())key = msg->readDigit(); + if (key.empty()) { + msg->reply(GlobalMsg["strDeckNameEmpty"]); + } + else if (!decks.count(key)) { + msg->reply(GlobalMsg["strDeckNotFound"]); + } + else { + decks.erase(key); + msg->reply(GlobalMsg["strDeckDelete"]); + update(); + } +} +void DiceSession::deck_clr(FromMsg* msg) { + decks.clear(); + msg->reply(GlobalMsg["strDeckListClr"]); + update(); +} + +std::mutex exSessionSave; + void DiceSession::save() const { mkDir(DiceDir + "\\user\\session"); string pathFile = (type == "solo") ? (DiceDir + R"(\user\session\Q)" + to_string(~room) + ".json" ) : (DiceDir + R"(\user\session\)" + to_string(room) + ".json"); - ofstream fout(pathFile); - if (!fout) - { - console.log("Ϣʧ:" + DiceDir + R"(\user\session\)" + to_string(room), 1); - return; - } nlohmann::json jData; - jData["type"] = type; - jData["room"] = room; - jData["create_time"] = tCreate; - jData["update_time"] = tUpdate; if (!sOB.empty())jData["observer"] = sOB; if (!mTable.empty()) for (auto& [key, table] : mTable) @@ -271,9 +519,38 @@ void DiceSession::save() const jLink["linking"] = linker.isLinking; jData["link"] = jLink; } + if (!decks.empty()) { + json jDecks; + for (auto& [key,deck]:decks) { + jDecks[GBKtoUTF8(key)] = { + {"meta",GBKtoUTF8(deck.meta)}, + {"idxs",deck.idxs}, + {"size",deck.sizRes} + }; + } + jData["decks"] = jDecks; + } + std::lock_guard lock(exSessionSave); + if (jData.empty()) { + remove(pathFile.c_str()); + return; + } + jData["type"] = type; + jData["room"] = room; + jData["create_time"] = tCreate; + jData["update_time"] = tUpdate; + ofstream fout(pathFile); + if (!fout) { + console.log("Ϣʧ:" + pathFile, 1); + return; + } fout << jData.dump(1); } +bool DiceTableMaster::has_session(long long group) { + return mSession.count(group); +} + Session& DiceTableMaster::session(long long group) { if (!mSession.count(group)) @@ -346,6 +623,21 @@ int DiceTableMaster::load() LinkList[pSession->linker.linkFwd] = { pSession->room,pSession->linker.typeLink != "to" }; } } + if (j.count("decks")) { + json& jDecks = j["decks"]; + for (auto it = jDecks.cbegin(); it != jDecks.cend(); ++it) { + std::string key = UTF8toGBK(it.key()); + pSession->decks[key].meta = UTF8toGBK(it.value()["meta"].get>()); + if (it.value().count("rest")) { + it.value()["rest"].get_to(pSession->decks[key].idxs); + pSession->decks[key].sizRes = pSession->decks[key].meta.size(); + } + else { + it.value()["idxs"].get_to(pSession->decks[key].idxs); + it.value()["size"].get_to(pSession->decks[key].sizRes); + } + } + } if (j["type"] == "simple") { if (j.count("observer")) pSession->sOB = j["observer"].get>(); diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index 5e86e37d..343bc405 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -4,6 +4,7 @@ #include #include #include +#include "STLExtern.hpp" using std::pair; using std::string; @@ -37,6 +38,19 @@ struct LinkInfo { long long linkFwd{ 0 }; }; +struct DeckInfo { + //Ԫ + vector meta; + //ʣ + vector idxs; + size_t sizRes; + DeckInfo() = default; + DeckInfo(const vector& deck); + void init(); + void reset(); + string draw(); +}; + class DiceSession { //ֵ @@ -47,6 +61,8 @@ class DiceSession LogInfo logger; // LinkInfo linker; + //ƶ + map decks; public: string type; //Ⱥ @@ -84,10 +100,11 @@ class DiceSession return *this; } - [[nodiscard]] bool table_count(string key) const { return mTable.count(key); } - int table_add(string, int, string); - [[nodiscard]] string table_prior_show(string key) const; - bool table_clr(string key); + [[nodiscard]] bool table_count(const string& key) const { return mTable.count(key); } + bool table_del(const string&, const string&); + int table_add(const string&, int, const string&); + [[nodiscard]] string table_prior_show(const string& key) const; + bool table_clr(const string& key); //Թָ void ob_enter(FromMsg*); @@ -116,6 +133,16 @@ class DiceSession void link_close(FromMsg*); [[nodiscard]] bool is_linking() const { return linker.isLinking; } + //deckָ + void deck_set(FromMsg*); + void deck_draw(FromMsg*); + void deck_show(FromMsg*); + void deck_reset(FromMsg*); + void deck_del(FromMsg*); + void deck_clr(FromMsg*); + void deck_new(FromMsg*); + [[nodiscard]] bool has_deck(const string& key) const { return decks.count(key); } + void save() const; }; @@ -132,6 +159,7 @@ class DiceTableMaster public: map> mSession; Session& session(long long group); + bool has_session(long long group); void session_end(long long group); //void save(); int load(); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index bf997d3d..9f92df36 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -57,8 +57,12 @@ std::map GlobalMsg {"strLogUpSuccess","{self}־ϴ\n https://logpainter.kokona.tech/?s3={log_file} Բ鿴¼"}, {"strLogUpFailure","{self}ϴ־ļʧܣڵ{retry}ԡ{ret}"}, {"strLogUpFailureEnd","ź{self}޷ɹϴ־ļ\n{ret}\nȡϵMaster:{master_QQ}\nļ:{log_file}"}, - {"strGMTableShow","{self}¼{table_name}б"}, - {"strGMTableNotExist","{self}ûб{table_name}¼"}, + {"strGMTableShow","{self}¼{table_name}б: {res}"}, + {"strGMTableClr","{self}{table_name}"}, + {"strGMTableItemDel","{self}Ƴ{table_name}Ŀ{table_item}"}, + {"strGMTableNotExist","{self}ûб{table_name}"}, + {"strGMTableItemNotFound","{self}ûҵ{table_name}Ŀ{table_item}"}, + {"strGMTableItemEmpty","֪{self}Ƴ{table_name}бĿ"}, {"strUserTrustShow","{user}{self}μΪ{trust}"}, {"strUserTrusted","ѽ{self}{user}μΪ{trust}"}, {"strUserTrustDenied","{nick}{self}ȨʶԷȨޡ"}, @@ -111,6 +115,7 @@ std::map GlobalMsg {"strPcInitDelErr","{nick}ijʼɾ"}, {"strPcNoteTooLong","עȲܳ255"}, {"strPcTextTooLong","ıȲܳ48"}, + {"strCOCBuild","{pc}ĵԱ:{res}"}, {"strCensorCaution","ѣ{nick}ָдʣ{self}ϱ"}, {"strCensorWarning","棺{nick}ָдʣ{self}Ѽ¼ϱ"}, {"strCensorDanger","棺{nick}ָдʣ{self}ָܾϱ"}, @@ -128,15 +133,24 @@ std::map GlobalMsg {"strWhiteQQAddNotice","{user_nick}ѻ{self}Σ뾡ʹ{self}"}, {"strWhiteQQDenied","㲻{self}εû"}, {"strWhiteGroupDenied","ȺIJڰС"}, - {"strDeckProNew","½Զƶѡ"}, - {"strDeckProSet","ѽ{deck_name}ΪĬƶѡ"}, - {"strDeckProClr","ɾĬƶѡ"}, - {"strDeckProNull","ĬƶѲ!"}, - {"strDeckTmpReset","ÿơ"}, - {"strDeckTmpShow","ǰʣ࿨:"}, - {"strDeckTmpEmpty","ʣ࿨ƣ"}, //ʣ࿨Ϊ0 - {"strDeckTmpNotFound","ʣ࿨ơ"}, //ûɹƶ + {"strDeckNew","{self}Ϊ{nick}Զƶ<{deck_name}>"}, + {"strDeckSet","{nick}<{deck_name}>{self}ƶʵ"}, + {"strDeckSetRename","{nick}<{deck_cited}>{self}ƶʵ{deck_name}"}, + {"strDeckRestEmpty","ƶ<{deck_name}>ѳգʹ.deck reset {deck_name}ֶƶ"}, + {"strDeckOversize","{nick}̫࣬{self}װ"}, + {"strDeckRestShow","ǰƶ<{deck_name}>ʣ࿨:{deck_rest}"}, + {"strDeckRestReset","{self}ƶʵ<{deck_name}>"}, + {"strDeckDelete","{self}Ƴƶʵ<{deck_name}>"}, + {"strDeckListShow","{self}ƶʵ:{res}"}, + {"strDeckListClr","{nick}{self}ƶʵ"}, + {"strDeckListEmpty","{self}ƶʵбΪգ"}, + {"strDeckNewEmpty","{self}޷Ϊ{nick}½ƶѡ"}, + {"strDeckListFull","{self}ƶʵѴޣʵ"}, + {"strDeckNotFound","{self}Ҳƶ{deck_name}"}, + {"strDeckCiteNotFound","{self}Ҳƶ{deck_cited}" }, {"strDeckNameEmpty","δָƶ"}, + {"strRangeEmpty","{self}ûſ" }, + {"strOutRange","{nick}г{self}Χ" }, {"strRollDice","{pc}: {res}"}, {"strRollDiceReason","{pc} {reason}: {res}"}, {"strRollHidden","{pc}һΰ"}, @@ -179,6 +193,7 @@ std::map GlobalMsg {"strClockOffWork","{self}Ѱʱرա"}, {"strNameGenerator","{pc}ƣ{res}"}, {"strDrawCard", "{pc}鵽ʲô{res}"}, + {"strDrawHidden", "{pc}{cnt}ơ" }, {"strMeOn", "ɹ{self}.me"}, {"strMeOff", "ɹ{self}.me"}, {"strMeOnAlready", "{self}.meûб!"}, @@ -334,6 +349,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +568:.deckԶƶ 567:дʼ 566:.helpѯ 565:.log־¼ @@ -407,7 +423,21 @@ const std::map HelpDoc = { .log off ͣ¼ .log end ɼ¼־ļ ־ϴʧܿܣʱϵ̨ȡ)"}, -{"deck","ָĬƶѣʹ.drawָƶʱʹôƶѡƶѲŻֱһźϴơ\n.deck set ƶ Ĭƶ\n.deck set 1-100 ָȵ\n.deck show 鿴ʣ࿨\n.deck reset ʣ࿨\n.deck new Զƶѣÿո|ָ޶\n.deck new е|޵|޵|޵|޵|޵\nshowȺڲҪȨ"}, +{"deck",R"(ƶʵ.deck +ָȺƶѣʹ.drawʱƶʵȼͬ +ƲŻֱ +ÿȺƶбౣ10ƶ +.deck set ([ƶʵ]=)[ƶ] //ӹƶѴʵ +.deck set ([ƶʵ]=)member //ȺԱбʵ +.deck set ([ƶʵ]=)range [] [] //ȲΪʵ +.deck show //鿴ƶʵб +.deck show [ƶ] //鿴ʣ࿨ +.deck reset [ƶ] //ʣ࿨ +.deck clr //ʵ +.deck new [ƶ]=[1](...|[n]) //Զƶ +: +.deck new ˹=е|޵|޵|޵|޵|޵ +showȺڲҪȨ)"}, {"Ⱥ","&dismiss"}, {"Ⱥָ","&dismiss"}, {"dismiss","ָҪȺԱȨޣʹú˳Ⱥ\n!dismiss [ĿQQ(ĩλ)]ָȺ\n!dismissú;Ĭ״ֻ̬ҪЧ"}, @@ -497,14 +527,19 @@ const std::map HelpDoc = { {"", "&draw"}, { "draw", - "ƣ.draw [ƶ] ([])\t//鵽ƲŻأܳƶ\nǰѰװƶ:{ȫƶб}" + R"(ƣ.draw [ƶ] ([]) +.draw _[ƶ] ([]) //飬˽ķ +.draw _ɱ //ƽͨ˽Ļȡob +*ƶƿǹƶƶʵ +*鵽ƲŻأƶѳպ޷ +*鿴{self}Ѱװƶѣ.help ȫƶб.help չƶ)" }, { "չƶ","{list_extern_deck}" }, { "ȫƶб","{list_all_deck}" }, {"ȹ", "&ri"}, {"ri", "ȹȺ޶.ri([ֵ])([dz])\n.ri -1 ijpc\t//Զȹб\n.ri +5 boss"}, {"ȹб", "&init"}, - {"init", "ȹб\n.init\t//鿴ȹб\n.init clr\t//ȹб"}, + {"init", "ȹб\n.init list\t//鿴ȹб\n.init clr\t//ȹб\n.init del [Ŀ]\t//ȹбƳĿ"}, {"", "&ww"}, {"ww", "أ.w(w) [Ӹ]a[]\n.wֱӸ.wwÿӵĵ\n̶10ÿһӵﵽһΣﵽ8\n÷οϷ"}, {"˳", "&me"}, diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 4c64aa2d..7bdfb53a 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -36,9 +36,9 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 567u; -inline const std::string Dice_Ver_Without_Build = "2.4.1"; -constexpr auto DiceRequestHeader = "Dice/2.4.1"; +const unsigned short Dice_Build = 568u; +inline const std::string Dice_Ver_Without_Build = "2.4.2beta1"; +constexpr auto DiceRequestHeader = "Dice/2.4.2beta1"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 6483ec43..4941fef5 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -43,6 +43,7 @@ User& getUser(long long qq) short trustedQQ(long long qq) { if (qq == console.master())return 256; + if (qq == console.DiceMaid)return 255; else if (!UserList.count(qq))return 0; else return UserList[qq].nTrust; } diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index c00f7888..56e15896 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -149,6 +149,21 @@ class ResList } }; +//Ŀ +class AttrList { + std::unordered_map mItem; + std::vector vKey; +public: + string show() { + string res; + int index = 0; + for (auto& key : vKey) { + res += "\n" + key + "=" + mItem[key]; + } + return res; + } +}; + template std::string listKey(std::map m) { diff --git a/Dice/RD.cpp b/Dice/RD.cpp index 9ef88642..f5c2de0a 100644 --- a/Dice/RD.cpp +++ b/Dice/RD.cpp @@ -269,7 +269,6 @@ void COC6D(string& strMAns) void COC7(string& strMAns, int intNum) { - strMAns += ":"; string strProperty[] = {"", "", "", "", "ò", "", "־", "", ""}; string strRoll[] = {"3D6", "3D6", "2D6+6", "3D6", "3D6", "2D6+6", "3D6", "2D6+6", "3D6"}; int intAllTotal = 0; @@ -292,7 +291,6 @@ void COC7(string& strMAns, int intNum) void COC6(string& strMAns, int intNum) { - strMAns += ":"; string strProperty[] = {"", "", "", "", "ò", "", "־", "", "ʲ"}; string strRoll[] = {"3D6", "3D6", "2D6+6", "3D6", "3D6", "2D6+6", "3D6", "3D6+3", "1D10"}; const bool boolAddSpace = intNum != 1; diff --git a/Dice/SHKQuerier.h b/Dice/SHKQuerier.h index 2e190a3e..8148a6ea 100644 --- a/Dice/SHKQuerier.h +++ b/Dice/SHKQuerier.h @@ -15,7 +15,7 @@ using std::unordered_set; struct WordNode { //ýڵķִʣ֡ĸGBK - string word; + //string word; unordered_setkeys; unordered_map>next; }; @@ -62,12 +62,12 @@ class WordQuerier { unordered_set res; unordered_set sInter; const WordNode* last_word = nullptr; - for (auto& word : words) { + for (const auto& word : words) { //޸ôʣԹ if (!word_list.count(word))continue; //׺ - if (last_word && !last_word->next.find(word)->second.empty()) { - for (auto& w : last_word->next.find(word)->second) { + if (last_word && last_word->next.find(word)!= last_word->next.end()) { + for (const auto& w : last_word->next.find(word)->second) { if (res.count(w))sInter.insert(w); } if (!sInter.empty()) { diff --git a/Dice/STLExtern.hpp b/Dice/STLExtern.hpp index 014fe6dd..2c2ed45c 100644 --- a/Dice/STLExtern.hpp +++ b/Dice/STLExtern.hpp @@ -129,6 +129,3 @@ class PriorList } }; -class FormStep -{ -}; diff --git a/Dice/com.w4123.dice.json b/Dice/com.w4123.dice.json index 078cd3aa..dcb7a498 100644 --- a/Dice/com.w4123.dice.json +++ b/Dice/com.w4123.dice.json @@ -2,8 +2,8 @@ "ret": 1, "apiver": 9, "name": "Dice!", - "version": "2.4.1", - "version_id": 567, + "version": "2.4.2beta1", + "version_id": 568, "author": "w4123溯洄 & Shiki", "description": "跑团用骰子 本程序使用AGPLv3开源协议授权 Copyright (c) 2018-2020 w4123溯洄 & Shiki", "event": [ From e1e7bcab5439447963064d93613f2e0aa1d1e89f Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Tue, 27 Oct 2020 11:25:37 +0800 Subject: [PATCH 037/171] update 569 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新.rc暗骰机制 --- Dice/DiceEvent.cpp | 16 +++++++++++++++- Dice/GlobalVar.cpp | 6 ++++-- Dice/GlobalVar.h | 6 +++--- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 1bd3f613..27cabdae 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -3127,6 +3127,11 @@ int FromMsg::DiceReply() ? get(chat(fromGroup).intConf, string("rc"), 0) : get(getUser(fromQQ).intConf, string("rc"), 0); int intTurnCnt = 1; + bool isHidden(false); + if (strMsg[intMsgCnt] == '_') { + isHidden = true; + ++intMsgCnt; + } if (strMsg.find('#') != string::npos) { string strTurnCnt = strMsg.substr(intMsgCnt, strMsg.find('#') - intMsgCnt); @@ -3162,6 +3167,10 @@ int FromMsg::DiceReply() } } readSkipSpace(); + if (strMsg[intMsgCnt] == '_') { + isHidden = true; + ++intMsgCnt; + } if (strMsg.length() == intMsgCnt) { strVar["attr"] = GlobalMsg["strEnDefaultName"]; @@ -3342,7 +3351,12 @@ int FromMsg::DiceReply() } strReply += Res.show(); } - reply(); + if (isHidden) { + replyHidden(); + reply(GlobalMsg["strRollSkillHidden"]); + } + else + reply(); return 1; } else if (strLowerMessage.substr(intMsgCnt, 2) == "ri" && intT) diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 9f92df36..b4ba6954 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -159,6 +159,7 @@ std::map GlobalMsg {"strRollMultiDiceReason","{pc}{turn}{reason}: {dice_exp}={res}"}, {"strRollSkill","{pc}{attr}춨"}, {"strRollSkillReason","{reason} {pc}{attr}춨"}, + {"strRollSkillHidden","{pc}һΰ{attr}춨" }, {"strEnRoll","{pc}{attr}ǿɳ춨\n{res}"},//{attr}ûʡԼ滻Ϊ{strEnDefaultName} {"strEnRollNotChange","{strEnRoll}\n{pc}{attr}ֵûб仯"}, {"strEnRollFailure","{strEnRoll}\n{pc}{attr}仯{change}㣬ǰΪ{final}"}, @@ -349,6 +350,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +569:.rc/.draw 568:.deckԶƶ 567:дʼ 566:.helpѯ @@ -362,7 +364,6 @@ const std::map HelpDoc = { 558:ÿͳ 557:ʱҵϵͳ 556:ϵͳ -555:û¼/Ⱥϵͳ 554:ɫ 553:nameܵ 552:̨ϵͳ @@ -410,6 +411,7 @@ const std::map HelpDoc = { R"([ҳ]ָ .nn dz .draw +.deck ƶʵ .name .jrrp Ʒ .welcome Ⱥӭ @@ -496,7 +498,7 @@ const std::map HelpDoc = { {"춨", "&rc/ra"}, { "rc/ra", - "춨ָ.rc/ra []([ɹ])\nɫʱʡԳɹ\n.rc*5\t//ʹ+-*/˳ҪΪ˷>Ӽ>\n.rc \t//ͷѺͼѻᱻΪؼ\n.rc -10\t//ɹʱ1-1000\n.rcp ǹ\t//9\nĬԹжɹʧܵķ.setcoc" + "춨ָ.rc/ra (_)([춨]#)([Ѷ])[]( [ɹ])\nɫʱʡԳɹ\n.rc*5\t//ʹ+-*/˳ҪΪ˷>Ӽ>\n.rc \t//ͷѺͼѻᱻΪؼ\n.rc _ѧ50\t//˼Թ߿ɼ\n.rc -10\t//ɹʱ1-1000\n.rcp ǹ\t//9\nĬԹжɹʧܵķ.setcoc" }, { "", diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 7bdfb53a..45348485 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -36,9 +36,9 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 568u; -inline const std::string Dice_Ver_Without_Build = "2.4.2beta1"; -constexpr auto DiceRequestHeader = "Dice/2.4.2beta1"; +const unsigned short Dice_Build = 569u; +inline const std::string Dice_Ver_Without_Build = "2.4.2beta2"; +constexpr auto DiceRequestHeader = "Dice/2.4.2beta2"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; From dc249a5b0c73afd0b4b88f049151180f4e440b28 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Tue, 27 Oct 2020 16:24:38 +0800 Subject: [PATCH 038/171] release 2.4.2beta2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 自定义化coc/dnd回执 --- Dice/DiceEvent.cpp | 19 ++++++++----------- Dice/GlobalVar.cpp | 1 + Dice/RD.cpp | 3 --- Dice/com.w4123.dice.json | 4 ++-- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 27cabdae..8ac9f90f 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1437,16 +1437,14 @@ int FromMsg::DiceReply() } else if (strLowerMessage.substr(intMsgCnt, 5) == "coc7d" || strLowerMessage.substr(intMsgCnt, 4) == "cocd") { - strReply = strVar["nick"]; - COC7D(strReply); - reply(strReply); + COC7D(strVar["res"]); + reply(GlobalMsg["strCOCBuild"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 5) == "coc6d") { - strReply = strVar["nick"]; - COC6D(strReply); - reply(strReply); + COC6D(strVar["res"]); + reply(GlobalMsg["strCOCBuild"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 5) == "group") @@ -1970,9 +1968,8 @@ int FromMsg::DiceReply() reply(GlobalMsg["strCharacterCannotBeZero"]); return 1; } - strReply = strVar["nick"]; - COC6(strReply, intNum); - reply(strReply); + COC6(strVar["res"], intNum); + reply(GlobalMsg["strCOCBuild"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 4) == "deck") @@ -2517,8 +2514,8 @@ int FromMsg::DiceReply() return 1; } string strReply = strVar["pc"]; - DND(strReply, intNum); - reply(strReply); + DND(strVar["res"], intNum); + reply(GlobalMsg["strDNDBuild"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 3) == "log") { diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index b4ba6954..51554a0d 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -116,6 +116,7 @@ std::map GlobalMsg {"strPcNoteTooLong","עȲܳ255"}, {"strPcTextTooLong","ıȲܳ48"}, {"strCOCBuild","{pc}ĵԱ:{res}"}, + {"strDNDBuild","{pc}Ӣ:{res}"}, {"strCensorCaution","ѣ{nick}ָдʣ{self}ϱ"}, {"strCensorWarning","棺{nick}ָдʣ{self}Ѽ¼ϱ"}, {"strCensorDanger","棺{nick}ָдʣ{self}ָܾϱ"}, diff --git a/Dice/RD.cpp b/Dice/RD.cpp index f5c2de0a..03314603 100644 --- a/Dice/RD.cpp +++ b/Dice/RD.cpp @@ -57,7 +57,6 @@ void COC7D(string& strMAns) { RD rd3D6("3D6"); RD rd2D6p6("2D6+6"); - strMAns += ":"; strMAns += '\n'; strMAns += "STR=3D6*5="; rd3D6.Roll(); @@ -174,7 +173,6 @@ void COC6D(string& strMAns) RD rd2D6p6("2D6+6"); RD rd3D6p3("3D6+3"); RD rd1D10("1D10"); - strMAns += ":"; strMAns += '\n'; strMAns += "STR=3D6="; rd3D6.Roll(); @@ -317,7 +315,6 @@ void COC6(string& strMAns, int intNum) void DND(string& strOutput, int intNum) { - strOutput += "Ӣ:"; const RD rdDND("4D6K3"); string strDNDName[6] = {"", "", "", "", "֪", ""}; const bool boolAddSpace = intNum != 1; diff --git a/Dice/com.w4123.dice.json b/Dice/com.w4123.dice.json index dcb7a498..622e37de 100644 --- a/Dice/com.w4123.dice.json +++ b/Dice/com.w4123.dice.json @@ -2,8 +2,8 @@ "ret": 1, "apiver": 9, "name": "Dice!", - "version": "2.4.2beta1", - "version_id": 568, + "version": "2.4.2beta2", + "version_id": 569, "author": "w4123溯洄 & Shiki", "description": "跑团用骰子 本程序使用AGPLv3开源协议授权 Copyright (c) 2018-2020 w4123溯洄 & Shiki", "event": [ From 3f45474f9f3692a7dd6976d64eca622cf66d8714 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 28 Oct 2020 00:34:27 +0800 Subject: [PATCH 039/171] fix deck MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复牌堆实例余量判定bug --- Dice/DiceSession.cpp | 23 ++++++++++++++++------- Dice/DiceSession.h | 3 ++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index eae864d8..b2535134 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -378,10 +378,19 @@ void DiceSession::deck_new(FromMsg* msg) { update(); } } -void DiceSession::deck_draw(FromMsg* msg) { - string& key{ msg->strVar["deck_name"] }; - if (key.empty())key = msg->readAttrName(); - DeckInfo& deck = decks[key]; +string DiceSession::deck_draw(const string& key) { + if (decks.count(key)) { + if (!decks[key].sizRes)return "{strDeckRestEmpty}"; + return decks[key].draw(); + } + else if (CardDeck::mPublicDeck.count(key)) { + return CardDeck::draw(key); + } + return ""; +} +void DiceSession::_draw(FromMsg* msg) { + if (msg->strVar["deck_name"].empty())msg->strVar["deck_name"] = msg->readAttrName(); + DeckInfo& deck = decks[msg->strVar["deck_name"]]; int intCardNum = 1; switch (msg->readNum(intCardNum)) { case 0: @@ -398,9 +407,9 @@ void DiceSession::deck_draw(FromMsg* msg) { return; } ResList Res; - while (!deck.idxs.empty()&&intCardNum--) { + while (deck.sizRes && intCardNum--) { Res << deck.draw(); - if (deck.idxs.empty())break; + if (!deck.sizRes)break; } if(!Res.empty()){ msg->strVar["res"] = Res.dot("|").show(); @@ -460,7 +469,7 @@ void DiceSession::deck_reset(FromMsg* msg) { } else { decks[key].reset(); - msg->reply(GlobalMsg["strDeckidxsReset"]); + msg->reply(GlobalMsg["strDeckRestReset"]); update(); } } diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index 343bc405..f014d7b3 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -135,7 +135,8 @@ class DiceSession //deckָ void deck_set(FromMsg*); - void deck_draw(FromMsg*); + string deck_draw(const string&); + void _draw(FromMsg*); void deck_show(FromMsg*); void deck_reset(FromMsg*); void deck_del(FromMsg*); From d4f8d7492cd7eaacaa54fbe7a4f7ba08309a49e5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sun, 15 Nov 2020 03:08:16 -0800 Subject: [PATCH 040/171] Add coc conf 6 --- Dice/DiceEvent.cpp | 3 +++ Dice/RD.cpp | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 27cabdae..0c96520c 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1285,6 +1285,9 @@ int FromMsg::DiceReply() case 5: reply(GlobalMsg["strDefaultCOCSet"] + "5\n1-2<֮һɹ\n5096-100ʧܣ5099-100ʧ"); break; + case 6: + reply(GlobalMsg["strDefaultCOCSet"] + "6\n1ͬ<=ɹʴɹ\n100ͬ>=ɹʴʧ"); + break; default: reply(GlobalMsg["strDefaultCOCNotFound"]); return 1; diff --git a/Dice/RD.cpp b/Dice/RD.cpp index f5c2de0a..c8a25620 100644 --- a/Dice/RD.cpp +++ b/Dice/RD.cpp @@ -451,6 +451,19 @@ int RollSuccessLevel(int res, int rate, int rule) if (rate >= 50 || res < 96)return 1; return 0; break; + case 6: + if (res > rate) { + if (res == 100 || res % 11 == 0) { + return 0; + } + return 1; + } else { + if (res == 1 || res % 11 == 0) { + return 5; + } + return 2; + } + break; default: return -1; } } From 84a3ba28cf99ba9a29c324eaacbe59f09aac4d83 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sun, 15 Nov 2020 03:10:53 -0800 Subject: [PATCH 041/171] Fix conf --- Dice/DiceEvent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 0c96520c..78dc9b22 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1286,7 +1286,7 @@ int FromMsg::DiceReply() reply(GlobalMsg["strDefaultCOCSet"] + "5\n1-2<֮һɹ\n5096-100ʧܣ5099-100ʧ"); break; case 6: - reply(GlobalMsg["strDefaultCOCSet"] + "6\n1ͬ<=ɹʴɹ\n100ͬ>=ɹʴʧ"); + reply(GlobalMsg["strDefaultCOCSet"] + "6\nɫ\n1ͬ<=ɹʴɹ\n100ͬ>ɹʴʧ"); break; default: reply(GlobalMsg["strDefaultCOCNotFound"]); From b915b6f218f47361ca498bfde8c6a126844ae3d7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 16 Nov 2020 22:02:40 -0800 Subject: [PATCH 042/171] Fix description --- Dice/GlobalVar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index b4ba6954..8644ea58 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -506,7 +506,7 @@ const std::map HelpDoc = { }, { "setcoc", - "ΪǰȺCOC棬.setcoc 1,ǰ0-5\n0 \n1ɹ\n5096 - 100ʧܣ50100ʧ\n1\n501ɹ501 - 5ɹ\n5096 - 100ʧܣ50100ʧ\n2\n1 - 5 <= ɹʴɹ\n10096 - 99 > ɹʴʧ\n3\n1 - 5ɹ\n96 - 100ʧ\n4\n1 - 5 <= ʮ֮һɹ\n50 >= 96 + ʮ֮һʧܣ50100ʧ\n5\n1 - 2 < ֮һɹ\n5096 - 100ʧܣ5099 - 100ʧ\n" + "ΪǰȺCOC棬.setcoc 1,ǰ0-5\n0 \n1ɹ\n5096 - 100ʧܣ50100ʧ\n1\n501ɹ501 - 5ɹ\n5096 - 100ʧܣ50100ʧ\n2\n1 - 5 <= ɹʴɹ\n10096 - 99 > ɹʴʧ\n3\n1 - 5ɹ\n96 - 100ʧ\n4\n1 - 5 <= ʮ֮һɹ\n50 >= 96 + ʮ֮һʧܣ50100ʧ\n5\n1 - 2 < ֮һɹ\n5096 - 100ʧܣ5099 - 100ʧ\n6 ɫ\n1ͬ<=ɹʴɹ\n100ͬ>ɹʴʧ" R"(򿪷߷ ΣȺڼ춨ֻȺãڳԱԵ)" }, From e8b4309171dd021a6f38a7c7e0e49e02e525326c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 16 Nov 2020 22:31:38 -0800 Subject: [PATCH 043/171] Better dsc --- Dice/GlobalVar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 8644ea58..d19a897d 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -506,7 +506,7 @@ const std::map HelpDoc = { }, { "setcoc", - "ΪǰȺCOC棬.setcoc 1,ǰ0-5\n0 \n1ɹ\n5096 - 100ʧܣ50100ʧ\n1\n501ɹ501 - 5ɹ\n5096 - 100ʧܣ50100ʧ\n2\n1 - 5 <= ɹʴɹ\n10096 - 99 > ɹʴʧ\n3\n1 - 5ɹ\n96 - 100ʧ\n4\n1 - 5 <= ʮ֮һɹ\n50 >= 96 + ʮ֮һʧܣ50100ʧ\n5\n1 - 2 < ֮һɹ\n5096 - 100ʧܣ5099 - 100ʧ\n6 ɫ\n1ͬ<=ɹʴɹ\n100ͬ>ɹʴʧ" + "ΪǰȺCOC棬.setcoc 1,ǰ0-5\n0 \n1ɹ\n5096 - 100ʧܣ50100ʧ\n1\n501ɹ501 - 5ɹ\n5096 - 100ʧܣ50100ʧ\n2\n1 - 5 <= ɹʴɹ\n10096 - 99 > ɹʴʧ\n3\n1 - 5ɹ\n96 - 100ʧ\n4\n1 - 5 <= ʮ֮һɹ\n50 >= 96 + ʮ֮һʧܣ50100ʧ\n5\n1 - 2 < ֮һɹ\n5096 - 100ʧܣ5099 - 100ʧ\n6 ɫ\n1ͬ<=ɹʴɹ\n100ͬ>ɹʴʧ\n" R"(򿪷߷ ΣȺڼ춨ֻȺãڳԱԵ)" }, From 6e1cbbd9802ad81a55aa96d33807c77f9d7fc5f5 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 4 Dec 2020 20:05:47 +0800 Subject: [PATCH 044/171] update 2.5.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增Lua脚本功能 --- CQSDK/CQAPI.h | 266 ----- CQSDK/CQAPI_EX.h | 259 ----- CQSDK/CQEVE.h | 289 ----- CQSDK/CQEVEBasic.h | 27 - CQSDK/CQEVEMsg.h | 50 - CQSDK/CQEVERequest.h | 18 - CQSDK/CQEVE_ALL.h | 15 - CQSDK/CQEVE_DiscussMsg.h | 45 - CQSDK/CQEVE_FriendAdd.h | 30 - CQSDK/CQEVE_GroupMsg.h | 94 -- CQSDK/CQEVE_PrivateMsg.h | 55 - CQSDK/CQEVE_RequestAddFriend.h | 42 - CQSDK/CQEVE_RequestAddGroup.h | 48 - CQSDK/CQEVE_Status.h | 57 - CQSDK/CQLogger.h | 54 - CQSDK/CQMsgCode.h | 150 --- CQSDK/CQMsgSend.h | 31 - CQSDK/CQconstant.h | 37 - CQSDK/cqdefine.h | 12 - CQSDKCPP/CQAPI.cpp | 19 - CQSDKCPP/CQAPI_EX.cpp | 504 -------- CQSDKCPP/CQEVE.cpp | 239 ---- CQSDKCPP/CQTools.cpp | 1 + CQSDKCPP/CQstream.cpp | 165 --- Dice/BlackListManager.cpp | 58 +- Dice/CQP.lib | Bin 9920 -> 0 bytes Dice/CharacterCard.h | 5 +- Dice/Dice.cpp | 349 +++--- Dice/Dice.vcxproj | 79 +- Dice/Dice.vcxproj.filters | 209 ++-- Dice/DiceCloud.cpp | 13 +- Dice/DiceCloud.h | 2 +- Dice/DiceConsole.cpp | 96 +- Dice/DiceConsole.h | 18 +- Dice/DiceEvent.cpp | 1991 ++++++++++++------------------- Dice/DiceEvent.h | 29 +- Dice/DiceFile.cpp | 29 +- Dice/DiceFile.hpp | 33 +- Dice/DiceGUI.cpp | 21 +- Dice/DiceJob.cpp | 278 +---- Dice/DiceJob.h | 8 +- Dice/DiceLua.cpp | 291 +++++ Dice/DiceLua.h | 13 + Dice/DiceMod.cpp | 136 ++- Dice/DiceMod.h | 27 +- Dice/DiceMsgSend.cpp | 10 +- Dice/DiceMsgSend.h | 8 +- Dice/DiceSchedule.cpp | 75 +- Dice/DiceSchedule.h | 2 + Dice/DiceSession.cpp | 23 +- Dice/EncodingConvert.cpp | 44 +- Dice/EncodingConvert.h | 44 +- Dice/GetRule.cpp | 4 +- Dice/GlobalVar.cpp | 41 +- Dice/GlobalVar.h | 18 +- Dice/ManagerSystem.cpp | 30 +- Dice/ManagerSystem.h | 18 +- Dice/MsgFormat.cpp | 4 +- Dice/MsgMonitor.cpp | 35 +- Dice/RD.cpp | 2 - Dice/RD.h | 11 +- Dice/RDConstant.h | 1 + Dice/SHKTrie.h | 12 + Dice/STLExtern.hpp | 1 + Dice/com.w4123.dice.json | 4 +- Lua/lapi.c | 1424 +++++++++++++++++++++++ Lua/lapi.h | 47 + Lua/lauxlib.c | 1059 +++++++++++++++++ Lua/lauxlib.h | 276 +++++ Lua/lbaselib.c | 527 +++++++++ Lua/lcode.c | 1814 +++++++++++++++++++++++++++++ Lua/lcode.h | 104 ++ Lua/lcorolib.c | 207 ++++ Lua/lctype.c | 64 + Lua/lctype.h | 101 ++ Lua/ldblib.c | 477 ++++++++ Lua/ldebug.c | 852 ++++++++++++++ Lua/ldebug.h | 52 + Lua/ldo.c | 822 +++++++++++++ Lua/ldo.h | 77 ++ Lua/ldump.c | 226 ++++ Lua/lfunc.c | 300 +++++ Lua/lfunc.h | 69 ++ Lua/lgc.c | 1710 +++++++++++++++++++++++++++ Lua/lgc.h | 189 +++ Lua/linit.c | 65 ++ Lua/liolib.c | 821 +++++++++++++ Lua/ljumptab.h | 112 ++ Lua/llex.c | 577 +++++++++ Lua/llex.h | 91 ++ Lua/llimits.h | 357 ++++++ Lua/lmathlib.c | 763 ++++++++++++ Lua/lmem.c | 202 ++++ Lua/lmem.h | 93 ++ Lua/loadlib.c | 759 ++++++++++++ Lua/lobject.c | 592 ++++++++++ Lua/lobject.h | 788 +++++++++++++ Lua/lopcodes.c | 104 ++ Lua/lopcodes.h | 392 +++++++ Lua/lopnames.h | 103 ++ Lua/loslib.c | 430 +++++++ Lua/lparser.c | 1996 ++++++++++++++++++++++++++++++++ Lua/lparser.h | 170 +++ Lua/lprefix.h | 45 + Lua/lstate.c | 470 ++++++++ Lua/lstate.h | 400 +++++++ Lua/lstring.c | 285 +++++ Lua/lstring.h | 58 + Lua/lstrlib.c | 1805 +++++++++++++++++++++++++++++ Lua/ltable.c | 924 +++++++++++++++ Lua/ltable.h | 62 + Lua/ltablib.c | 428 +++++++ Lua/ltm.c | 270 +++++ Lua/ltm.h | 103 ++ Lua/lua.h | 517 +++++++++ Lua/lua.hpp | 9 + Lua/luac.c | 724 ++++++++++++ Lua/luaconf.h | 776 +++++++++++++ Lua/lualib.h | 58 + Lua/lundump.c | 333 ++++++ Lua/lundump.h | 36 + Lua/lutf8lib.c | 289 +++++ Lua/lvm.c | 1810 +++++++++++++++++++++++++++++ Lua/lvm.h | 134 +++ Lua/lzio.c | 68 ++ Lua/lzio.h | 66 ++ QQAPI/DDAPI.cpp | 161 +++ QQAPI/DDAPI.h | 59 + QQAPI/QQEvent.cpp | 10 + QQAPI/QQEvent.h | 35 + 130 files changed, 30753 insertions(+), 4573 deletions(-) delete mode 100644 CQSDK/CQAPI.h delete mode 100644 CQSDK/CQAPI_EX.h delete mode 100644 CQSDK/CQEVE.h delete mode 100644 CQSDK/CQEVEBasic.h delete mode 100644 CQSDK/CQEVEMsg.h delete mode 100644 CQSDK/CQEVERequest.h delete mode 100644 CQSDK/CQEVE_ALL.h delete mode 100644 CQSDK/CQEVE_DiscussMsg.h delete mode 100644 CQSDK/CQEVE_FriendAdd.h delete mode 100644 CQSDK/CQEVE_GroupMsg.h delete mode 100644 CQSDK/CQEVE_PrivateMsg.h delete mode 100644 CQSDK/CQEVE_RequestAddFriend.h delete mode 100644 CQSDK/CQEVE_RequestAddGroup.h delete mode 100644 CQSDK/CQEVE_Status.h delete mode 100644 CQSDK/CQLogger.h delete mode 100644 CQSDK/CQMsgCode.h delete mode 100644 CQSDK/CQMsgSend.h delete mode 100644 CQSDK/CQconstant.h delete mode 100644 CQSDK/cqdefine.h delete mode 100644 CQSDKCPP/CQAPI.cpp delete mode 100644 CQSDKCPP/CQAPI_EX.cpp delete mode 100644 CQSDKCPP/CQEVE.cpp delete mode 100644 CQSDKCPP/CQstream.cpp delete mode 100644 Dice/CQP.lib create mode 100644 Dice/DiceLua.cpp create mode 100644 Dice/DiceLua.h create mode 100644 Lua/lapi.c create mode 100644 Lua/lapi.h create mode 100644 Lua/lauxlib.c create mode 100644 Lua/lauxlib.h create mode 100644 Lua/lbaselib.c create mode 100644 Lua/lcode.c create mode 100644 Lua/lcode.h create mode 100644 Lua/lcorolib.c create mode 100644 Lua/lctype.c create mode 100644 Lua/lctype.h create mode 100644 Lua/ldblib.c create mode 100644 Lua/ldebug.c create mode 100644 Lua/ldebug.h create mode 100644 Lua/ldo.c create mode 100644 Lua/ldo.h create mode 100644 Lua/ldump.c create mode 100644 Lua/lfunc.c create mode 100644 Lua/lfunc.h create mode 100644 Lua/lgc.c create mode 100644 Lua/lgc.h create mode 100644 Lua/linit.c create mode 100644 Lua/liolib.c create mode 100644 Lua/ljumptab.h create mode 100644 Lua/llex.c create mode 100644 Lua/llex.h create mode 100644 Lua/llimits.h create mode 100644 Lua/lmathlib.c create mode 100644 Lua/lmem.c create mode 100644 Lua/lmem.h create mode 100644 Lua/loadlib.c create mode 100644 Lua/lobject.c create mode 100644 Lua/lobject.h create mode 100644 Lua/lopcodes.c create mode 100644 Lua/lopcodes.h create mode 100644 Lua/lopnames.h create mode 100644 Lua/loslib.c create mode 100644 Lua/lparser.c create mode 100644 Lua/lparser.h create mode 100644 Lua/lprefix.h create mode 100644 Lua/lstate.c create mode 100644 Lua/lstate.h create mode 100644 Lua/lstring.c create mode 100644 Lua/lstring.h create mode 100644 Lua/lstrlib.c create mode 100644 Lua/ltable.c create mode 100644 Lua/ltable.h create mode 100644 Lua/ltablib.c create mode 100644 Lua/ltm.c create mode 100644 Lua/ltm.h create mode 100644 Lua/lua.h create mode 100644 Lua/lua.hpp create mode 100644 Lua/luac.c create mode 100644 Lua/luaconf.h create mode 100644 Lua/lualib.h create mode 100644 Lua/lundump.c create mode 100644 Lua/lundump.h create mode 100644 Lua/lutf8lib.c create mode 100644 Lua/lvm.c create mode 100644 Lua/lvm.h create mode 100644 Lua/lzio.c create mode 100644 Lua/lzio.h create mode 100644 QQAPI/DDAPI.cpp create mode 100644 QQAPI/DDAPI.h create mode 100644 QQAPI/QQEvent.cpp create mode 100644 QQAPI/QQEvent.h diff --git a/CQSDK/CQAPI.h b/CQSDK/CQAPI.h deleted file mode 100644 index dbf2febe..00000000 --- a/CQSDK/CQAPI.h +++ /dev/null @@ -1,266 +0,0 @@ -/* -CoolQ SDK for VS2017 -Api Version 9.13 -Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs -*/ -#pragma once -#include "cqdefine.h" -#ifdef _MSC_VER -#define CQAPI(NAME,ReturnType) extern "C" __declspec(dllimport) ReturnType __stdcall NAME // NOLINT -#else -#define CQAPI(NAME,ReturnType) extern "C" __attribute__((dllimport)) ReturnType __attribute__((__stdcall__)) NAME // NOLINT -#endif /*_MSC_VER*/ - -namespace CQ -{ - // ȡapiAuthCode - int getAuthCode() noexcept; - - //ͺϢ - //Auth=106 ʧܷظֵ,ɹϢID - CQAPI(CQ_sendPrivateMsg, int)( - int AuthCode, // - long long QQID, // ĿQQ - const char* msg // Ϣ - ) noexcept; - - //ȺϢ - //Auth=101 ʧܷظֵ,ɹϢID - CQAPI(CQ_sendGroupMsg, int)( - int AuthCode, // - long long GroupID, // ĿȺ - const char* msg // Ϣ - ) noexcept; - - //Ϣ - //Auth=103 ʧܷظֵ,ɹϢID - CQAPI(CQ_sendDiscussMsg, int)( - int AuthCode, // - long long DiscussID, // Ŀ - const char* msg // Ϣ - ) noexcept; - - // Auth=110 - CQAPI(CQ_sendLike, int)( - int AuthCode, // - long long QQID // ĿQQ - ) noexcept; - - //V2 Auth=110 - CQAPI(CQ_sendLikeV2, int)( - int AuthCode, // - long long QQID, // ĿQQ - int times // ޵Ĵ10 - ) noexcept; - - //ȡCookies (ã˽ӿҪϸȨ) - //Auth=20 ,˽ӿҪϸȨ - CQAPI(CQ_getCookiesV2, const char *)( - int AuthCode, // - const char* Domain - ) noexcept; - - // - CQAPI(CQ_getRecordV2, const char *)( - int AuthCode, // - const char* file, // յϢеļ (file) - const char* outformat // Ӧĸʽ mp3,amr,wma,m4a,spx,ogg,wav,flac - ) noexcept; - - //ȡCsrfToken (ã˽ӿҪϸȨ) - //Auth=20 QQҳõbkn/g_tk ,˽ӿҪϸȨ - CQAPI(CQ_getCsrfToken, int)( - int AuthCode // - ) noexcept; - - //ȡӦĿ¼ - //ص·ĩβ"\" - CQAPI(CQ_getAppDirectory, const char *)( - int AuthCode // - ) noexcept; - - //ȡ¼QQ - CQAPI(CQ_getLoginQQ, long long)( - int AuthCode // - ) noexcept; - - //ȡ¼dz - CQAPI(CQ_getLoginNick, const char *)( - int AuthCode // - ) noexcept; - - //ȺԱƳ Auth=120 - CQAPI(CQ_setGroupKick, int)( - int AuthCode, // - long long GroupID, // ĿȺ - long long QQID, // ĿQQ - CQBOOL RefuseForever // Ϊ棬򡰲ٽմ˼Ⱥ롱 - ) noexcept; - - //ȺԱ Auth=121 - CQAPI(CQ_setGroupBan, int)( - int AuthCode, // - long long GroupID, // ĿȺ - long long QQID, // ĿQQ - long long Time // Եʱ䣬λΪ롣Ҫд0 - ) noexcept; - - //ȺԱ Auth=122 - CQAPI(CQ_setGroupAdmin, int)( - int AuthCode, // - long long GroupID, // ĿȺ - long long QQID, // õQQ - CQBOOL setAdmin // /ùԱ /ȡԱ - ) noexcept; - - //ȺԱרͷ Auth=128 ȺȨ - CQAPI(CQ_setGroupSpecialTitle, int)( - int AuthCode, // - long long GroupID, // ĿȺ - long long QQID, // ĿQQ - const char* Title, // Ҫɾ - long long ExpireTime // רͷЧڣλΪ롣Чд-1 - ) noexcept; - - //ȫȺ Auth=123 - CQAPI(CQ_setGroupWholeBan, int)( - int AuthCode, // - long long GroupID, // ĿȺ - CQBOOL EnableWholeBan // / /ر - ) noexcept; - - //ȺԱ Auth=124 - CQAPI(CQ_setGroupAnonymousBan, int)( - int AuthCode, // - long long GroupID, // ĿȺ - const char* AnonymousID, // ȺϢ¼յġ - long long time // Եʱ䣬λΪ롣ֽ֧ - ) noexcept; - - //Ⱥ Auth=125 - CQAPI(CQ_setGroupAnonymous, int)( - int AuthCode, // - long long GroupID, // - CQBOOL EnableAnonymous // - ) noexcept; - - //ȺԱƬ Auth=126 - CQAPI(CQ_setGroupCard, int)( - int AuthCode, // - long long GroupID, // ĿȺ - long long QQID, // õQQ - const char* NewGroupCarkNick // - ) noexcept; - - //Ⱥ˳ Auth=127 ,˽ӿҪϸȨ - CQAPI(CQ_setGroupLeave, int)( - int AuthCode, // - long long GroupID, // ĿȺ - CQBOOL isDismiss // /ɢȺ (Ⱥ) /˳Ⱥ (ȺԱ) - ) noexcept; - - //˳ Auth=140 - CQAPI(CQ_setDiscussLeave, int)( - int AuthCode, // - long long DiscussID // Ŀ - ) noexcept; - - //ú Auth=150 - CQAPI(CQ_setFriendAddRequest, int)( - int AuthCode, // - const char* ResponseToken, // ¼յġʶ - int ResponseType, // #_ͨ #_ܾ - const char* Remarks // Ӻĺѱע - ) noexcept; - - //Ⱥ Auth=151 - CQAPI(CQ_setGroupAddRequest, int)( - int AuthCode, // - const char* ResponseToken, // ¼յġʶ - int RequestType, // ¼ #_Ⱥ #_Ⱥ - int ResponseType // #_ͨ #_ܾ - ) noexcept; - - //Ⱥ Auth=151 - CQAPI(CQ_setGroupAddRequestV2, int)( - int AuthCode, // - const char* RequestToken, // ¼յġʶ - int RequestType, // ¼ #_Ⱥ #_Ⱥ - int ResponseType, // #_ͨ #_ܾ - const char* Reason // ɣ #_Ⱥ #_ܾ ʱ - ) noexcept; - - //־ - CQAPI(CQ_addLog, int)( - int AuthCode, // - int Priorty, // #Log_ ͷ - const char* Type, // - const char* Content // - ) noexcept; - - //ʾ - CQAPI(CQ_setFatal, int)( - int AuthCode, // - const char* ErrorMsg // - ) noexcept; - - //ȡȺԱϢ (ɰ,CQ_getGroupMemberInfoV2) Auth=130 - CQAPI(CQ_getGroupMemberInfo, const char *)( - int AuthCode, // - long long GroupID, // ĿQQȺ - long long QQID // ĿQQ - ) noexcept; - //ȡȺԱϢ (ֻ֧) Auth=130 - CQAPI(CQ_getGroupMemberInfoV2, const char *)( - int AuthCode, // - long long GroupID, // ĿQQȺ - long long QQID, // ĿQQ - CQBOOL DisableCache - ) noexcept; - //ȡİϢ (ֻ֧) Auth=131 - CQAPI(CQ_getStrangerInfo, const char *)( - int AuthCode, // - long long QQID, // ĿQQ - CQBOOL DisableCache - ) noexcept; - //ȡȺϢ (ֻ֧) Auth=132 - CQAPI(CQ_getGroupInfo, const char*)( - int AuthCode, // - long long GroupID, // ĿȺ - CQBOOL DisableCache - ) noexcept; - - //ȡȺԱб Auth=160 - CQAPI(CQ_getGroupMemberList, const char *)( - int AuthCode, // - long long GroupID // ĿQQȺ - ) noexcept; - //ȡȺб Auth=161 - CQAPI(CQ_getGroupList, const char *)( - int AuthCode - ) noexcept; - //ȡб Auth=162 - CQAPI(CQ_getFriendList, const char*)( - int AuthCode, - CQBOOL Reserved = false // Ϊfalse - ) noexcept; - //Ϣ Auth=180 - CQAPI(CQ_deleteMsg, int)( - int AuthCode, - long long MsgId - ) noexcept; - - //Ƿַ֧ͼƬش 0 Ϊ֧֣ 0 Ϊ֧ - CQAPI(CQ_canSendImage, int)( - int AuthCode - ) noexcept; - //Ƿַ֧ش 0 Ϊ֧֣ 0 Ϊ֧ - CQAPI(CQ_canSendRecord, int)( - int AuthCode - ) noexcept; - //ͼƬͼƬļ· - CQAPI(CQ_getImage, const char*)( - int AuthCode, - const char* file//յϢеͼƬļ(file) - ) noexcept; -} diff --git a/CQSDK/CQAPI_EX.h b/CQSDK/CQAPI_EX.h deleted file mode 100644 index 4e677ae4..00000000 --- a/CQSDK/CQAPI_EX.h +++ /dev/null @@ -1,259 +0,0 @@ -#pragma once - -#include "cqdefine.h" - -#include -#include -#include - -class Unpack; - -namespace CQ -{ - //־ - int addLog(int Priorty, const char* Type, const char* Content) noexcept; - - //ͺϢ - //Auth=106 ʧܷظֵ,ɹϢID - int sendPrivateMsg(long long QQ, const char* msg) noexcept; - //ͺϢ - //Auth=106 ʧܷظֵ,ɹϢID - int sendPrivateMsg(long long QQ, const std::string& msg) noexcept; - - //ȺϢ - //Auth=101 ʧܷظֵ,ɹϢID - int sendGroupMsg(long long GroupID, const char* msg) noexcept; - //ȺϢ - //Auth=101 ʧܷظֵ,ɹϢID - int sendGroupMsg(long long GroupID, const std::string& msg) noexcept; - - - //Ϣ - //Auth=103 ʧܷظֵ,ɹϢID - int sendDiscussMsg(long long DiscussID, const char* msg) noexcept; - //Ϣ - //Auth=103 ʧܷظֵ,ɹϢID - int sendDiscussMsg(long long DiscussID, const std::string& msg) noexcept; - - // Auth=110 - int sendLike(long long QQID, int times) noexcept; - - //ȡCookies (ã˽ӿҪϸȨ) - //Auth=20 - const char* getCookies(const char* Domain) noexcept; - - //ȡCookies (ã˽ӿҪϸȨ) - //Auth=20 - const char* getCookies(const std::string& Domain) noexcept; - - // - const char* getRecord( - const char* file, // յϢеļ (file) - const char* outformat // Ӧĸʽ mp3,amr,wma,m4a,spx,ogg,wav,flac - ) noexcept; - // - std::string getRecord( - const std::string& file, // յϢеļ (file) - const std::string& outformat // Ӧĸʽ mp3,amr,wma,m4a,spx,ogg,wav,flac - ) noexcept; - - //ȡCsrfToken (ã˽ӿҪϸȨ) - //Auth=20 QQҳõbkn/g_tk - int getCsrfToken() noexcept; - - //ȡӦĿ¼ - //ص·ĩβ"\" - const char* getAppDirectory() noexcept; - - //ȡ¼QQ - long long getLoginQQ() noexcept; - - //ȡ¼dz - const char* getLoginNick() noexcept; - - //ȺԱƳ Auth=120 - int setGroupKick( - long long GroupID, long long QQID, - CQBOOL RefuseForever = false // Ϊ棬򡰲ٽմ˼Ⱥ롱 - ) noexcept; - - //ȺԱ Auth=121 - int setGroupBan( - long long GroupID, long long QQID, - long long Time = 60 // Եʱ䣬λΪ롣Ҫд0 - ) noexcept; - - //ȺԱ Auth=122 - int setGroupAdmin( - long long GroupID, long long QQID, - CQBOOL isAdmin = true // /ùԱ /ȡԱ - ) noexcept; - - //ȺԱרͷ Auth=128 ȺȨ - int setGroupSpecialTitle( - long long GroupID, long long QQID, - const char* Title, // Ҫɾ - long long ExpireTime = -1 // רͷЧڣλΪ롣Чд-1 - ) noexcept; - //ȺԱרͷ Auth=128 ȺȨ - int setGroupSpecialTitle( - long long GroupID, long long QQID, - const std::string& Title, // Ҫɾ - long long ExpireTime = -1 // רͷЧڣλΪ롣Чд-1 - ) noexcept; - - //ȫȺ Auth=123 - int setGroupWholeBan( - long long GroupID, - CQBOOL isBan = true // / /ر - ) noexcept; - - //ȺԱ Auth=124 - int setGroupAnonymousBan( - long long GroupID, - const char* AnonymousToken, // ȺϢ¼յġ - long long banTime = 60 // Եʱ䣬λΪ롣ֽ֧ - ) noexcept; - - //Ⱥ Auth=125 - int setGroupAnonymous(long long GroupID, CQBOOL enableAnonymous = true) noexcept; - - //ȺԱƬ Auth=126 - int setGroupCard(long long GroupID, long long QQID, const char* newGroupNick) noexcept; - - //ȺԱƬ Auth=126 - int setGroupCard(long long GroupID, long long QQID, const std::string& newGroupNick) noexcept; - - //Ⱥ˳ Auth=127 ,˽ӿҪϸȨ - int setGroupLeave( - long long GroupID, - CQBOOL isDismiss = false // /ɢȺ (Ⱥ) /˳Ⱥ (ȺԱ) - ) noexcept; - - //˳ Auth=140 - int setDiscussLeave( - long long DiscussID - ) noexcept; - - //ú Auth=150 - int setFriendAddRequest( - const char* RequestToken, // ¼յġʶ - int ReturnType, // #_ͨ #_ܾ - const char* Remarks // Ӻĺѱע - ) noexcept; - - //Ⱥ Auth=151 - int setGroupAddRequest( - const char* RequestToken, // ¼յġʶ - int RequestType, // ¼ #_Ⱥ #_Ⱥ - int ReturnType, // #_ͨ #_ܾ - const char* Reason // ɣ #_Ⱥ #_ܾ ʱ - ) noexcept; - - //ʾ,ʱ֪ʲôõ - int setFatal(const char* ErrorMsg) noexcept; - - - class GroupMemberInfo; - //ȡȺԱϢ (ֻ֧) Auth=130 - GroupMemberInfo getGroupMemberInfo(long long GroupID, long long QQID, CQBOOL DisableCache = false) noexcept; - - class StrangerInfo; - //ȡİϢ (ֻ֧) Auth=131 - StrangerInfo getStrangerInfo(long long QQID, CQBOOL DisableCache = false) noexcept; - - //ȡȺԱб Auth=160 - std::vector getGroupMemberList(long long GroupID); - - //ȡȺб Auth=161 - std::map getGroupList(bool disableCache = false); - - class FriendInfo; - //ȡб Auth=162 - std::map getFriendList(bool disableCache = false); - - //Ƿַ֧ͼƬtrueΪ֧֣falseΪ֧ - bool canSendImage() noexcept; - - //Ƿַ֧ش 0 Ϊ֧֣ 0 Ϊ֧ - bool canSendRecord() noexcept; - - //ͼƬͼƬļ· - const char* getImage(const char* file) noexcept; - - //ͼƬͼƬļ· - const char* getImage(const std::string& file) noexcept; - - //Ϣ Auth=180 - int deleteMsg(long long MsgId) noexcept; - - const char* getlasterrmsg() noexcept; - // ȺϢ - class GroupInfo final { - void setdata(Unpack& u); - public: - long long llGroup{}; - std::string strGroupName{}; - int nGroupSize = 0; // Ⱥ - int nMaxGroupSize = 0;//Ⱥģ - int nFriendCnt = 0;// - - explicit GroupInfo(long long group); - GroupInfo() = default; - [[nodiscard]] std::string tostring() const; - }; - // ȺԱϢ - class GroupMemberInfo final - { - void setdata(Unpack& u); - public: - long long Group{}; - long long QQID{}; - std::string Nick{}; - std::string GroupNick{}; - int Gender{}; // 0/ 1/Ů - int Age{}; - std::string Region{}; - int AddGroupTime{}; - int LastMsgTime{}; - std::string LevelName{}; - int permissions{}; //1/Ա 2/Ա 3/Ⱥ - std::string Title{}; - int ExpireTime{}; // -1 - CQBOOL NaughtyRecord{}; - CQBOOL canEditGroupNick{}; - - explicit GroupMemberInfo(Unpack& msg); - explicit GroupMemberInfo(const char* msg); //API - explicit GroupMemberInfo(const std::vector& data); //Unpack - GroupMemberInfo() = default; - - [[nodiscard]] std::string tostring() const; - }; - // Ϣ - class FriendInfo final - { - public: - long long QQID = 0; - std::string nick; //dz - std::string remark; //ע - - explicit FriendInfo(Unpack p); - FriendInfo() = default; - [[nodiscard]] std::string tostring() const; - }; - // İϢ - class StrangerInfo final - { - public: - long long QQID = 0; - std::string nick = ""; //dz - int sex = 255; //0/ 1/Ů 255/δ֪ - int age = -1; // - - explicit StrangerInfo(const char* msg); - StrangerInfo() = default; - - [[nodiscard]] std::string tostring() const; - }; -} diff --git a/CQSDK/CQEVE.h b/CQSDK/CQEVE.h deleted file mode 100644 index 94e06eb7..00000000 --- a/CQSDK/CQEVE.h +++ /dev/null @@ -1,289 +0,0 @@ -/* -CoolQ SDK for VS2017 -Api Version 9.10 -Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs -*/ -#pragma once -#ifdef _MSC_VER -#define CQEVENT(ReturnType, Name, Size) __pragma(comment(linker, "/EXPORT:" #Name "=_" #Name "@" #Size))\ - extern "C" __declspec(dllexport) ReturnType __stdcall Name -#else -#define CQEVENT(ReturnType, Name, Size)\ - extern "C" __attribute__((dllexport)) ReturnType __attribute__((__stdcall__)) Name -#endif /*_MSC_VER*/ -/* -ӦõApiVerAppid󽫲 -*/ -#define MUST_AppInfo CQEVENT(const char*, AppInfo, 0)() - -/* -ӦõApiVerAppid󽫲 -*/ -#define MUST_AppInfo_RETURN(CQAPPID) MUST_AppInfo{return CQAPIVERTEXT "," CQAPPID;} - -/* -ʧЧ,ʹ getAuthCode(); ֱӻȡ, ˺CQAPI.h - -ӦAuthCode - -뱣 AuthCode ,ֵǵCQAPIƾ֤ - -벻Ҫڱ -*/ -//#define MUST_Initialize CQEVENT(int, Initialize, 4)(int AuthCode) - - -///////////////////////////////// ¼ ///////////////////////////////// -//extern "C" __declspec(dllexport) void __stdcall Int32(int a){}//@4 -//extern "C" __declspec(dllexport) void __stdcall Char(const char* a){}//@4 -//extern "C" __declspec(dllexport) void __stdcall Int64(long long a){}//@8 - -/* -Q(Type=1001) - -ӳڿQ̡߳бá -۱ӦǷãڿQִһΣִвʼ롣 -ؾ췵رӳ򣬷ῨסԼļء -̶ 0 - -ʹ»߿ͷҪij˫» -*/ -#define EVE_Startup(Name) CQEVENT(int, Name, 0)() - -/* -Q˳(Type=1002) - -ӳڿQ̡߳бá -۱ӦǷãڿQ˳ǰִһΣִвرմ롣 -Ϻ󣬿Qܿرգ벻Ҫ̵ͨ߳ȷʽִ - -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Exit(Name) CQEVENT(int, Name, 0)() - -/* -Ӧѱ(Type=1003) - -Ӧñú󣬽յ¼ -QʱӦѱã EVE_Startup(Type=1001,Q) ú󣬱ҲһΡ -DZҪشڡӲ˵ûֶ򿪴ڣ - -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Enable(Name) CQEVENT(int, Name, 0)() - -/* -Ӧýͣ(Type=1004) - -Ӧñͣǰյ¼ -QʱӦѱͣã򱾺᡿á -۱ӦǷãQرǰ᡿á - -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Disable(Name) CQEVENT(int, Name, 0)() - -/* -˽Ϣ(Type=21) - -˺² -subType ͣ11/Ժ 1/״̬ 2/Ⱥ 3/ -msgId ϢID -fromQQ ԴQQ -msg Ϣ -font - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_PrivateMsg(Name) CQEVENT(int, Name, 24)(int subType, int msgId, long long fromQQ, const char* msg, int font) - -/* -ȺϢ(Type=2) - -subType ͣĿǰ̶Ϊ1 -msgId ϢID -fromGroup ԴȺ -fromQQ ԴQQ -fromAnonymous Դ -msg Ϣ -font - -Ϣ,fromQQ ̶Ϊ 80000000,ʹù߽ fromAnonymous תΪ Ϣ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_GroupMsg(Name) CQEVENT(int, Name, 36)(int subType, int msgId, long long fromGroup, long long fromQQ, const char* fromAnonymous, const char* msg, int font) - -/* -Ϣ(Type=4) - -subtype ͣĿǰ̶Ϊ1 -msgId ϢID -fromDiscuss Դ -fromQQ ԴQQ -msg Ϣ -font - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_DiscussMsg(Name) CQEVENT(int, Name, 32)(int subType, int msgId, long long fromDiscuss, long long fromQQ, const char* msg, int font) - -/* -Ⱥļϴ¼(Type=11) - -subType ͣĿǰ̶Ϊ1 -sendTime ʱ(ʱ) -fromGroup ԴȺ -fromQQ ԴQQ -file ϴļϢ,ʹ <_ת_ıȺļ> תΪЧ,༭ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_GroupUpload(Name) CQEVENT(int, Name, 28)(int subType, int sendTime, long long fromGroup,long long fromQQ, const char* file) -/*ǿİ汾, -#define EVE_GroupUpload_EX(Name) CQEVENT(int, Name, 28)(int subType, int sendTime, long long fromGroup,long long fromQQ, const char* file)\ -{\ -int Name(int subType, int sendTime, long long fromGroup,long long fromQQ, File file);\ -return Name(subType,sendTime,fromGroup,fromQQ,ToFile(file));\ -}\ -int Name(int subType, int sendTime, long long fromGroup,long long fromQQ, File file) -*/ - -/* -Ⱥ¼-Ա䶯(Type=101) - -subtype ͣ1/ȡԱ 2/ùԱ -sendTime ʱ(ʱ) -fromGroup ԴȺ -beingOperateQQ QQ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_System_GroupAdmin(Name) CQEVENT(int, Name, 24)(int subType, int msgId, long long fromGroup, long long beingOperateQQ) - -/* -Ⱥ¼-ȺԱ(Type=102) - -subtype ͣ1/ȺԱ뿪 2/ȺԱ 3/Լ(¼) -sendTime ʱ(ʱ) -fromGroup ԴȺ -fromQQ QQ(Ϊ23ʱ) -beingOperateQQ QQ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_System_GroupMemberDecrease(Name) CQEVENT(int, Name, 32)(int subType, int msgId, long long fromGroup, long long fromQQ, long long beingOperateQQ) - -/* -Ⱥ¼-ȺԱ(Type=103) - -subtype ͣ1/Աͬ 2/Ա -sendTime ʱ(ʱ) -fromGroup ԴȺ -fromQQ QQ(ԱQQ) -beingOperateQQ QQ(ȺQQ) - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_System_GroupMemberIncrease(Name) CQEVENT(int, Name, 32)(int subType, int msgId, long long fromGroup, long long fromQQ, long long beingOperateQQ) - -/* -Ⱥ¼-Ⱥ(Type=104) - -subtype ͣ1/ 2/ -msgId Ϣid -fromGroup ԴȺ -fromQQ QQ(ԱQQ) -beingOperateQQ QQ(ΪȫȺΪ0) -duration ʱλ룬Ϊ2ʱã -*/ -#define EVE_System_GroupBan(Name) CQEVENT(int, Name, 40)(int subType, int msgId, long long fromGroup, long long fromQQ, long long beingOperateQQ,long long duration) - -/* -¼-(Type=201) - -subtype ͣĿǰ̶Ϊ1 -sendTime ʱ(ʱ) -fromQQ ԴQQ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Friend_Add(Name) CQEVENT(int, Name, 16)(int subType, int msgId, long long fromQQ) - -/* --(Type=301) - -subtype ͣĿǰ̶Ϊ1 -sendTime ʱ(ʱ) -fromQQ ԴQQ -msg -responseFlag ʶ() - -ú (responseFlag, #_ͨ) - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Request_AddFriend(Name) CQEVENT(int, Name, 24)(int subType, int msgId, long long fromQQ, const char* msg, const char* responseFlag) - -/* --Ⱥ(Type=302) - -subtype ͣ1/Ⱥ 2/Լ(¼)Ⱥ -sendTime ʱ(ʱ) -fromGroup ԴȺ -fromQQ ԴQQ -msg -responseFlag ʶ() - - subtype 1 -Ⱥ (responseFlag, #_Ⱥ, #_ͨ) - subtype 2 -Ⱥ (responseFlag, #_Ⱥ, #_ͨ) - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Request_AddGroup(Name) CQEVENT(int, Name, 32)(int subType, int sendTime, long long fromGroup, long long fromQQ, const char* msg, const char* responseFlag) - -/* -˵ - - .json ļò˵Ŀ -ʹò˵ .json ˴ɾò˵ -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Menu(Name) CQEVENT(int, Name, 0)() - -/* - - -ʹEX汾 -emmm,ΪһЩԭ,ʱ... - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -*/ -#define EVE_Status(Name) CQEVENT(const char*, Name, 0)() diff --git a/CQSDK/CQEVEBasic.h b/CQSDK/CQEVEBasic.h deleted file mode 100644 index f8f90404..00000000 --- a/CQSDK/CQEVEBasic.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "CQconstant.h" - -namespace CQ -{ - // ¼ - struct EVE - { - //Ϣκζ - //֮ǰϢ,ォ·бϢ - void message_ignore() noexcept; - //رϢ - void message_block() noexcept; - - int _EVEret = Msg_Ignored; - - virtual ~EVE() - { - } - }; - - - inline void EVE::message_ignore() noexcept { _EVEret = Msg_Ignored; } - inline void EVE::message_block() noexcept { _EVEret = Msg_Blocked; } - -} diff --git a/CQSDK/CQEVEMsg.h b/CQSDK/CQEVEMsg.h deleted file mode 100644 index ae377f0f..00000000 --- a/CQSDK/CQEVEMsg.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include "CQEVEBasic.h" - -#include -#include - -namespace CQ -{ - - //Ϣ - class regexMsg final - { - //Ϣ - std::map regexMap{}; - public: - regexMsg(const std::string& msg); - std::string get(const std::string&); - std::string operator [](const std::string&); - }; - - class msg; - - //Ϣ¼ - struct EVEMsg : EVE - { - // - int subType; - //ϢID - int msgId; - //ԴQQ - long long fromQQ; - //Ϣ - std::string message; - // - int font; - - EVEMsg(int subType, int msgId, long long fromQQ, std::string message, int font) noexcept; - - //ʵû - bool isUser() const noexcept; - //Ƿϵͳû - bool isSystem() const noexcept; - - virtual int sendMsg(const char*) const noexcept = 0; - virtual int sendMsg(const std::string&) const noexcept = 0; - virtual msg sendMsg() const noexcept = 0; - }; - - inline bool EVEMsg::isSystem() const noexcept { return fromQQ == 1000000; } -} diff --git a/CQSDK/CQEVERequest.h b/CQSDK/CQEVERequest.h deleted file mode 100644 index 2cb53b4d..00000000 --- a/CQSDK/CQEVERequest.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include -#include "CQEVEBasic.h" - -namespace CQ -{ - struct EVERequest : EVE - { - int sendTime; // ʱ(ʱ) - long long fromQQ; // ԴQQ - const char* msg; // - const char* responseFlag; // ʶ() - - EVERequest(int sendTime, long long fromQQ, const char* msg, const char* responseFlag) noexcept; - virtual void pass(const std::string& msg) const noexcept = 0; //ͨ - virtual void fail(const std::string& msg) const noexcept = 0; //ܾ - }; -} diff --git a/CQSDK/CQEVE_ALL.h b/CQSDK/CQEVE_ALL.h deleted file mode 100644 index 7ff894dd..00000000 --- a/CQSDK/CQEVE_ALL.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "CQEVE.h" -#include "CQEVEBasic.h" - -#include "CQEVEMsg.h" -#include "CQEVERequest.h" -#include "CQAPI_EX.h" -#include "CQEVE_Status.h" -#include "CQEVE_GroupMsg.h" -#include "CQEVE_PrivateMsg.h" -#include "CQEVE_DiscussMsg.h" -#include "CQEVE_RequestAddFriend.h" -#include "CQEVE_RequestAddGroup.h" -#include "CQEVE_FriendAdd.h" diff --git a/CQSDK/CQEVE_DiscussMsg.h b/CQSDK/CQEVE_DiscussMsg.h deleted file mode 100644 index 933f840f..00000000 --- a/CQSDK/CQEVE_DiscussMsg.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "CQMsgSend.h" -#include "CQEVEMsg.h" -/* -Ϣ(Type=4) - -subtype ͣĿǰ̶Ϊ1 -msgId ϢID -fromDiscuss Դ -fromQQ ԴQQ -msg Ϣ -font - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_DiscussMsg_EX(Name) \ - void Name(CQ::EVEDiscussMsg & eve); \ - EVE_DiscussMsg(Name) \ - { \ - CQ::EVEDiscussMsg tep(subType, msgId, fromDiscuss, fromQQ, msg, font); \ - Name(tep);\ - return tep._EVEret; \ - } \ - void Name(CQ::EVEDiscussMsg & eve) - - -namespace CQ -{ - struct EVEDiscussMsg : EVEMsg - { - long long fromDiscuss; // - - EVEDiscussMsg(int subType, int msgId, long long fromDiscuss, long long fromQQ, const char* msg, int font) noexcept; - - bool leave() const noexcept; //˳ - - // ͨ EVEMsg ̳ - msg sendMsg() const noexcept override; - int sendMsg(const char*) const noexcept override; - int sendMsg(const std::string&) const noexcept override; - }; -} diff --git a/CQSDK/CQEVE_FriendAdd.h b/CQSDK/CQEVE_FriendAdd.h deleted file mode 100644 index 7d655e7e..00000000 --- a/CQSDK/CQEVE_FriendAdd.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "CQEVEBasic.h" - -/* -¼-(Type=201) - -subtype ͣĿǰ̶Ϊ1 -msgId ϢID -fromQQ ԴQQ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Friend_Add_EX(Name) \ - void Name(CQ::EVERequestAddFriend & eve);\ - EVE_Friend_Add(Name)\ - {\ - CQ::EVEFriendAdd tep(subType, msgId, fromGroup, fromQQ, msg, responseFlag);\ - Name(tep);\ - return tep._EVEret;\ - }\ - void Name(CQ::EVEFriendAdd & eve) - -namespace CQ -{ - struct EVEFriendAdd final : EVE - { - }; -} diff --git a/CQSDK/CQEVE_GroupMsg.h b/CQSDK/CQEVE_GroupMsg.h deleted file mode 100644 index f3341697..00000000 --- a/CQSDK/CQEVE_GroupMsg.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include "CQEVEMsg.h" - -#include -/* -ȺϢ(Type=2) - -subType ͣĿǰ̶Ϊ1 -msgId ϢID -fromGroup ԴȺ -fromQQ ԴQQ -fromAnonymous Դ -msg Ϣ -font - -Ϣ,isAnonymous() true, ʹ getFromAnonymousInfo() ȡ Ϣ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_GroupMsg_EX(Name) \ - void Name(CQ::EVEGroupMsg & eve); \ - EVE_GroupMsg(Name) \ - { \ - CQ::EVEGroupMsg tep(subType, msgId, fromGroup, fromQQ, fromAnonymous, msg, font); \ - Name(tep); \ - return tep._EVEret; \ - } \ - void Name(CQ::EVEGroupMsg & eve) - -namespace CQ -{ - class GroupMemberInfo; - - // ȺϢ - struct AnonymousInfo final - { - long long AID = 0; - std::string AnonymousNick = ""; - - explicit AnonymousInfo(const char* msg) noexcept; - AnonymousInfo() noexcept = default; - }; - - //Ⱥ¼ - struct EVEGroupMsg final : public EVEMsg - { - private: - AnonymousInfo* fromAnonymousInfo; - public: - //Ⱥ - long long fromGroup; - //õ - const char* fromAnonymousToken; - EVEGroupMsg(int subType, int msgId, long long fromGroup, long long fromQQ, const char* fromAnonymous, - const char* msg, int font) noexcept; - - virtual ~EVEGroupMsg() noexcept; - - bool isAnonymous() const noexcept; - - // ͨ EVEMsg ̳ - int sendMsg(const char*) const noexcept override; - int sendMsg(const std::string&) const noexcept override; - msg sendMsg() const noexcept override; - - //ȡϢ - AnonymousInfo& getFromAnonymousInfo() noexcept(false); - - //ȺԱƳ - bool setGroupKick(bool refusedAddAgain = false) const noexcept; - //ȺԱ - //ԶжǷ - bool setGroupBan(long long banTime = 60) const noexcept; - //ȺԱ - bool setGroupAdmin(bool isAdmin) const noexcept; - //ȺԱרͷ - bool setGroupSpecialTitle(const std::string& Title, long long ExpireTime = -1) const noexcept; - - //ȫȺ - bool setGroupWholeBan(bool enableBan = true) const noexcept; - //Ⱥ - bool setGroupAnonymous(bool enableAnonymous) const noexcept; - //ȺԱƬ - bool setGroupCard(const std::string& newGroupNick) const noexcept; - //Ⱥ˳ - bool setGroupLeave(bool isDismiss) const noexcept; - //ȡȺԱϢ (ֻ֧) - GroupMemberInfo getGroupMemberInfo(bool disableCache = false) const noexcept; - //ȡȺԱб - std::vector getGroupMemberList() const noexcept; - }; -} diff --git a/CQSDK/CQEVE_PrivateMsg.h b/CQSDK/CQEVE_PrivateMsg.h deleted file mode 100644 index a2184b42..00000000 --- a/CQSDK/CQEVE_PrivateMsg.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include "CQMsgSend.h" -#include "CQEVEMsg.h" - -/* -˽Ϣ(Type=21) - -˺² -subType ͣ11/Ժ 1/״̬ 2/Ⱥ 3/ -msgId ϢID -fromQQ ԴQQ -msg Ϣ -font - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_PrivateMsg_EX(Name) \ - void Name(CQ::EVEPrivateMsg & eve); \ - EVE_PrivateMsg(Name) \ - { \ - CQ::EVEPrivateMsg tep(subType, msgId, fromQQ, msg, font); \ - Name(tep); \ - return tep._EVEret; \ - } \ - void Name(CQ::EVEPrivateMsg & eve) - - -namespace CQ -{ - struct EVEPrivateMsg final : EVEMsg - { - EVEPrivateMsg(int subType, int msgId, long long fromQQ, const char* msg, int font) noexcept; - - //Ժ - [[nodiscard]] bool fromPrivate() const noexcept; - - //״̬ - [[nodiscard]] bool fromOnlineStatus() const noexcept; - - //Ⱥʱ - [[nodiscard]] bool fromGroup() const noexcept; - - //ʱ - [[nodiscard]] bool fromDiscuss() const noexcept; - - // ͨ EVEMsg ̳ - [[nodiscard]] msg sendMsg() const noexcept override; - - int sendMsg(const char*) const noexcept override; - - [[nodiscard]] int sendMsg(const std::string&) const noexcept override; - }; -} diff --git a/CQSDK/CQEVE_RequestAddFriend.h b/CQSDK/CQEVE_RequestAddFriend.h deleted file mode 100644 index 66835aeb..00000000 --- a/CQSDK/CQEVE_RequestAddFriend.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include -#include "CQEVERequest.h" - -/* --(Type=301) - -subtype ͣĿǰ̶Ϊ1 -sendTime ʱ(ʱ) -fromQQ ԴQQ -msg -responseFlag ʶ() - -ú (responseFlag, #_ͨ) - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Request_AddFriend_EX(Name) \ - void Name(CQ::EVERequestAddFriend & eve);\ - EVE_Request_AddFriend(Name)\ - {\ - CQ::EVERequestAddFriend tep(subType, sendTime, fromGroup, fromQQ, msg, responseFlag);\ - Name(tep);\ - return tep._EVEret;\ - }\ - void Name(CQ::EVERequestAddFriend & eve) - -namespace CQ -{ - struct EVERequestAddFriend final : EVERequest - { - // - //1:̶Ϊ1 - int subType; - long long fromGroup; // ԴȺ - EVERequestAddFriend(int subType, int sendTime, long long fromQQ, const char* msg, const char* responseFlag) noexcept; - void pass(const std::string& msg = "") const noexcept override; //ͨ - void fail(const std::string& msg = "ڲijЩҪ󱻾ܾ!") const noexcept override; //ܾ - }; -} diff --git a/CQSDK/CQEVE_RequestAddGroup.h b/CQSDK/CQEVE_RequestAddGroup.h deleted file mode 100644 index bda60a73..00000000 --- a/CQSDK/CQEVE_RequestAddGroup.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -#include -#include "CQEVERequest.h" - -/* --Ⱥ(Type=302) - -subtype ͣ1/Ⱥ 2/Լ(¼)Ⱥ -sendTime ʱ(ʱ) -fromGroup ԴȺ -fromQQ ԴQQ -msg -responseFlag ʶ() - - subtype 1 -Ⱥ (responseFlag, #_Ⱥ, #_ͨ) - subtype 2 -Ⱥ (responseFlag, #_Ⱥ, #_ͨ) - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -طֵ,Ϣ,Ȳ -*/ -#define EVE_Request_AddGroup_EX(Name) \ - void Name(CQ::EVERequestAddGroup & eve);\ - EVE_Request_AddGroup(Name)\ - {\ - CQ::EVERequestAddGroup tep(subType, sendTime, fromGroup, fromQQ, msg, responseFlag);\ - Name(tep);\ - return tep._EVEret;\ - }\ - void Name(CQ::EVERequestAddGroup & eve) - -namespace CQ -{ - struct EVERequestAddGroup final : EVERequest - { - // - //1:Ⱥ - //2:Լ(¼)Ⱥ - int subType; - long long fromGroup; // ԴȺ - EVERequestAddGroup(int subType, int sendTime, long long fromGroup, long long fromQQ, const char* msg, - const char* responseFlag) noexcept; - void pass(const std::string& msg = "") const noexcept override; //ͨ - void fail(const std::string& msg = "ڲijЩҪ󱻾ܾ!") const noexcept override; //ܾ - }; -} diff --git a/CQSDK/CQEVE_Status.h b/CQSDK/CQEVE_Status.h deleted file mode 100644 index 4dfee8e7..00000000 --- a/CQSDK/CQEVE_Status.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -/* - - - eve.data eve.dataf eve.color ֲ - -ӳڿQ̡߳бãעʹöҪʼ(CoInitialize,CoUninitialize) -ʹ»߿ͷҪij˫» -*/ - -#define EVE_Status_EX(Name) \ - void Name(CQ::EVEStatus & eve);\ - EVE_Status(Name)\ - {\ - CQ::EVEStatus tep;\ - Name(tep);\ - static std::string ret;\ - ret = CQ::statusEVEreturn(tep);\ - return ret.c_str();\ - }\ - void Name(CQ::EVEStatus & eve) - -namespace CQ -{ - struct EVEStatus final - { - std::string - // - data, - //ݵλ - dataf; - int - // 1 : - // 2 : - // 3 : - // 4 : - // 5 : - // 6 : - color = 4; - // 1 : - void color_green() noexcept; - // 2 : - void color_orange() noexcept; - // 3 : - void color_red() noexcept; - // 4 : - void color_crimson() noexcept; - // 5 : - void color_black() noexcept; - // 6 : - void color_gray() noexcept; - }; - - std::string statusEVEreturn(EVEStatus& eve) noexcept; -} diff --git a/CQSDK/CQLogger.h b/CQSDK/CQLogger.h deleted file mode 100644 index 47176750..00000000 --- a/CQSDK/CQLogger.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include "bufstream.h" -#include - -#define DEBUGINFO "ļ:" << __FILE__ << ",:" << __LINE__ << ",:" - -namespace CQ -{ - class logstream : public CQstream - { - int flag; - std::string title; - public: - logstream(std::string title, int Log_flag) noexcept; - - // ͨ CQstream ̳ - void send() noexcept override; - }; - - class logger - { - std::string title; - public: - logger(std::string title) noexcept; - void setTitle(std::string title) noexcept; - - void Debug(const std::string& msg) const noexcept; - void Info(const std::string& msg) const noexcept; - void InfoSuccess(const std::string& msg) const noexcept; - void InfoRecv(const std::string& msg) const noexcept; - void InfoSend(const std::string& msg) const noexcept; - void Warning(const std::string& msg) const noexcept; - void Error(const std::string& msg) const noexcept; - void Fatal(const std::string& msg) const noexcept; - - void Debug(const char* msg) const noexcept; - void Info(const char* msg) const noexcept; - void InfoSuccess(const char* msg) const noexcept; - void InfoRecv(const char* msg) const noexcept; - void InfoSend(const char* msg) const noexcept; - void Warning(const char* msg) const noexcept; - void Error(const char* msg) const noexcept; - void Fatal(const char* msg) const noexcept; - - [[nodiscard]] logstream Debug() const noexcept; - [[nodiscard]] logstream Info() const noexcept; - [[nodiscard]] logstream InfoSuccess() const noexcept; - [[nodiscard]] logstream InfoRecv() const noexcept; - [[nodiscard]] logstream InfoSend() const noexcept; - [[nodiscard]] logstream Warning() const noexcept; - [[nodiscard]] logstream Error() const noexcept; - [[nodiscard]] logstream Fatal() const noexcept; - }; -} diff --git a/CQSDK/CQMsgCode.h b/CQSDK/CQMsgCode.h deleted file mode 100644 index 943e8947..00000000 --- a/CQSDK/CQMsgCode.h +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once -#include "CQface.h" - -#include -#include -#include -#include -//#include - -namespace CQ { - //cqϢ - struct OneCodeMsg { size_t key, keylen = 0, value = 0; OneCodeMsg(size_t key); }; - //һcqϢͨϢ - struct CodeMsg : std::vector { public: bool isCode; size_t key, keylen = 0; CodeMsg(bool isCode, size_t key); }; - - class CodeMsgs; - struct CodeMsgsFor { - CodeMsgs&t; - size_t pos; - CQ::CodeMsgs&operator*(); - CQ::CodeMsgsFor&operator++(); - bool operator!=(CQ::CodeMsgsFor&); - CodeMsgsFor(CodeMsgs&t, int pos); - }; - //Ϣ - class CodeMsgs { - std::vector msglist; - std::string txt; - size_t thismsg=0;//ָ - void decod();// - bool find(std::string &s,int); - bool is(std::string &s, int); - public: - CodeMsgs(std::string); - - //char* at(int); - - //λָ - CQ::CodeMsgs&operator[](size_t); - CQ::CodeMsgs&operator++(int); - CQ::CodeMsgs&operator++(); - CQ::CodeMsgs&operator--(int); - CQ::CodeMsgs&operator--(); - CQ::CodeMsgs&operator-(size_t); - CQ::CodeMsgs&operator+(size_t); - //ָ뵱ǰλ - int pos(); - - //ӵǰλÿʼָcq - //,λָ - //򷵻null,Ҳƶָ - bool find(std::string s); - - //ӵǰλÿʼָcq - //,λָ - //򷵻null,Ҳƶָ - bool lastfind(std::string); - - - //жǷCQ - bool isCQcode(); - - //жǷΪָCQ - bool is(std::string); - - //CQ,CQ - //,Ϣ - std::string get(); - - //CQ,ؼӦֵ - //Ҳ,򷵻ؿַ - //,ؿַ - std::string get(std::string key); - std::vector keys(); - - CQ::CodeMsgsFor begin(); - CQ::CodeMsgsFor end(); - - void debug(); - - }; - - struct code { - //[CQ:image,file={1}] - ԶͼƬ - //ļ QĿ¼\data\image\ Ϊ - static std::string image(std::string fileurl); - - //[CQ:record,file={1},magic={2}] - - //ļ QĿ¼\data\record\ Ϊ - static std::string record(std::string fileurl, bool magic); - - //[CQ:face,id={1}] - QQ - static std::string face(int faceid); - - //[CQ:face,id={1}] - QQ - //static std::string face(CQ::face face); - - //[CQ:at,qq={1}] - @ij - static std::string at(long long QQ); - - //[CQ:effect,type=art,id=2003,content=С] - ħ - static std::string effect(std::string type, int id, std::string content); - - //[CQ:sign,title=ɹҸ,image=http://pub.idqqimg.com/pc/misc/files/20170825/cc9103d0db0b4dcbb7a17554d227f4d7.jpg] - ǩ - - //[CQ:hb, title = ϲ] - (ֻ,ܷ) - - //[CQ:shake, id = 1] - һ(ԭڶֺ֧Ϣʹ) - - //[CQ:sface,id={1}] - С - - //[CQ:bface,id={1}] - ԭ - - //[CQ:emoji,id={1}] - emoji - - //[CQ:rps,type={1}] - Ͳȭħ - //Ͳ֧Զtype - //1 Ϊ ʯͷ - //2 Ϊ - //3 Ϊ - - //[CQ:dice,type={1}] - ħ - //Ͳ֧Զtype - //typeΪӵ - - //[CQ:anonymous,ignore={1}] - Ϣ(֧ȺϢʹ) - //Ϣͷ - //ignoreΪtrueʱ,ʧתΪͨϢ - - //[CQ:music,type={1},id={2}] - - //typeΪƽ̨,֧qq163xiami - //idΪid - - //[CQ:music,type=custom,url={1},audio={2},title={3},content={4},image={5}] - Զ - //urlΪ,ҳ棨ҳ - //audioΪƵӣmp3ӣ - //titleΪֵı⣬12 - //contentΪֵļ飬30ڡòɱ - //imageΪֵķͼƬӡΪջ򱻺ԣʾĬͼƬ - //!ԶֻΪһϢ - - //[CQ:share,url={1},title={2},content={3},image={4}] - ӷ - //urlΪӡ - //titleΪı⣬12ڡ - //contentΪļ飬30ڡòɱԡ - //imageΪͼƬӡΪջ򱻺ԣʾĬͼƬ - //!ӷֻΪһϢ - - }; -} \ No newline at end of file diff --git a/CQSDK/CQMsgSend.h b/CQSDK/CQMsgSend.h deleted file mode 100644 index 6aa52a1d..00000000 --- a/CQSDK/CQMsgSend.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include "bufstream.h" - -namespace CQ -{ - enum class msgtype : int { Private = 0, Group = 1, Discuss = 2 }; - - class msg final : public CQstream - { - long long ID; - int subType = 0; - - public: - /* - Type: - 0=msgtype:: - 1=msgtype::Ⱥ - 2=msgtype:: - */ - msg(long long GroupID_Or_QQID, msgtype Type) noexcept; - /* - Type: - 0= - 1=Ⱥ - 2= - */ - msg(long long GroupID_Or_QQID, int Type) noexcept; - - void send() noexcept override; - }; -} diff --git a/CQSDK/CQconstant.h b/CQSDK/CQconstant.h deleted file mode 100644 index dd1bdabe..00000000 --- a/CQSDK/CQconstant.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -// ɫ -#define Log_Debug 0 -// Ϣ ɫ -#define Log_Info 10 -// Ϣ(ɹ) ɫ -#define Log_InfoSuccess 11 -// Ϣ() ɫ -#define Log_InfoRecv 12 -// Ϣ() ɫ -#define Log_InfoSend 13 -// ɫ -#define Log_Warning 20 -// ɫ -#define Log_Error 30 -// -#define Log_Fatal 40 - -// شϢٴݸӦ -//ע⣺ӦȼΪ(10000)ʱʹñֵ -#define Msg_Blocked 1 -// ϢݸӦ -#define Msg_Ignored 0 - -#define FloatingWindows_Green 1 -#define FloatingWindows_Orange 2 -#define FloatingWindows_Red 3 -#define FloatingWindows_DeepRed 4 -#define FloatingWindows_Black 5 -#define FloatingWindows_Grey 6 - -#define RequestAccepted 1 -#define RequestRefused 2 - -#define RequestGroupAdd 1 -#define RequestGroupInvite 2 diff --git a/CQSDK/cqdefine.h b/CQSDK/cqdefine.h deleted file mode 100644 index ed600447..00000000 --- a/CQSDK/cqdefine.h +++ /dev/null @@ -1,12 +0,0 @@ -/* -CoolQ SDK for VS2017 -Api Version 9.10 -Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs -*/ -#pragma once - - -#define CQAPIVER 9 -#define CQAPIVERTEXT "9" - -typedef int CQBOOL; diff --git a/CQSDKCPP/CQAPI.cpp b/CQSDKCPP/CQAPI.cpp deleted file mode 100644 index d83edda8..00000000 --- a/CQSDKCPP/CQAPI.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "CQAPI.h" -#include "CQEVE.h" - -int AuthCode; -CQEVENT(int, Initialize, 4)(const int AuthCode) -{ - ::AuthCode = AuthCode; - return 0; -} - -int getAuthCode() noexcept -{ - return AuthCode; -} - -int CQ::getAuthCode() noexcept -{ - return AuthCode; -} diff --git a/CQSDKCPP/CQAPI_EX.cpp b/CQSDKCPP/CQAPI_EX.cpp deleted file mode 100644 index 9c2fafc2..00000000 --- a/CQSDKCPP/CQAPI_EX.cpp +++ /dev/null @@ -1,504 +0,0 @@ -#include "CQAPI_EX.h" - -#include - - -#include "CQAPI.h" -#include "Unpack.h" -#include "CQEVE_GroupMsg.h" -#include "CQTools.h" - -using namespace CQ; -using namespace std; -int lasterr; - -StrangerInfo::StrangerInfo(const char* msg) -{ - if (msg != nullptr && msg[0] != '\0') - { - Unpack p(base64_decode(msg)); - QQID = p.getLong(); - nick = p.getstring(); - sex = p.getInt(); - age = p.getInt(); - } -} - -string StrangerInfo::tostring() const -{ - return string("{") - + "QQ:" + to_string(QQID) - + " ,dz:" + nick - + " ,Ա:" + (sex == 255 ? "δ֪" : sex == 1 ? "" : "Ů") - + " ,:" + to_string(age) - + "}"; -} - -void GroupInfo::setdata(Unpack& u) -{ - llGroup = u.getLong(); - strGroupName = u.getstring(); - nGroupSize = u.getInt(); // Ⱥ - nMaxGroupSize = u.getInt(); //Ⱥģ - nFriendCnt = u.getInt(); // -} - -GroupInfo::GroupInfo(long long group) -{ - const char* data = CQ_getGroupInfo(getAuthCode(), group, true); - if (!data || data[0] == '\0')return; - Unpack pack(base64_decode(data)); - if (!pack.len())return; - setdata(pack); -} - -std::string GroupInfo::tostring() const -{ - return strGroupName + "(" + std::to_string(llGroup) + ")[" + std::to_string(nGroupSize) + "/" + - std::to_string(nMaxGroupSize) + "]"; -} - -void GroupMemberInfo::setdata(Unpack& u) -{ - Group = u.getLong(); - QQID = u.getLong(); - Nick = u.getstring(); - GroupNick = u.getstring(); - Gender = u.getInt(); - Age = u.getInt(); - Region = u.getstring(); - AddGroupTime = u.getInt(); - LastMsgTime = u.getInt(); - LevelName = u.getstring(); - permissions = u.getInt(); - NaughtyRecord = u.getInt() == 1; - Title = u.getstring(); - ExpireTime = u.getInt(); - canEditGroupNick = u.getInt() == 1; -} - -GroupMemberInfo::GroupMemberInfo(Unpack& msg) { setdata(msg); } - -GroupMemberInfo::GroupMemberInfo(const char* msg) -{ - if (msg != nullptr && msg[0] != '\0') - { - Unpack u(base64_decode(msg)); - setdata(u); - } -} - -GroupMemberInfo::GroupMemberInfo(const vector& data) -{ - if (!data.empty()) - { - Unpack u(data); - setdata(u); - } -} - -string GroupMemberInfo::tostring() const -{ - string s = "{"; - s += "Ⱥ:"; - s += to_string(Group); - s += " ,QQ:"; - s += to_string(QQID); - s += " ,dz:"; - s += Nick; - s += " ,Ƭ:"; - s += GroupNick; - s += " ,Ա:"; - s += (Gender == 255 ? "δ֪" : Gender == 1 ? "" : "Ů"); - s += " ,:"; - s += to_string(Age); - s += " ,:"; - s += Region; - s += " ,Ⱥʱ:"; - s += to_string(AddGroupTime); - s += " ,:"; - s += to_string(LastMsgTime); - s += " ,ȼ_:"; - s += LevelName; - s += " ,Ȩ:"; - s += (permissions == 3 ? "Ⱥ" : permissions == 2 ? "Ա" : "ȺԱ"); - s += "("; - s += to_string(permissions); - s += ")"; - s += " ,¼Ա:"; - s += to_string(NaughtyRecord); - s += " ,רͷ:"; - s += Title; - s += " ,רͷιʱ:"; - s += to_string(ExpireTime); - s += " ,޸Ƭ:"; - s += to_string(canEditGroupNick); - s += "}"; - return s; -} - -FriendInfo::FriendInfo(Unpack p) -{ - QQID = p.getLong(); - nick = p.getstring(); - remark = p.getstring(); -} - -std::string FriendInfo::tostring() const -{ - return remark + '(' + std::to_string(QQID) + ')' + ((remark == nick) ? "" : "" + nick + ""); -} - -//־ -int CQ::addLog(const int Priorty, const char* Type, const char* Content) noexcept -{ - return lasterr = CQ_addLog(getAuthCode(), Priorty, Type, Content); -} - -//ͺϢ -int CQ::sendPrivateMsg(const long long QQ, const char* msg) noexcept -{ - return CQ_sendPrivateMsg(getAuthCode(), QQ, msg); -} - -//ͺϢ -int CQ::sendPrivateMsg(const long long QQ, const std::string& msg) noexcept { return sendPrivateMsg(QQ, msg.c_str()); } - -//ȺϢ -int CQ::sendGroupMsg(const long long GroupID, const char* msg) noexcept -{ - return CQ_sendGroupMsg(getAuthCode(), GroupID, msg); -} - -//ȺϢ -int CQ::sendGroupMsg(const long long GroupID, const std::string& msg) noexcept -{ - return sendGroupMsg(GroupID, msg.c_str()); -} - -int CQ::sendDiscussMsg(const long long DiscussID, const char* msg) noexcept -{ - return CQ_sendDiscussMsg(getAuthCode(), DiscussID, msg); -} - -//Ϣ -int CQ::sendDiscussMsg(const long long DiscussID, const std::string& msg) noexcept -{ - return sendDiscussMsg(DiscussID, msg.c_str()); -} - -// -int CQ::sendLike(const long long QQID, const int times) noexcept -{ - return lasterr = CQ_sendLikeV2(getAuthCode(), QQID, times); -} - - -//ȡCookies (ã˽ӿҪϸȨ) -const char* CQ::getCookies(const char* Domain) noexcept { return CQ_getCookiesV2(getAuthCode(), Domain); } - -//ȡCookies (ã˽ӿҪϸȨ) -const char* CQ::getCookies(const std::string& Domain) noexcept { return getCookies(Domain.c_str()); } - -// -const char* CQ::getRecord(const char* file, const char* outformat) noexcept -{ - return CQ_getRecordV2(getAuthCode(), file, outformat); -} - -// -std::string CQ::getRecord(const std::string& file, const std::string& outformat) noexcept -{ - return getRecord(file.c_str(), outformat.c_str()); -} - -//ȡCsrfToken (ã˽ӿҪϸȨ) -int CQ::getCsrfToken() noexcept { return CQ_getCsrfToken(getAuthCode()); } - -//ȡӦĿ¼ -const char* CQ::getAppDirectory() noexcept { return CQ_getAppDirectory(getAuthCode()); } - -//ȡ¼QQ -long long CQ::getLoginQQ() noexcept { return CQ_getLoginQQ(getAuthCode()); } - -//ȡ¼dz -const char* CQ::getLoginNick() noexcept { return CQ_getLoginNick(getAuthCode()); } - -//ȺԱƳ -int CQ::setGroupKick(const long long GroupID, const long long QQID, const CQBOOL refuseForever) noexcept -{ - return lasterr = CQ_setGroupKick(getAuthCode(), GroupID, QQID, refuseForever); -} - -//ȺԱ -int CQ::setGroupBan(const long long GroupID, const long long QQID, const long long banTime) noexcept -{ - return lasterr = CQ_setGroupBan(getAuthCode(), GroupID, QQID, banTime); -} - -//ȺԱ -int CQ::setGroupAdmin(const long long GroupID, const long long QQID, const CQBOOL isAdmin) noexcept -{ - return lasterr = CQ_setGroupAdmin(getAuthCode(), GroupID, QQID, isAdmin); -} - -//ȺԱרͷ -int CQ::setGroupSpecialTitle(const long long GroupID, const long long QQID, const char* Title, - const long long ExpireTime) noexcept -{ - return lasterr = CQ_setGroupSpecialTitle(getAuthCode(), GroupID, QQID, Title, ExpireTime); -} - -//ȺԱרͷ -int CQ::setGroupSpecialTitle(const long long GroupID, const long long QQID, const std::string& Title, - const long long ExpireTime) noexcept -{ - return setGroupSpecialTitle(GroupID, QQID, Title.c_str(), ExpireTime); -} - -//ȫȺ -int CQ::setGroupWholeBan(const long long GroupID, const CQBOOL isBan) noexcept -{ - return lasterr = CQ_setGroupWholeBan(getAuthCode(), GroupID, isBan); -} - -//AnonymousȺԱ -int CQ::setGroupAnonymousBan(const long long GroupID, const char* AnonymousToken, const long long banTime) noexcept -{ - return lasterr = CQ_setGroupAnonymousBan(getAuthCode(), GroupID, AnonymousToken, banTime); -} - -//ȺAnonymous -int CQ::setGroupAnonymous(const long long GroupID, const CQBOOL enableAnonymous) noexcept -{ - return lasterr = CQ_setGroupAnonymous(getAuthCode(), GroupID, enableAnonymous); -} - -//ȺԱƬ -int CQ::setGroupCard(const long long GroupID, const long long QQID, const char* newGroupNick) noexcept -{ - return lasterr = CQ_setGroupCard(getAuthCode(), GroupID, QQID, newGroupNick); -} - -//ȺԱƬ -int CQ::setGroupCard(const long long GroupID, const long long QQID, const std::string& newGroupNick) noexcept -{ - return setGroupCard(GroupID, QQID, newGroupNick.c_str()); -} - -//Ⱥ˳ -int CQ::setGroupLeave(const long long GroupID, const CQBOOL isDismiss) noexcept -{ - return lasterr = CQ_setGroupLeave(getAuthCode(), GroupID, isDismiss); -} - -//˳ -int CQ::setDiscussLeave(const long long DiscussID) noexcept -{ - return lasterr = CQ_setDiscussLeave(getAuthCode(), DiscussID); -} - -//ú -int CQ::setFriendAddRequest(const char* RequestToken, const int ReturnType, const char* Remarks) noexcept -{ - return lasterr = CQ_setFriendAddRequest(getAuthCode(), RequestToken, ReturnType, Remarks); -} - -//Ⱥ -int CQ::setGroupAddRequest(const char* RequestToken, const int RequestType, const int ReturnType, - const char* Reason) noexcept -{ - return lasterr = CQ_setGroupAddRequestV2(getAuthCode(), RequestToken, RequestType, ReturnType, Reason); -} - -//ʾ -int CQ::setFatal(const char* ErrorMsg) noexcept { return lasterr = CQ_setFatal(getAuthCode(), ErrorMsg); } - -//ȡȺԱϢ (ֻ֧) -GroupMemberInfo CQ::getGroupMemberInfo(const long long GroupID, const long long QQID, - const CQBOOL disableCache) noexcept -{ - return GroupMemberInfo(CQ_getGroupMemberInfoV2(getAuthCode(), GroupID, QQID, disableCache)); -} - -//ȡİϢ (ֻ֧) -StrangerInfo CQ::getStrangerInfo(const long long QQID, const CQBOOL DisableCache) noexcept -{ - return StrangerInfo(CQ_getStrangerInfo(getAuthCode(), QQID, DisableCache)); -} - -//ȡȺԱб -std::vector CQ::getGroupMemberList(const long long GroupID) -{ - const char* ret = CQ_getGroupMemberList(getAuthCode(), GroupID); - if (!ret || ret[0] == '\0') return {}; - const string data(base64_decode(ret)); - if (data.empty())return {}; - vector infovector; - Unpack u(data); - auto i = u.getInt(); - while (--i && u.len() > 0) - { - auto tmp = u.getUnpack(); - infovector.emplace_back(tmp); - } - - return infovector; -} - -#include -//ȡȺб -std::map CQ::getGroupList(bool disableCache) -{ - static std::map ret; - static time_t lastUpdateTime = 0; - - const time_t timeNow = time(nullptr); - if (!disableCache && timeNow - lastUpdateTime < 600 && !ret.empty()) - { - return ret; - } - - ret.clear(); - lastUpdateTime = timeNow; - - const char* src = CQ_getGroupList(getAuthCode()); - if (!src || src[0] == '\0') return {}; - const auto data(base64_decode(src)); // - if (data.empty())return {}; - Unpack pack(data); // תΪUnpack - - pack.getInt(); //ȡȺ, ֵڴ˲û - while (pack.len() > 0) - { - //ʣ,ͼȡ - auto tep = pack.getUnpack(); //ȡһȺ - auto ID = tep.getLong(); //ȡGroupID - const auto name = tep.getstring(); //ȡȺ - ret[ID] = name; //дmap - } - return ret; -} - -//ȡб -std::map CQ::getFriendList(bool disableCache) -{ - static std::map ret; - static time_t lastUpdateTime = 0; - - const time_t timeNow = time(nullptr); - if (!disableCache && timeNow - lastUpdateTime < 600 && !ret.empty()) - { - return ret; - } - - ret.clear(); - lastUpdateTime = timeNow; - - const char* src = CQ_getFriendList(getAuthCode(), false); - if (!src || src[0] == '\0') return {}; - const auto data(base64_decode(src)); // - if (data.empty())return {}; - Unpack pack(data); // ȡԭʼתΪUnpack - int Cnt = pack.getInt(); //ȡ - while (Cnt--) - { - FriendInfo info(pack.getUnpack()); //ȡ - ret[info.QQID] = info; //дmap - } - return ret; -} - -bool CQ::canSendImage() noexcept -{ - return CQ_canSendImage(getAuthCode()) > 0; -} - -bool CQ::canSendRecord() noexcept -{ - return CQ_canSendRecord(getAuthCode()) > 0; -} - -const char* CQ::getImage(const char* file) noexcept -{ - return CQ_getImage(getAuthCode(), file); -} - -const char* CQ::getImage(const std::string& file) noexcept -{ - return getImage(file.c_str()); -} - -int CQ::deleteMsg(const long long MsgId) noexcept -{ - return lasterr = CQ_deleteMsg(getAuthCode(), MsgId); -} - -const char* CQ::getlasterrmsg() noexcept -{ - switch (lasterr) - { - case 0: return "ɹ"; - case -1: return "ʧ"; - case -2: return "δյظδͳɹ"; - case -3: return "ϢΪ"; - case -4: return "Ϣ쳣"; - case -5: return "־δ"; - case -6: return "־ȼ"; - case -7: return "ʧ"; - case -8: return "ֶ֧ϵͳʺŲ"; - case -9: return "ʺŲڸȺڣϢ޷"; - case -10: return "û/Ⱥ"; - case -11: return "ݴ޷"; - case -12: return "ֶ֧AnonymousԱ"; - case -13: return "޷ҪԵAnonymousԱ"; - case -14: return "δ֪ԭ򣬲ʧ"; - case -15: return "ȺδAnonymousԹܣAnonymousʺű"; - case -16: return "ʺŲȺڻ޷˳/ɢȺ"; - case -17: return "ʺΪȺ޷˳Ⱥ"; - case -18: return "ʺŷȺ޷ɢȺ"; - case -19: return "ʱϢʧЧδ"; - case -20: return ""; - case -21: return "ʱϢʧЧδ"; - case -22: return "ȡQQϢʧ"; - case -23: return "ҲĿQQĹϵϢ޷"; - case -99: return "õĹ޷ڴ˰汾ʵ"; - case -101: return "Ӧù"; - case -102: return "ǺϷӦ"; - case -103: return "ǺϷӦ"; - case -104: return "ӦòڹInformation"; - case -105: return "޷ӦϢ"; - case -106: return "ļӦIDͬ"; - case -107: return "Ϣ"; - case -108: return "AppInfoصApi汾ֱ֧Ӽأ֧Api汾Ϊ9()ӦֱӼ"; - case -109: return "AppInfoصAppID"; - case -110: return "ȱʧAppInfoصAppIDӦ[Appid].jsonļ"; - case -111: return "[Appid].jsonļڵAppIDļͬ"; - case -120: return "ApiȨպ(Initialize)"; - case -121: return "ApiȨպ(Initialize)ֵ0"; - case -122: return "Զ޸ĿQļȡزرտQ"; - case -150: return "޷ӦϢ"; - case -151: return "ӦϢJsonʧܣJsonǷȷ"; - case -152: return "Api汾ɻ"; - case -153: return "ӦϢȱʧ"; - case -154: return "AppidϷ"; - case -160: return "¼(Type)ȱʧ"; - case -161: return "¼(Function)ȱʧ"; - case -162: return "ӦȼΪ10000200003000040000еһ"; - case -163: return "¼(Api)֧ӦApi汾"; - case -164: return "ӦApi汾8ʹ°汾ͣõ¼(Type)1(Ϣ)3(ʱϢ)"; - case -165: return "¼Ϊ2(ȺϢ)4(Ϣ)21(˽Ϣ)ȱʽ(regex)ıʽ(expression)"; - case -166: return "Ϊյʽ(regex)key"; - case -167: return "Ϊյʽ(regex)ıʽ(expression)"; - case -168: return "Ӧ¼(event)idڻΪ0"; - case -169: return "Ӧ¼(event)idظ"; - case -180: return "Ӧ״̬(status)idڻΪ0"; - case -181: return "Ӧ״̬(status)periodڻô"; - case -182: return "Ӧ״̬(status)idظ"; - case -201: return "޷ӦãӦļ"; - case -202: return "Api汾ɻ"; - case -997: return "Ӧδ"; - case -998: return "ӦõAuth֮ Q Api"; - default: return "δ֪"; - } -} diff --git a/CQSDKCPP/CQEVE.cpp b/CQSDKCPP/CQEVE.cpp deleted file mode 100644 index 4d279128..00000000 --- a/CQSDKCPP/CQEVE.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* -ļEX¼ʵ -*/ -#include -#include -#include "CQEVE_ALL.h" - -#include "CQAPI_EX.h" -#include "CQTools.h" -#include "Unpack.h" - -#define WIN32_LEAN_AND_MEAN -#include - -using namespace CQ; - - -EVEMsg::EVEMsg(const int subType, const int msgId, const long long fromQQ, std::string message, const int font) noexcept - : subType(subType), msgId(msgId), fromQQ(fromQQ), message(move(message)), font(font) -{ -} - -//ʵû -bool EVEMsg::isUser() const noexcept -{ - switch (fromQQ) - { - case 1000000: // ϵͳʾ - case 80000000: // - return false; - default: - return true; - } -} - -EVEGroupMsg::EVEGroupMsg(const int subType, const int msgId, const long long fromGroup, const long long fromQQ, - const char* fromAnonymous, - const char* msg, const int font) noexcept - : EVEMsg(subType, msgId, fromQQ, msg, font), fromAnonymousInfo(), fromGroup(fromGroup), - fromAnonymousToken(fromAnonymous) -{ -} - -EVEGroupMsg::~EVEGroupMsg() noexcept { delete fromAnonymousInfo; } - - -bool EVEGroupMsg::isAnonymous() const noexcept { return fromQQ == 80000000; } - - -AnonymousInfo& EVEGroupMsg::getFromAnonymousInfo() noexcept(false) -{ - if (isAnonymous()) - return - fromAnonymousInfo != nullptr - ? *fromAnonymousInfo - : *(fromAnonymousInfo = new AnonymousInfo(fromAnonymousToken)); - throw std::logic_error("Trying to Get Anonymous Info from Non-anonymous User"); -} - -bool EVEGroupMsg::setGroupKick(const bool refusedAddAgain) const noexcept -{ - return !CQ::setGroupKick(fromGroup, fromQQ, refusedAddAgain); -} - -bool EVEGroupMsg::setGroupBan(const long long banTime) const noexcept -{ - if (isAnonymous()) - { - return !setGroupAnonymousBan(fromGroup, fromAnonymousToken, banTime); - } - return !CQ::setGroupBan(fromGroup, fromQQ, banTime); -} - -bool EVEGroupMsg::setGroupAdmin(const bool isAdmin) const noexcept -{ - return !CQ::setGroupAdmin(fromGroup, fromQQ, isAdmin); -} - -bool EVEGroupMsg::setGroupSpecialTitle(const std::string& Title, const long long ExpireTime) const noexcept -{ - return !CQ::setGroupSpecialTitle(fromGroup, fromQQ, Title, ExpireTime); -} - -bool EVEGroupMsg::setGroupWholeBan(const bool isBan) const noexcept -{ - return CQ::setGroupWholeBan(fromGroup, isBan) != 0; -} - -bool EVEGroupMsg::setGroupAnonymous(const bool enableAnonymous) const noexcept -{ - return CQ::setGroupAnonymous(fromGroup, enableAnonymous) != 0; -} - -bool EVEGroupMsg::setGroupCard(const std::string& newGroupNick) const noexcept -{ - return CQ::setGroupCard(fromGroup, fromQQ, newGroupNick) != 0; -} - -bool EVEGroupMsg::setGroupLeave(const bool isDismiss) const noexcept -{ - return CQ::setGroupLeave(fromGroup, isDismiss) != 0; -} - -GroupMemberInfo EVEGroupMsg::getGroupMemberInfo(const bool disableCache) const noexcept -{ - return CQ::getGroupMemberInfo(fromGroup, fromQQ, disableCache); -} - -std::vector EVEGroupMsg::getGroupMemberList() const noexcept -{ - return CQ::getGroupMemberList(fromGroup); -} - -EVEPrivateMsg::EVEPrivateMsg(const int subType, const int msgId, const long long fromQQ, const char* msg, - const int font) noexcept - : EVEMsg(subType, msgId, fromQQ, msg, font) -{ -} - -//Ժ -bool EVEPrivateMsg::fromPrivate() const noexcept { return subType == 11; } - -//״̬ -bool EVEPrivateMsg::fromOnlineStatus() const noexcept { return subType == 1; } - -//Ⱥʱ -bool EVEPrivateMsg::fromGroup() const noexcept { return subType == 2; } - -//ʱ -bool EVEPrivateMsg::fromDiscuss() const noexcept { return subType == 3; } - -msg EVEPrivateMsg::sendMsg() const noexcept { return msg(fromQQ, msgtype::Private); } -msg EVEGroupMsg::sendMsg() const noexcept { return msg(fromGroup, msgtype::Group); } -msg EVEDiscussMsg::sendMsg() const noexcept { return msg(fromQQ, msgtype::Discuss); } - -int EVEPrivateMsg::sendMsg(const char* msg) const noexcept { return sendPrivateMsg(fromQQ, msg); } -int EVEPrivateMsg::sendMsg(const std::string& msg) const noexcept { return sendPrivateMsg(fromQQ, msg); } -int EVEGroupMsg::sendMsg(const char* msg) const noexcept { return sendGroupMsg(fromGroup, msg); } -int EVEGroupMsg::sendMsg(const std::string& msg) const noexcept { return sendGroupMsg(fromGroup, msg); } -int EVEDiscussMsg::sendMsg(const char* msg) const noexcept { return sendDiscussMsg(fromDiscuss, msg); } -int EVEDiscussMsg::sendMsg(const std::string& msg) const noexcept { return sendDiscussMsg(fromDiscuss, msg); } - -EVEDiscussMsg::EVEDiscussMsg(const int subType, const int msgId, const long long fromDiscuss, const long long fromQQ, - const char* msg, const int font) noexcept - : EVEMsg(subType, msgId, fromQQ, msg, font), fromDiscuss(fromDiscuss) -{ -} - -bool EVEDiscussMsg::leave() const noexcept { return !setDiscussLeave(fromDiscuss); } - -void EVEStatus::color_green() noexcept { color = 1; } -void EVEStatus::color_orange() noexcept { color = 2; } -void EVEStatus::color_red() noexcept { color = 3; } -void EVEStatus::color_crimson() noexcept { color = 4; } -void EVEStatus::color_black() noexcept { color = 5; } -void EVEStatus::color_gray() noexcept { color = 6; } - -std::string CQ::statusEVEreturn(EVEStatus& eve) noexcept -{ - Unpack pack; - std::string _ret = pack.add(eve.data).add(eve.dataf).add(eve.color).getAll(); - _ret = base64_encode(_ret); - return _ret; -} - -EVERequest::EVERequest(const int sendTime, const long long fromQQ, const char* msg, const char* responseFlag) noexcept - : sendTime(sendTime), fromQQ(fromQQ), msg(msg), responseFlag(responseFlag) -{ -} - -EVERequestAddFriend::EVERequestAddFriend(const int subType, const int sendTime, const long long fromQQ, const char* msg, - const char* responseFlag) noexcept - : EVERequest(sendTime, fromQQ, msg, responseFlag), subType(subType), fromGroup(0) -{ -} - -void EVERequestAddFriend::pass(const std::string& msg) const noexcept -{ - setFriendAddRequest(responseFlag, RequestAccepted, msg.c_str()); -} - -void EVERequestAddFriend::fail(const std::string& msg) const noexcept -{ - setFriendAddRequest(responseFlag, RequestRefused, msg.c_str()); -} - -EVERequestAddGroup::EVERequestAddGroup(const int subType, const int sendTime, const long long fromGroup, - const long long fromQQ, - const char* const msg, const char* const responseFlag) noexcept - : EVERequest(sendTime, fromQQ, msg, responseFlag), subType(subType), fromGroup(fromGroup) -{ -} - -void EVERequestAddGroup::pass(const std::string& msg) const noexcept -{ - setGroupAddRequest(responseFlag, subType, RequestAccepted, msg.c_str()); -} - -void EVERequestAddGroup::fail(const std::string& msg) const noexcept -{ - setGroupAddRequest(responseFlag, subType, RequestRefused, msg.c_str()); -} - -AnonymousInfo::AnonymousInfo(const char* msg) noexcept -{ - if (msg != nullptr && msg[0] != '\0') - { - Unpack p(base64_decode(msg)); - AID = p.getLong(); - AnonymousNick = p.getstring(); - } -} - -regexMsg::regexMsg(const std::string& msg) -{ - Unpack msgs(base64_decode(msg)); - auto len = msgs.getInt(); //ȡ - while (len-- > 0) - { - auto tep = msgs.getUnpack(); - const auto key = tep.getstring(); - const auto value = tep.getstring(); - if (key.empty()) - { - return; - } - regexMap[key] = value; - } -} - -std::string regexMsg::get(const std::string& key) -{ - return regexMap[key]; -} - -std::string regexMsg::operator[](const std::string& key) -{ - return regexMap[key]; -} diff --git a/CQSDKCPP/CQTools.cpp b/CQSDKCPP/CQTools.cpp index b2287fd2..c1262ba9 100644 --- a/CQSDKCPP/CQTools.cpp +++ b/CQSDKCPP/CQTools.cpp @@ -138,5 +138,6 @@ std::string& msg_decode(std::string& s, const bool isCQ) msg_replace(s, "]", "]"); msg_replace(s, ",", "\t"); msg_replace(s, "&", "&"); + //msg_replace(s, " ", " "); return s; } diff --git a/CQSDKCPP/CQstream.cpp b/CQSDKCPP/CQstream.cpp deleted file mode 100644 index 3de9fcf0..00000000 --- a/CQSDKCPP/CQstream.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/*ļͷļʵ*/ -#include -#include -#include "CQAPI_EX.h" -#include "bufstream.h" -#include "CQLogger.h" -#include "CQMsgSend.h" -#include "CQconstant.h" - -using namespace CQ; -using namespace std; - -logger::logger(std::string title) noexcept : title(std::move(title)) -{ -} - -void logger::setTitle(std::string title) noexcept { this->title = std::move(title); } - -void logger::Debug(const std::string& msg) const noexcept { Debug(msg.c_str()); } - -void logger::Info(const std::string& msg) const noexcept { Info(msg.c_str()); } - -void logger::InfoSuccess(const std::string& msg) const noexcept { InfoSuccess(msg.c_str()); } - -void logger::InfoRecv(const std::string& msg) const noexcept { InfoRecv(msg.c_str()); } - -void logger::InfoSend(const std::string& msg) const noexcept { InfoSend(msg.c_str()); } - -void logger::Warning(const std::string& msg) const noexcept { Warning(msg.c_str()); } - -void logger::Error(const std::string& msg) const noexcept { Error(msg.c_str()); } - -void logger::Fatal(const std::string& msg) const noexcept { Fatal(msg.c_str()); } - -void logger::Debug(const char* const msg) const noexcept { addLog(Log_Debug, title.c_str(), msg); } - -void logger::Info(const char* const msg) const noexcept { addLog(Log_Info, title.c_str(), msg); } - -void logger::InfoSuccess(const char* const msg) const noexcept { addLog(Log_InfoSuccess, title.c_str(), msg); } - -void logger::InfoRecv(const char* const msg) const noexcept { addLog(Log_InfoRecv, title.c_str(), msg); } - -void logger::InfoSend(const char* const msg) const noexcept { addLog(Log_InfoSend, title.c_str(), msg); } - -void logger::Warning(const char* const msg) const noexcept { addLog(Log_Warning, title.c_str(), msg); } - -void logger::Error(const char* const msg) const noexcept { addLog(Log_Error, title.c_str(), msg); } - -void logger::Fatal(const char* const msg) const noexcept { addLog(Log_Fatal, title.c_str(), msg); } - -logstream logger::Debug() const noexcept { return logstream(title, Log_Debug); } - -logstream logger::Info() const noexcept { return logstream(title, Log_Info); } - -logstream logger::InfoSuccess() const noexcept { return logstream(title, Log_InfoSuccess); } - -logstream logger::InfoRecv() const noexcept { return logstream(title, Log_InfoRecv); } - -logstream logger::InfoSend() const noexcept { return logstream(title, Log_InfoSend); } - -logstream logger::Warning() const noexcept { return logstream(title, Log_Warning); } - -logstream logger::Error() const noexcept { return logstream(title, Log_Error); } - -logstream logger::Fatal() const noexcept { return logstream(title, Log_Fatal); } - -void CQ::send(CQstream& log) noexcept -{ - log.send(); - log.clear(); -} - -void CQ::flush(CQstream& log) noexcept { log.flush(); } -void CQ::endl(CQstream& log) noexcept { log << "\r\n"; } - -void CQstream::clear() noexcept { buf.clear(); } - -CQstream& CQstream::append(const string& s) noexcept -{ - buf += s; - return *this; -} - -CQstream& CQstream::operator<<(const string& s) noexcept { return append(s); } - -CQstream& CQstream::append(const int& i) noexcept -{ - buf += to_string(i); - return *this; -} - -CQstream& CQstream::operator<<(const int& i) noexcept { return append(i); } - -CQstream& CQstream::append(const size_t& i) noexcept -{ - buf += to_string(i); - return *this; -} - -CQstream& CQstream::operator<<(const size_t& i) noexcept { return append(i); } - -CQstream& CQstream::append(const long long& l) noexcept -{ - buf += to_string(l); - return *this; -} - -CQstream& CQstream::operator<<(const long long& l) noexcept { return append(l); } - -CQstream& CQstream::append(const char* const c) noexcept -{ - buf += c; - return *this; -} - -CQstream& CQstream::operator<<(const char* const c) noexcept { return append(c); } - -CQstream& CQstream::operator<<(void (*control)(CQstream&)) -{ - control(*this); - return *this; -} - -void CQstream::flush() noexcept { send(); } - -inline CQstream::~CQstream() noexcept = default; - -inline logstream::logstream(std::string title, const int Log_flag) noexcept : flag(Log_flag), title(std::move(title)) -{ -} - -void logstream::send() noexcept -{ - if (buf.empty())return; - addLog(flag, title.c_str(), buf.c_str()); -} - -msg::msg(const long long GroupID_Or_QQID, const msgtype Type) noexcept : ID(GroupID_Or_QQID), - subType(static_cast(Type)) -{ -} - -msg::msg(const long long GroupID_Or_QQID, const int Type) noexcept : ID(GroupID_Or_QQID), subType(Type) -{ -} - -void msg::send() noexcept -{ - if (buf.empty())return; - switch (subType) - { - case static_cast(msgtype::Private): // - sendPrivateMsg(ID, buf); - break; - case static_cast(msgtype::Group): //Ⱥ - sendGroupMsg(ID, buf); - break; - case static_cast(msgtype::Discuss): // - sendDiscussMsg(ID, buf); - break; - default: - assert(false); - break; - } -} diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index cde8eb36..292e8af4 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -11,12 +11,12 @@ #include "BlackListManager.h" #include "Jsonio.h" #include "STLExtern.hpp" +#include "DDAPI.h" #include "DiceEvent.h" #include "DiceConsole.h" #include "DiceNetwork.h" using namespace std; -using namespace CQ; using namespace nlohmann; using Mark = DDBlackMark; @@ -46,42 +46,29 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) string strNotice; for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("δ") || grp.isset("") || !grp.isGroup)continue; - if (GroupMemberInfo member = getGroupMemberInfo(id, llQQ); member.QQID == llQQ && member.Group == id) - { - if (frame == QQFrame::XianQu) { - bool isValid = false; - for (auto& mem : getGroupMemberList(id)) { - if (mem.QQID == llQQ) { - isValid = true; - break; - } - } - if (!isValid)continue; - } + int authSelf; + if (grp.isset("") || grp.isset("δ") || grp.isset("") || !grp.isGroup + || !(authSelf = DD::getGroupAuth(id, console.DiceMaid, 0)))continue; + if (DD::isGroupMember(grp.ID, llQQ, false)) { strNotice = printGroup(id); - if (grp.isset("ЭЧ")) - { + if (grp.isset("ЭЧ")) { strNotice += "ȺЭЧ"; } else if (grp.isset("")) { - if (mark.isSource(console.DiceMaid) && !mark.isType("local"))sendGroupMsg(id, mark.warning()); + if (mark.isSource(console.DiceMaid) && !mark.isType("local"))DD::sendGroupMsg(id, mark.warning()); strNotice += "Ⱥ"; } - else if (GroupMemberInfo self = getGroupMemberInfo(id, console.DiceMaid); !self.permissions) { - continue; + else if (int authBlack{ DD::getGroupAuth(id,llQQ,0) }; authBlack < 1 || authSelf < 1) { + strNotice += "ȺȨ޻ȡʧ"; } - else if (member.permissions < 1 || member.permissions > 3) { - strNotice += "ԷȺȨ޻ȡʧ"; - } - else if (member.permissions < self.permissions) { + else if (authBlack < authSelf) { if (mark.isSource(console.DiceMaid && !mark.isType("local")))AddMsgToQueue( mark.warning(), id, msgtype::Group); strNotice += "ԷȺȨ޽ϵ"; } - else if (member.permissions > self.permissions) + else if (authSelf > authBlack) { - sendGroupMsg(id, mark.warning()); + DD::sendGroupMsg(id, mark.warning()); grp.leave("Ա" + printQQ(llQQ) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); strNotice += "ԷȺȨ޽ϸߣȺ"; this_thread::sleep_for(1s); @@ -94,7 +81,7 @@ void checkGroupWithBlackQQ(const DDBlackMark& mark, long long llQQ) } else if (console["LeaveBlackQQ"]) { - sendGroupMsg(id, mark.warning()); + DD::sendGroupMsg(id, mark.warning()); grp.leave("Ա" + printQQ(llQQ) + "ͬȺȨޣ\n" + GlobalMsg["strSelfName"] + "ԤȺ"); strNotice += "Ⱥ"; this_thread::sleep_for(1s); @@ -920,7 +907,8 @@ short DDBlackManager::get_group_danger(long long id) const short DDBlackManager::get_qq_danger(long long id) const { - if (auto it = mQQDanger.find(id); it != mQQDanger.end())return it->second; + if (auto it = mQQDanger.find(id); it != mQQDanger.end()) + return it->second; return 0; } @@ -935,6 +923,7 @@ void DDBlackManager::rm_black_group(long long llgroup, FromMsg* msg) if (mGroupDanger[llgroup] >= msg->trusted && msg->fromQQ != console.master()) { msg->reply("עĿȨ޲"); + return; } for (auto [key,index] : multi_range(mGroupIndex, llgroup)) { @@ -956,6 +945,7 @@ void DDBlackManager::rm_black_qq(long long llqq, FromMsg* msg) if (mQQDanger[llqq] >= msg->trusted && msg->fromQQ != console.master()) { msg->reply("עĿȨ޲"); + return; } for (auto [key, index] : multi_range(mQQIndex, llqq)) { @@ -1099,7 +1089,7 @@ void DDBlackManager::add_black_qq(long long llqq, FromMsg* msg) DDBlackMark mark{llqq, 0}; mark.danger = 1; mark.note = msg->strVar["note"]; - if (!mark.note.empty()) { + if (!mark.note.empty() && !msg->strVar.count("user")) { mark.danger = 2; mark.type = "other"; } @@ -1116,11 +1106,11 @@ void DDBlackManager::add_black_qq(long long llqq, FromMsg* msg) msg->note("" + printQQ(llqq) + "ıغ¼"); } -void DDBlackManager::verify(void* pJson, long long operateQQ) +void DDBlackManager::verify(void* pJson, long long operatorQQ) { DDBlackMark mark{pJson}; if (!mark.isValid)return; - int credit = isReliable(operateQQ); + int credit = isReliable(operatorQQ); //ݿǷм¼:-1=;0=ע;1=δȷ;2=ȷ; int is_cloud = -1; if (console["CloudBlackShare"]) @@ -1190,7 +1180,7 @@ void DDBlackManager::verify(void* pJson, long long operateQQ) || (mark.isType("spam") && !console["ListenSpam"]))return; if (mark.type == "local" || mark.type == "other" || mark.isSource(console.DiceMaid)) { if (credit > 0)console.log( - getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼(δ):\n!warning" + UTF8toGBK( + getName(operatorQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼(δ):\n!warning" + UTF8toGBK( static_cast(pJson)->dump()), 1, printSTNow()); return; } @@ -1225,13 +1215,13 @@ void DDBlackManager::verify(void* pJson, long long operateQQ) } if (mark.fromGroup.first && (groupset(mark.fromGroup.first, "") > 0 || groupset(mark.fromGroup.first, "ЭЧ") > 0 || ExceptGroups.count(mark.fromGroup.first)))return; insert(mark); - console.log(getName(operateQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼" + to_string(vBlackList.size() - 1) + ":\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); + console.log(getName(operatorQQ) + "֪ͨ" + GlobalMsg["strSelfName"] + "¼" + to_string(vBlackList.size() - 1) + ":\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); } else { //м¼ DDBlackMark& old_mark = vBlackList[index]; - bool isSource = operateQQ == old_mark.DiceMaid || operateQQ == old_mark.masterQQ; + bool isSource = operatorQQ == old_mark.DiceMaid || operatorQQ == old_mark.masterQQ; //ΣյȼȨ޸ if (old_mark.danger > credit && credit < 255) { if (old_mark.danger != 2)return; @@ -1255,7 +1245,7 @@ void DDBlackManager::verify(void* pJson, long long operateQQ) if (mark.danger != old_mark.danger && credit < 3) { mark.danger = old_mark.danger; } - if(update(mark,index,credit))console.log(getName(operateQQ) + "Ѹ" + GlobalMsg["strSelfName"] + "¼" + to_string(index) + ":\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); + if(update(mark,index,credit))console.log(getName(operatorQQ) + "Ѹ" + GlobalMsg["strSelfName"] + "¼" + to_string(index) + ":\n!warning" + UTF8toGBK(((json*)pJson)->dump()), 1, printSTNow()); } } diff --git a/Dice/CQP.lib b/Dice/CQP.lib deleted file mode 100644 index 8af05707b8ab92790d3bffbe6a75e05da0572c64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9920 zcmcIpO>7fK6n;qxfi#7Lzx)#?aZ=zw6URQ^t3rD0%&~J8Tc4ubS8}Ev)^fKOg-^@2N``*l( zm!Z#dmHf>w+dhlw&qQi^;^O$k#MGo-KQ3Mqc=*rjZA5g1sPi?^3H&^nAUbtN$SahP zt|cK)P(r%nLhhkt@{UW-Eh1ZcEWuKt zU>B|Jb*p0cpBUGtY4>mFeOqFM5`J`UTY>Pjwg;qrIyqF{*3t8p+=7g+;Z$-AY_V8c zD&{xMFQF+E?A5EjNM6lx!sg6@D%C0ull4am1;?(|D!I~zSbEKQumm0@+EigQ^Tld@ zw_3FgUiCL0ooK;7B6AQEuW1B2Q{1%l!Ly#7S_Q@CV)n>zqFx}EE5*H>a8MI%=tK@AipX;<7y=LE4P<>7 zeM06(h#o`E#fct3nn%$mnsH9>o|+CXlb_AcS42-r^*c;M!!;2PWKlm_ZP75Y}- z8}d7cqv>9zLIhB1?5Mk%(*Uj&6u@h4r#8~UZ&jhYpsKMApnkwD-Omasp&L_)-;Y=durMKp( zg5lyy8*ebEDxK(0tK2UJ^mBc+14A!103U>m%e*RWGpMhYTY?86<0+5A*&aL^CrazdZ|vOCG?X_4o--j-=awt4Jr*yod74NJs_OwCDd&d@w`AWH3zEO*vSvecaEG%8NwZ@-zyt9U^ z144+yes$pQ0sEI=pme^xy@N59_kN-G?64Xa0F=sc`9ZrmxP^M+=TTc5## zs~he?oW%&HBvoi{N?s7oWN@(!pjqc~;xkDWdR{4iF7|m^1eX@^NutoRN=bt^W`)Zw zwphM`-XK!ag`QWy_d4LRsbQ(43O%!A0M|i_gFl3pWS<0J|qzV1~g8c1>%cEYtqH_i8k4FtI`#$%- zFmB*{YCORA2&;2%8r4~+CX@xM$pOBJ25j;FJz zWUwBiNPJ0BQ8xS?gU;@l!6{N{k}C9y2Hm5E3@(|SlVqXi6>t&^8(cEq7nv)-E=LS5 lCxa--La$+Gbt%a!>(qk&YeC6%N#X0)ikH%`wy}Z>I?t? diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 1c082cd6..946ba667 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -371,6 +371,7 @@ class CharaCard int set(string key, short val) { + if (key.empty())return -1; key = standard(key); if (pTemplet->defaultSkill.count(key) && val == pTemplet->defaultSkill.find(key)->second) { @@ -383,14 +384,14 @@ class CharaCard int setInfo(const string& key, const string& s) { - if (s.length() > 48)return -1; + if (key.empty() || s.length() > 48)return -1; Info[key] = s; return 0; } int setExp(const string& key, const string& exp) { - if (exp.length() > 48)return -1; + if (key.empty() || exp.length() > 48)return -1; DiceExp[key] = exp; return 0; } diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 89ff52b2..f82a6781 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -36,7 +36,8 @@ #include "APPINFO.h" #include "DiceFile.hpp" #include "Jsonio.h" -#include "CQEVE_ALL.h" +#include "QQEvent.h" +#include "DDAPI.h" #include "ManagerSystem.h" #include "DiceMod.h" #include "DiceMsgSend.h" @@ -56,7 +57,6 @@ #pragma warning(disable:6031) using namespace std; -using namespace CQ; unordered_map UserList{}; ThreadFactory threads; @@ -72,26 +72,26 @@ string strFileLoc; void loadData() { mkDir(DiceDir); - string strLog; - loadDir(loadXML, string(DiceDir + "\\CardTemp\\"), getmCardTemplet(), strLog, true); + ResList logList; + loadDir(loadXML, string(DiceDir + "\\CardTemp\\"), getmCardTemplet(), logList, true); if (loadJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck) < 0 && loadJMap( strFileLoc + "ReplyDeck.json", CardDeck::mReplyDeck) > 0) { - console.log("ǨԶظ" + to_string(CardDeck::mReplyDeck.size()) + "", 1); + logList << "ǨԶظ" + to_string(CardDeck::mReplyDeck.size()) + ""; saveJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck); } fmt->set_help("ظб", "ظб:" + listKey(CardDeck::mReplyDeck)); - if (loadDir(loadJMap, string(DiceDir + "\\PublicDeck\\"), CardDeck::mExternPublicDeck, strLog) < 1) + if (loadDir(loadJMap, string(DiceDir + "\\PublicDeck\\"), CardDeck::mExternPublicDeck, logList) < 1) { loadJMap(strFileLoc + "PublicDeck.json", CardDeck::mExternPublicDeck); loadJMap(strFileLoc + "ExternDeck.json", CardDeck::mExternPublicDeck); } map_merge(CardDeck::mPublicDeck, CardDeck::mExternPublicDeck); //ȡĵ - fmt->load(strLog); + fmt->load(&logList); if (int cnt; (cnt = loadJMap(DiceDir + "\\conf\\CustomHelp.json", CustomHelp)) < 0) { - if (cnt == -1)console.log("Զļjsonʧܣ", 1); + if (cnt == -1)logList << DiceDir + "\\conf\\CustomHelp.jsonʧܣ"; ifstream ifstreamHelpDoc(strFileLoc + "HelpDoc.txt"); if (ifstreamHelpDoc) { @@ -106,20 +106,20 @@ void loadData() if (!CustomHelp.empty()) { saveJMap(DiceDir + "\\conf\\CustomHelp.json", CustomHelp); - console.log("ʼԶ" + to_string(CustomHelp.size()) + "", 1); + logList << "ʼԶ" + to_string(CustomHelp.size()) + ""; } } ifstreamHelpDoc.close(); } map_merge(fmt->helpdoc, CustomHelp); //ȡдʿ - loadDir(load_words, DiceDir + "\\conf\\censor\\", censor, strLog, true); + loadDir(load_words, DiceDir + "\\conf\\censor\\", censor, logList, true); loadJMap(DiceDir + "\\conf\\CustomCensor.json", censor.CustomWords); censor.build(); - if (!strLog.empty()) + if (!logList.empty()) { - strLog += "չöȡϡ"; - console.log(strLog, 1, printSTNow()); + logList << "չöȡϡ"; + console.log(logList.show(), 1, printSTNow()); } } @@ -169,29 +169,19 @@ void dataBackUp() saveBFile(DiceDir + "\\user\\UserConf.RDconf", UserList); } +bool isIniting{ false }; EVE_Enable(eventEnable) { + if (isIniting || Enabled)return; + isIniting = true; llStartTime = clock(); char path[MAX_PATH]; GetModuleFileNameA(nullptr, path, MAX_PATH); - std::string pathStr(path); - strModulePath = pathStr; - string pathExe = pathStr.substr(pathStr.rfind("\\") + 1); - std::transform(pathStr.begin(), pathStr.end(), pathStr.begin(), [](unsigned char c) { return tolower(c); }); - if (pathExe.substr(0, 4) == "java") - { - frame = QQFrame::Mirai; - Dice_Full_Ver_For = Dice_Full_Ver + " For Mirai]"; - dirExe = pathStr.substr(0, pathStr.find("jre\\bin\\java.exe")); - this_thread::sleep_for(3s); //ȷMirai첽Ϣִ - } - else if (pathExe.substr(0, 4) == "") { - frame = QQFrame::XianQu; - Dice_Full_Ver_For = Dice_Full_Ver + " For CQXQ]"; - dirExe = pathStr.substr(0, pathStr.find_last_of('\\') + 1); - this_thread::sleep_for(3s); - } - if (console.DiceMaid = getLoginQQ()) + strModulePath = string(path); + Dice_Full_Ver_On = Dice_Full_Ver + " on\n" + DD::getDriVer(); + DD::debugLog(Dice_Full_Ver_On); + dirExe = strModulePath.substr(0, strModulePath.rfind("\\") + 1); + if (console.DiceMaid = DD::getLoginQQ()) { DiceDir = dirExe + "Dice" + to_string(console.DiceMaid); filesystem::path pathDir(DiceDir); @@ -202,9 +192,8 @@ EVE_Enable(eventEnable) } } console.setPath(DiceDir + "\\conf\\Console.xml"); - strFileLoc = getAppDirectory(); - mkDir(strFileLoc); // MiraiԶļ - GlobalMsg["strSelfName"] = getLoginNick(); + strFileLoc = DiceDir + "\\com.w4123.dice\\"; + GlobalMsg["strSelfName"] = DD::getLoginNick(); if (GlobalMsg["strSelfName"].empty()) { GlobalMsg["strSelfName"] = "[" + toString(console.DiceMaid % 1000, 4) + "]"; @@ -232,7 +221,7 @@ EVE_Enable(eventEnable) } else { - sendPrivateMsg(console.DiceMaid, msgInit); + DD::sendPrivateMsg(console.DiceMaid, msgInit); } ifstreamMaster.close(); std::map boolConsole; @@ -289,7 +278,10 @@ EVE_Enable(eventEnable) getUser(qq).create(NEWYEAR).trust(4); } if (console.master())getUser(console.master()).create(NEWYEAR).trust(5); - if (UserList.size())console.log("ʼû¼" + to_string(UserList.size()) + "", 1); + if (UserList.size()){ + console.log("ʼû¼" + to_string(UserList.size()) + "", 1); + saveFile(DiceDir + "\\user\\UserList.txt", UserList); + } } if (loadBFile(DiceDir + "\\user\\ChatConf.RDconf", ChatList) < 1) { @@ -377,15 +369,10 @@ EVE_Enable(eventEnable) chat(g).group().set("ʹ").set(""); } } + saveBFile(DiceDir + "\\user\\ChatConf.RDconf", ChatList); } if (loadFile(DiceDir + "\\user\\ChatList.txt", ChatList) < 1) { - map mLastMsgList; - for (const auto& it : mLastMsgList) - { - if (it.first.second == msgtype::Private)getUser(it.first.first).create(it.second); - else chat(it.first.first).create(it.second).lastmsg(it.second).isGroup = 2 - int(it.first.second); - } std::map mGroupInviter; if (loadFile(strFileLoc + "GroupInviter.RDconf", mGroupInviter) < 1) { @@ -394,11 +381,14 @@ EVE_Enable(eventEnable) chat(it.first).group().inviter = it.second; } } - if(ChatList.size())console.log("ʼȺ¼" + to_string(ChatList.size()) + "", 1); + if(ChatList.size()){ + console.log("ʼȺ¼" + to_string(ChatList.size()) + "", 1); + saveFile(DiceDir + "\\user\\ChatList.txt", ChatList); + } } - for (auto& [gid,gname] : getGroupList()) + for (auto gid : DD::getGroupIDList()) { - chat(gid).group().name(gname).reset(""); + chat(gid).group().name(DD::getGroupName(gid)).reset("δ").reset(""); } blacklist = make_unique(); if (blacklist->loadJson(DiceDir + "\\conf\\BlackList.json") < 0) @@ -423,6 +413,7 @@ EVE_Enable(eventEnable) GlobalMsg["strSelfName"]); GlobalMsg[it.first] = it.second; } + DD::debugLog("Dice.loadData"); loadData(); if (loadBFile(DiceDir + "\\user\\PlayerCards.RDconf", PList) < 1) { @@ -461,7 +452,7 @@ EVE_Enable(eventEnable) getDiceList(); getExceptGroup(); llStartTime = clock(); - return 0; + isIniting = false; } mutex GroupAddMutex; @@ -472,26 +463,21 @@ bool eve_GroupAdd(Chat& grp) unique_lock lock_queue(GroupAddMutex); if (grp.lastmsg(time(nullptr)).isset("δ") || grp.isset(""))grp.reset("δ").reset(""); else return false; - if (ChatList.size() == 1 && !console.isMasterMode)sendGroupMsg(grp.ID, msgInit); - } - GroupInfo ginf(grp.ID); - //ȺϢǷȡɹ - if (ginf.llGroup) { - grp.Name = ginf.strGroupName; - } - else { - ginf.llGroup = grp.ID; + if (ChatList.size() == 1 && !console.isMasterMode)DD::sendGroupMsg(grp.ID, msgInit); } - if (grp.boolConf.empty() && ginf.nGroupSize > 499) { + long long fromGroup = grp.ID; + if (grp.Name.empty()) + grp.Name = DD::getGroupName(fromGroup); + Size gsize(DD::getGroupSize(fromGroup)); + if (grp.boolConf.empty() && gsize.siz > 499) { grp.set("ЭЧ"); } if (!console["ListenGroupAdd"] || grp.isset(""))return 0; - long long fromGroup = grp.ID; string strNow = printSTNow(); string strMsg(GlobalMsg["strSelfName"]); try { - strMsg += "¼:" + ginf.tostring(); + strMsg += "¼:" + DD::printGroupInfo(grp.ID); if (blacklist->get_group_danger(fromGroup)) { grp.leave(blacklist->list_group_warning(fromGroup)); @@ -510,7 +496,7 @@ bool eve_GroupAdd(Chat& grp) //int max_danger = 0; long long ownerQQ = 0; ResList blacks; - std::vector list = getGroupMemberList(fromGroup); + std::set list = DD::getGroupMemberList(fromGroup); if (list.empty()) { strMsg += "ȺԱδأ"; @@ -520,50 +506,50 @@ bool eve_GroupAdd(Chat& grp) int cntUser(0), cntMember(0); for (auto& each : list) { - if (each.QQID == console.DiceMaid)continue; + if (each == console.DiceMaid)continue; cntMember++; - if (UserList.count(each.QQID)) + if (UserList.count(each)) { cntUser++; - ave_trust += getUser(each.QQID).nTrust; + ave_trust += getUser(each).nTrust; } - if (each.permissions > 1) + if (DD::isGroupAdmin(fromGroup, each, false)) { - max_trust |= (1 << trustedQQ(each.QQID)); - if (blacklist->get_qq_danger(each.QQID) > 1) + max_trust |= (1 << trustedQQ(each)); + if (blacklist->get_qq_danger(each) > 1) { - strMsg += ",ֺԱ" + printQQ(each.QQID); + strMsg += ",ֺԱ" + printQQ(each); if (grp.isset("")) { strMsg += "Ⱥڣ"; } else { - sendGroupMsg(fromGroup, blacklist->list_qq_warning(each.QQID)); - grp.leave("ֺԱ" + printQQ(each.QQID) + "ԤȺ"); + DD::sendGroupMsg(fromGroup, blacklist->list_qq_warning(each)); + grp.leave("ֺԱ" + printQQ(each) + "ԤȺ"); strMsg += "Ⱥ"; console.log(strMsg, 0b10, strNow); return true; } } - if (each.permissions == 3) + if (DD::isGroupOwner(fromGroup, each, false)) { - ownerQQ = each.QQID; - ave_trust += ginf.nGroupSize * trustedQQ(each.QQID); - strMsg += "Ⱥ" + printQQ(each.QQID) + ""; + ownerQQ = each; + ave_trust += (gsize.siz - 1) * trustedQQ(each); + strMsg += "Ⱥ" + printQQ(each) + ""; } else { - ave_trust += ginf.nGroupSize * trustedQQ(each.QQID) / 10; + ave_trust += (gsize.siz - 10) * trustedQQ(each) / 10; } } - else if (blacklist->get_qq_danger(each.QQID) > 1) + else if (blacklist->get_qq_danger(each) > 1) { //max_trust |= 1; - blacks << printQQ(each.QQID); - if (blacklist->get_qq_danger(each.QQID)) + blacks << printQQ(each); + if (blacklist->get_qq_danger(each)) { - AddMsgToQueue(blacklist->list_self_qq_warning(each.QQID), fromGroup, msgtype::Group); + AddMsgToQueue(blacklist->list_self_qq_warning(each), fromGroup, msgtype::Group); } } } @@ -628,120 +614,105 @@ bool eve_GroupAdd(Chat& grp) //ָ -EVE_PrivateMsg_EX(eventPrivateMsg) +EVE_PrivateMsg(eventPrivateMsg) { - if (!Enabled)return; - shared_ptr Msg(make_shared(eve.message, eve.fromQQ)); - if (Msg->DiceFilter())eve.message_block(); + if (!Enabled)return 0; + shared_ptr Msg(make_shared(message, fromQQ)); + return Msg->DiceFilter(); } -EVE_GroupMsg_EX(eventGroupMsg) +EVE_GroupMsg(eventGroupMsg) { - if (!Enabled)return; - if (eve.isAnonymous())return; - if (eve.isSystem())return; - Chat& grp = chat(eve.fromGroup).group().lastmsg(time(nullptr)); + if (!Enabled)return 0; + Chat& grp = chat(fromGroup).group().lastmsg(time(nullptr)); + if (fromQQ == console.DiceMaid && !console["ListenGroupEcho"])return 0; if (grp.isset("δ") || grp.isset(""))eve_GroupAdd(grp); if (!grp.isset("")) { - shared_ptr Msg(make_shared(eve.message, eve.fromGroup, msgtype::Group, eve.fromQQ)); - if (Msg->DiceFilter())eve.message_block(); + shared_ptr Msg(make_shared(message, fromGroup, msgtype::Group, fromQQ)); + return Msg->DiceFilter(); } - if (grp.isset("Ϣ"))eve.message_block(); + return grp.isset("Ϣ"); } -EVE_DiscussMsg_EX(eventDiscussMsg) +EVE_DiscussMsg(eventDiscussMsg) { - if (!Enabled)return; + if (!Enabled)return 0; //time_t tNow = time(NULL); if (console["LeaveDiscuss"]) { - sendDiscussMsg(eve.fromDiscuss, getMsg("strLeaveDiscuss")); + DD::sendDiscussMsg(fromDiscuss, getMsg("strLeaveDiscuss")); Sleep(1000); - setDiscussLeave(eve.fromDiscuss); - return; + DD::setDiscussLeave(fromDiscuss); + return 1; } - Chat& grp = chat(eve.fromDiscuss).discuss().lastmsg(time(nullptr)); - if (blacklist->get_qq_danger(eve.fromQQ) && console["AutoClearBlack"]) + Chat& grp = chat(fromDiscuss).discuss().lastmsg(time(nullptr)); + if (blacklist->get_qq_danger(fromQQ) && console["AutoClearBlack"]) { - const string strMsg = "ֺû" + printQQ(eve.fromQQ) + "ԶִȺ"; - console.log(printChat({eve.fromDiscuss, msgtype::Discuss}) + strMsg, 0b10, printSTNow()); + const string strMsg = "ֺû" + printQQ(fromQQ) + "ԶִȺ"; + console.log(printChat({fromDiscuss, msgtype::Discuss}) + strMsg, 0b10, printSTNow()); grp.leave(strMsg); - return; + return 1; } - shared_ptr Msg(make_shared(eve.message, eve.fromDiscuss, msgtype::Discuss, eve.fromQQ)); - if (Msg->DiceFilter() || grp.isset("Ϣ"))eve.message_block(); + shared_ptr Msg(make_shared(message, fromDiscuss, msgtype::Discuss, fromQQ)); + return Msg->DiceFilter() || grp.isset("Ϣ"); } -EVE_System_GroupMemberIncrease(eventGroupMemberIncrease) +EVE_GroupMemberIncrease(eventGroupMemberAdd) { Chat& grp = chat(fromGroup); if (grp.isset(""))return 0; - if (beingOperateQQ != console.DiceMaid) + if (fromQQ != console.DiceMaid) { if (chat(fromGroup).strConf.count("Ⱥӭ")) { string strReply = chat(fromGroup).strConf["Ⱥӭ"]; while (strReply.find("{at}") != string::npos) { - strReply.replace(strReply.find("{at}"), 4, "[CQ:at,qq=" + to_string(beingOperateQQ) + "]"); + strReply.replace(strReply.find("{at}"), 4, "[CQ:at,qq=" + to_string(fromQQ) + "]"); } while (strReply.find("{@}") != string::npos) { - strReply.replace(strReply.find("{@}"), 3, "[CQ:at,qq=" + to_string(beingOperateQQ) + "]"); + strReply.replace(strReply.find("{@}"), 3, "[CQ:at,qq=" + to_string(fromQQ) + "]"); } while (strReply.find("{nick}") != string::npos) { - strReply.replace(strReply.find("{nick}"), 6, getStrangerInfo(beingOperateQQ).nick); - } - while (strReply.find("{age}") != string::npos) - { - strReply.replace(strReply.find("{age}"), 5, to_string(getStrangerInfo(beingOperateQQ).age)); - } - while (strReply.find("{sex}") != string::npos) - { - strReply.replace(strReply.find("{sex}"), 5, - getStrangerInfo(beingOperateQQ).sex == 0 - ? "" - : getStrangerInfo(beingOperateQQ).sex == 1 - ? "Ů" - : "δ֪"); + strReply.replace(strReply.find("{nick}"), 6, DD::getQQNick(fromQQ)); } while (strReply.find("{qq}") != string::npos) { - strReply.replace(strReply.find("{qq}"), 4, to_string(beingOperateQQ)); + strReply.replace(strReply.find("{qq}"), 4, to_string(fromQQ)); } grp.update(time(nullptr)); AddMsgToQueue(strReply, fromGroup, msgtype::Group); } - if (blacklist->get_qq_danger(beingOperateQQ)) + if (blacklist->get_qq_danger(fromQQ)) { const string strNow = printSTNow(); string strNote = printGroup(fromGroup) + "" + GlobalMsg["strSelfName"] + "ĺû" + printQQ( - beingOperateQQ) + "Ⱥ"; - AddMsgToQueue(blacklist->list_self_qq_warning(beingOperateQQ), fromGroup, msgtype::Group); + fromQQ) + "Ⱥ"; + AddMsgToQueue(blacklist->list_self_qq_warning(fromQQ), fromGroup, msgtype::Group); if (grp.isset(""))strNote += "Ⱥ壩"; else if (grp.isset(""))strNote += "Ⱥڣ"; else if (grp.isset("ЭЧ"))strNote += "ȺЭЧ"; - else if (getGroupMemberInfo(fromGroup, console.DiceMaid).permissions > 1)strNote += "ȺȨޣ"; + else if (DD::isGroupAdmin(fromGroup, console.DiceMaid, false))strNote += "ȺȨޣ"; else if (console["LeaveBlackQQ"]) { strNote += "Ⱥ"; - grp.leave("ֺû" + printQQ(beingOperateQQ) + "Ⱥ,ԤȺ"); + grp.leave("ֺû" + printQQ(fromQQ) + "Ⱥ,ԤȺ"); } console.log(strNote, 0b10, strNow); } } - else - { + else{ + if (!grp.inviter)grp.inviter = operatorQQ; if (!grp.tLastMsg)grp.set("δ"); return eve_GroupAdd(grp); } return 0; } -EVE_System_GroupMemberDecrease(eventGroupMemberDecrease) -{ +EVE_GroupMemberKicked(eventGroupMemberKicked){ if (fromQQ == 0)return 0; // MiraiڻȺʱҲһ Chat& grp = chat(fromGroup); if (beingOperateQQ == console.DiceMaid) @@ -762,7 +733,7 @@ EVE_System_GroupMemberDecrease(eventGroupMemberDecrease) grp.reset("ʹ").reset(""); blacklist->create(mark.product()); } - else if (mDiceList.count(beingOperateQQ) && subType == 2 && console["ListenGroupKick"]) + else if (mDiceList.count(beingOperateQQ) && console["ListenGroupKick"]) { if (!console || grp.isset(""))return 0; string strNow = printSTime(stNow); @@ -777,12 +748,12 @@ EVE_System_GroupMemberDecrease(eventGroupMemberDecrease) return 0; } -EVE_System_GroupBan(eventGroupBan) +EVE_GroupBan(eventGroupBan) { Chat& grp = chat(fromGroup); if (grp.isset("") || (beingOperateQQ != console.DiceMaid && !mDiceList.count(beingOperateQQ)) || !console[ "ListenGroupBan"])return 0; - if (subType == 1) + if (!duration || !duration[0]) { if (beingOperateQQ == console.DiceMaid) { @@ -794,15 +765,15 @@ EVE_System_GroupBan(eventGroupBan) { string strNow = printSTNow(); long long llOwner = 0; - string strNote = "" + printGroup(fromGroup) + "," + printQQ(beingOperateQQ) + "" + printQQ(fromQQ) + "" + printDuringTime(duration); - if (!console["ListenGroupBan"] || trustedQQ(fromQQ) > 1 || grp.isset("") || grp.isset("ЭЧ") || ExceptGroups.count(fromGroup)) + string strNote = "" + printGroup(fromGroup) + "," + printQQ(beingOperateQQ) + "" + printQQ(operatorQQ) + "" + duration; + if (!console["ListenGroupBan"] || trustedQQ(operatorQQ) > 1 || grp.isset("") || grp.isset("ЭЧ") || ExceptGroups.count(fromGroup)) { console.log(strNote, 0b10, strNow); return 1; } - DDBlackMarkFactory mark{fromQQ, fromGroup}; + DDBlackMarkFactory mark{operatorQQ, fromGroup}; mark.type("ban").time(strNow).note(strNow + " " + strNote); - if (mDiceList.count(fromQQ))mark.fromQQ(0); + if (mDiceList.count(operatorQQ))mark.fromQQ(0); if (beingOperateQQ == console.DiceMaid) { if (!console)return 0; @@ -815,16 +786,14 @@ EVE_System_GroupBan(eventGroupBan) //ͳȺڹ int intAuthCnt = 0; string strAuthList; - vector list = getGroupMemberList(fromGroup); - for (auto& member : list) + for (auto admin : DD::getGroupAdminList(fromGroup)) { - if (member.permissions == 3) - { - llOwner = member.QQID; + if (DD::isGroupOwner(fromGroup, admin, false)) { + llOwner = admin; } - else if (member.permissions == 2) + else { - strAuthList += '\n' + member.Nick + "(" + to_string(member.QQID) + ")"; + strAuthList += '\n' + printQQ(admin); intAuthCnt++; } } @@ -844,48 +813,59 @@ EVE_System_GroupBan(eventGroupBan) return 0; } -EVE_Request_AddGroup(eventGroupInvited) +EVE_GroupInvited(eventGroupInvited) { if (!console["ListenGroupRequest"])return 0; - if (subType == 2 && groupset(fromGroup, "") < 1) + if (groupset(fromGroup, "") < 1) { this_thread::sleep_for(3s); const string strNow = printSTNow(); string strMsg = "Ⱥԣ" + printQQ(fromQQ) + ",Ⱥ:" + to_string(fromGroup) + ""; - if (blacklist->get_group_danger(fromGroup)) + if (ExceptGroups.count(fromGroup)) { + strMsg += "\nѺԣĬЭЧ"; + console.log(strMsg, 0b10, strNow); + DD::answerGroupInvited(fromGroup, 3); + } + else if (blacklist->get_group_danger(fromGroup)) { strMsg += "\nѾܾȺںУ"; console.log(strMsg, 0b10, strNow); - setGroupAddRequest(responseFlag, 2, 2, ""); + DD::answerGroupInvited(fromGroup, 2); } else if (blacklist->get_qq_danger(fromQQ)) { strMsg += "\nѾܾûںУ"; console.log(strMsg, 0b10, strNow); - setGroupAddRequest(responseFlag, 2, 2, ""); + DD::answerGroupInvited(fromGroup, 2); } else if (Chat& grp = chat(fromGroup).group(); grp.isset("ʹ")) { grp.set("δ"); grp.inviter = fromQQ; strMsg += "\nͬ⣨Ⱥʹã"; console.log(strMsg, 1, strNow); - setGroupAddRequest(responseFlag, 2, 1, ""); + DD::answerGroupInvited(fromGroup, 1); } else if (trustedQQ(fromQQ)) { - grp.set("ʹ").set("δ"); + grp.set("ʹ").set("δ").reset("δ").reset("ЭЧ"); grp.inviter = fromQQ; strMsg += "\nͬ⣨û"; console.log(strMsg, 1, strNow); - setGroupAddRequest(responseFlag, 2, 1, ""); + DD::answerGroupInvited(fromGroup, 1); + } + else if (grp.isset("ЭЧ")) { + grp.set("δ"); + strMsg += "\nѺԣЭЧ"; + console.log(strMsg, 0b10, strNow); + DD::answerGroupInvited(fromGroup, 3); } else if (console && console["Private"]) { - sendPrivateMsg(fromQQ, getMsg("strPreserve")); + DD::sendPrivateMsg(fromQQ, getMsg("strPreserve")); strMsg += "\nѾܾǰ˽ģʽ"; console.log(strMsg, 1, strNow); - setGroupAddRequest(responseFlag, 2, 2, ""); + DD::answerGroupInvited(fromGroup, 2); } else { @@ -894,12 +874,51 @@ EVE_Request_AddGroup(eventGroupInvited) strMsg += "ͬ"; this_thread::sleep_for(2s); console.log(strMsg, 1, strNow); - setGroupAddRequest(responseFlag, 2, 1, ""); + DD::answerGroupInvited(fromGroup, true); } return 1; } return 0; } +EVE_FriendRequest(eventFriendRequest) { + if (!console["ListenFriendRequest"])return 0; + string strMsg = " " + printQQ(fromQQ) + ":" + message; + this_thread::sleep_for(3s); + if (blacklist->get_qq_danger(fromQQ)) { + strMsg += "\nѾܾûںУ"; + DD::answerFriendRequest(fromQQ, 2, ""); + console.log(strMsg, 0b10, printSTNow()); + } + else if (trustedQQ(fromQQ)) { + strMsg += "\nͬ⣨û"; + DD::answerFriendRequest(fromQQ, 1, getMsg("strAddFriendWhiteQQ")); + console.log(strMsg, 1, printSTNow()); + } + else if (console["AllowStranger"] < 2 && !UserList.count(fromQQ)) { + strMsg += "\nѾܾû¼"; + DD::answerFriendRequest(fromQQ, 2, getMsg("strFriendDenyNotUser")); + console.log(strMsg, 1, printSTNow()); + } + else if (console["AllowStranger"] < 1) { + strMsg += "\nѾܾû"; + DD::answerFriendRequest(fromQQ, 2, getMsg("strFriendDenyNoTrust")); + console.log(strMsg, 1, printSTNow()); + } + else { + strMsg += "\nͬ"; + DD::answerFriendRequest(fromQQ, 1, getMsg("strAddFriend")); + console.log(strMsg, 1, printSTNow()); + } + return 1; +} +EVE_FriendAdded(eventFriendAdd) { + if (!console["ListenFriendAdd"])return 0; + this_thread::sleep_for(3s); + GlobalMsg["strAddFriendWhiteQQ"].empty() + ? AddMsgToQueue(getMsg("strAddFriend"), fromQQ) + : AddMsgToQueue(getMsg("strAddFriendWhiteQQ"), fromQQ); + return 0; +} EVE_Menu(eventMasterMode) { @@ -925,8 +944,6 @@ EVE_Menu(eventGUI) void global_exit() { Enabled = false; - threads.exit(); - threads = {}; dataBackUp(); sch.end(); censor = {}; @@ -939,20 +956,28 @@ void global_exit() { EditedMsg.clear(); blacklist.reset(); Aws::ShutdownAPI(options); + threads.exit(); } EVE_Disable(eventDisable) { global_exit(); - return 0; } EVE_Exit(eventExit) { - if (!Enabled) - return 0; - global_exit(); - return 0; + if (Enabled)global_exit(); } -MUST_AppInfo_RETURN(CQAPPID); +EVE_Menu(eventGlobalSwitch) { + if (console["DisabledGlobal"]) { + console.set("DisabledGlobal", 0); + MessageBoxA(nullptr, "ѽĬ", "ȫֿ", MB_OK | MB_ICONINFORMATION); + } + else { + console.set("DisabledGlobal", 1); + MessageBoxA(nullptr, "ȫ־Ĭ", "ȫֿ", MB_OK | MB_ICONINFORMATION); + } + + return 0; +} \ No newline at end of file diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj index 1a68a8ba..338c4b14 100644 --- a/Dice/Dice.vcxproj +++ b/Dice/Dice.vcxproj @@ -55,6 +55,8 @@ false static + $(SolutionDir)QQAPI;$(IncludePath) + w4123.Dice true @@ -63,7 +65,8 @@ static false static - app + w4123.Dice + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)Lua;$(SolutionDir)QQAPI @@ -77,14 +80,14 @@ true SyncCThrow true - MultiThreaded + MultiThreadedDebugDLL AnySuitable Speed true true true Precise - ..\CQSDK;%(AdditionalIncludeDirectories) + ..\CQSDK;..\Lua;%(AdditionalIncludeDirectories) true stdcpp17 @@ -127,6 +130,9 @@ true ..\packages\AWSSDKCPP-Core.1.6.25\build\native\lib\Win32\Debug\v141\static;..\packages\AWSSDKCPP-S3.1.6.20060301.25\build\native\lib\Win32\Debug\v141\static;%(AdditionalLibraryDirectories) aws-cpp-sdk-core.lib;aws-cpp-sdk-s3.lib;%(AdditionalDependencies) + 4194304 + + @@ -137,12 +143,43 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -154,6 +191,7 @@ + @@ -174,26 +212,13 @@ - - - - - - - - - - - - - - - - - - + + + + + @@ -205,6 +230,7 @@ + @@ -234,9 +260,6 @@ - - - diff --git a/Dice/Dice.vcxproj.filters b/Dice/Dice.vcxproj.filters index 75088b7c..41b2c96c 100644 --- a/Dice/Dice.vcxproj.filters +++ b/Dice/Dice.vcxproj.filters @@ -22,6 +22,18 @@ {911e9211-0dd0-42ff-82bc-c6ae90928af6} + + {f5bad68a-94f2-4074-9c0f-07a82e89f4c1} + + + {fad2c6f3-9fa5-4929-bda1-abc1ebf0f39a} + + + {b57b7ace-ef77-42c8-b7a6-683797f35a8a} + + + {0cd77685-d94c-4a78-a991-b73ea3153780} + @@ -36,18 +48,6 @@ 源文件 - - 源文件\CQCPP - - - 源文件\CQCPP - - - 源文件\CQCPP - - - 源文件\CQCPP - 源文件\CQCPP @@ -126,6 +126,114 @@ 源文件 + + 源文件 + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\Lua + + + 源文件\QQAPI + + + 源文件\QQAPI + @@ -143,60 +251,6 @@ 头文件\CQHead - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - 头文件\CQHead @@ -293,6 +347,24 @@ 头文件 + + 头文件 + + + 头文件\Lua + + + 头文件\Lua + + + 头文件\Lua + + + 头文件\DDAPI + + + 头文件\DDAPI + @@ -301,11 +373,6 @@ 资源文件 - - - 资源文件 - - 资源文件 diff --git a/Dice/DiceCloud.cpp b/Dice/DiceCloud.cpp index d7654ab4..a8f62a0f 100644 --- a/Dice/DiceCloud.cpp +++ b/Dice/DiceCloud.cpp @@ -10,11 +10,11 @@ #include "DiceCloud.h" #include "GlobalVar.h" #include "EncodingConvert.h" -#include "CQAPI_EX.h" #include "DiceNetwork.h" #include "DiceConsole.h" #include "DiceMsgSend.h" #include "DiceEvent.h" +#include "DDAPI.h" #pragma comment(lib, "urlmon.lib") @@ -23,18 +23,13 @@ using namespace nlohmann; namespace Cloud { - void update() + void heartbeat() { const string strVer = GBKtoUTF8(string(Dice_Ver)); - const string data = "DiceQQ=" + to_string(console.DiceMaid) + "&masterQQ=" + to_string(console.master()) + "&Ver=" + + const string data = "&masterQQ=" + to_string(console.master()) + "&Ver=" + strVer + "&isGlobalOn=" + to_string(!console["DisabledGlobal"]) + "&isPublic=" + to_string(!console["Private"]) + "&isVisible=" + to_string(console["CloudVisible"]); - char* frmdata = new char[data.length() + 1]; - strcpy_s(frmdata, data.length() + 1, data.c_str()); - string temp; - Network::POST("shiki.stringempty.xyz", "/DiceCloud/update.php", 80, frmdata, temp); - //AddMsgToQueue(temp, masterQQ); - delete[] frmdata; + DD::heartbeat(data); } int checkWarning(const char* warning) diff --git a/Dice/DiceCloud.h b/Dice/DiceCloud.h index 75f0e877..a30da1a0 100644 --- a/Dice/DiceCloud.h +++ b/Dice/DiceCloud.h @@ -8,7 +8,7 @@ class FromMsg; namespace Cloud { - void update(); + void heartbeat(); int checkWarning(const char* warning); int DownloadFile(const char* url, const char* downloadPath); int checkUpdate(FromMsg* msg); diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 10ab443f..3968e672 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -33,9 +33,9 @@ #include "Jsonio.h" #include "BlackListManager.h" #include "DiceSchedule.h" +#include "DDAPI.h" using namespace std; -using namespace CQ; const std::mapConsole::intDefault{ {"DisabledGlobal",0},{"DisabledBlock",0},{"DisabledListenAt",1}, @@ -51,7 +51,8 @@ const std::mapConsole::intDefault{ {"SystemAlarmCPU",90},{"SystemAlarmRAM",90},{"SystemAlarmDisk",90}, {"SendIntervalIdle",500},{"SendIntervalBusy",100}, //Զ¼[min],ԶͼƬ[h],Զܼ[h] -{"AutoSaveInterval",10},{"AutoClearImage",0},{"AutoFrameRemake",0} +{"AutoSaveInterval",10},{"AutoClearImage",0},{"AutoFrameRemake",0}, +{"ListenGroupEcho",0} }; const enumap Console::mClockEvent{"off", "on", "save", "clear"}; @@ -158,17 +159,18 @@ int Console::log(const std::string& strMsg, int note_lv, const string& strTime) AddMsgToQueue(note, ct); Cnt++; } - if (!Cnt)sendPrivateMsg(DiceMaid, note); + if (!Cnt)DD::sendPrivateMsg(DiceMaid, note); } return Cnt; } void Console::newMaster(long long qq) { masterQQ = qq; - getUser(qq).trust(5); - setNotice({qq, CQ::msgtype::Private}, 0b111111); + if (trustedQQ(qq) < 5)getUser(qq).trust(5); + setNotice({qq, msgtype::Private}, 0b111111); save(); - AddMsgToQueue(getMsg("strNewMaster"), qq); + AddMsgToQueue(getMsg("strNewMaster"), qq); + AddMsgToQueue(intConf["Private"] ? getMsg("strNewMasterPrivate") : getMsg("strNewMasterPublic"), qq); } void Console::reset() @@ -183,7 +185,7 @@ void Console::loadNotice() if (loadFile(DiceDir + "\\conf\\NoticeList.txt", NoticeList) < 1) { std::set sChat; - if (loadFile(std::string(getAppDirectory()) + "MonitorList.RDconf", sChat) > 0) + if (loadFile(DiceDir + "com.w4123.dice\\MonitorList.RDconf", sChat) > 0) for (const auto& it : sChat) { console.setNotice(it, 0b100000); @@ -271,17 +273,16 @@ std::string printSTime(const SYSTEMTIME st) //ӡûdzQQ string printQQ(long long llqq) { - string nick = getStrangerInfo(llqq).nick; - if (nick.empty())nick = getFriendList()[llqq].nick; - if(nick.empty())return "û(" + to_string(llqq) + ")"; + string nick = DD::getQQNick(llqq); + if (nick.empty())return getMsg("stranger") + "[" + to_string(llqq) + "]"; return nick + "(" + to_string(llqq) + ")"; } //ӡQQȺ string printGroup(long long llgroup) { - if (!llgroup)return"˽"; + if (!llgroup)return "˽"; if (ChatList.count(llgroup))return printChat(ChatList[llgroup]); - if (getGroupList().count(llgroup))return "[" + getGroupList()[llgroup] + "](" + to_string(llgroup) + ")"; + if (string name{ DD::getGroupName(llgroup) };!name.empty())return "[" + name + "](" + to_string(llgroup) + ")"; return "Ⱥ(" + to_string(llgroup) + ")"; } //ӡ촰 @@ -370,69 +371,12 @@ bool operator<(const Console::Clock clock, const SYSTEMTIME& st) } -EVE_Menu(eventGlobalSwitch) -{ - if (console["DisabledGlobal"]) - { - console.set("DisabledGlobal", 0); - MessageBoxA(nullptr, "ѽĬ", "ȫֿ", MB_OK | MB_ICONINFORMATION); - } - else - { - console.set("DisabledGlobal", 1); - MessageBoxA(nullptr, "ȫ־Ĭ", "ȫֿ", MB_OK | MB_ICONINFORMATION); - } - - return 0; -} - -EVE_Request_AddFriend(eventAddFriend) -{ - if (!console["ListenFriendRequest"])return 0; - string strMsg = " " + printQQ(fromQQ)+ ":"; - if (msg && msg[0] != '\0') strMsg += msg; - this_thread::sleep_for(3s); - if (blacklist->get_qq_danger(fromQQ)) - { - strMsg += "\nѾܾûںУ"; - setFriendAddRequest(responseFlag, 2, ""); - console.log(strMsg, 0b10, printSTNow()); - } - else if (trustedQQ(fromQQ)) - { - strMsg += "\nͬ⣨û"; - setFriendAddRequest(responseFlag, 1, ""); - AddMsgToQueue(getMsg("strAddFriendWhiteQQ"), fromQQ); - console.log(strMsg, 1, printSTNow()); - } - else if (console["AllowStranger"] < 2 && !UserList.count(fromQQ)) - { - strMsg += "\nѾܾû¼"; - setFriendAddRequest(responseFlag, 2, getMsg("strFriendDenyNotUser").c_str()); - console.log(strMsg, 1, printSTNow()); - } - else if (console["AllowStranger"] < 1) - { - strMsg += "\nѾܾû"; - setFriendAddRequest(responseFlag, 2, getMsg("strFriendDenyNoTrust").c_str()); - console.log(strMsg, 1, printSTNow()); - } - else - { - strMsg += "\nͬ"; - setFriendAddRequest(responseFlag, 1, ""); - AddMsgToQueue(getMsg("strAddFriend"), fromQQ); - console.log(strMsg, 1, printSTNow()); + void ThreadFactory::exit() { + rear = 0; + for (auto& th : vTh) { + if (th.joinable())th.join(); + } + vTh = {}; + DD::debugLog("Dice߳"); } - return 1; -} -EVE_Friend_Add(eventFriendAdd) -{ - if (!console["ListenFriendAdd"])return 0; - this_thread::sleep_for(3s); - GlobalMsg["strAddFriendWhiteQQ"].empty() - ? AddMsgToQueue(getMsg("strAddFriend"), fromQQ) - : AddMsgToQueue(getMsg("strAddFriendWhiteQQ"), fromQQ); - return 0; -} diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index dd9fffe3..f5ffb263 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -18,7 +18,6 @@ #include "DiceFile.hpp" #include "MsgFormat.h" #include "DiceMsgSend.h" -#include "CQEVE_ALL.h" using namespace std::literals::chrono_literals; using std::string; using std::to_string; @@ -69,7 +68,7 @@ class Console void killMaster() { - rmNotice({masterQQ, CQ::msgtype::Private}); + rmNotice({masterQQ, msgtype::Private}); masterQQ = 0; save(); } @@ -205,18 +204,15 @@ class ThreadFactory int rear = 0; std::array vTh; - void operator()(void (*func)()) - { - std::thread th(func); + void operator()(void (*func)()) { + std::thread th{ [func]() {try { func(); } catch (...) { return; }} }; vTh[rear] = std::move(th); + //vTh[rear].detach(); rear++; } - void exit() { - for (auto& th : vTh) { - th.join(); - } - vTh = {}; - rear = 0; + void exit(); + ~ThreadFactory() { + exit(); } }; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 8ac9f90f..c0dd15d3 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1,4 +1,5 @@ #include +#include "DDAPI.h" #include "DiceEvent.h" #include "Jsonio.h" #include "MsgFormat.h" @@ -9,13 +10,11 @@ #include "CharacterCard.h" #include "DiceSession.h" #include "GetRule.h" -#include "CQAPI.h" #include "DiceNetwork.h" #include "DiceCloud.h" +#include "DiceGUI.h" #include using namespace std; -using namespace CQ; - FromMsg& FromMsg::initVar(const std::initializer_list& replace_str) { int index = 0; @@ -25,6 +24,9 @@ FromMsg& FromMsg::initVar(const std::initializer_list& replac return *this; } void FromMsg::formatReply() { + if (!strVar.count("nick") || strVar["nick"].empty())strVar["nick"] = getName(fromQQ, fromGroup); + if (!strVar.count("pc") || strVar["pc"].empty())getPCName(*this); + if (!strVar.count("at") || strVar["nick"].empty())strVar["at"] = fromChat.second != msgtype::Private ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; strReply = format(strReply, GlobalMsg, strVar); } @@ -125,9 +127,9 @@ int FromMsg::AdminEvent(const string& strOption) if (console["DisabledDraw"])res << "ȫֽ.draw"; if (console["DisabledSend"])res << "ȫֽ.send"; if (trusted > 3) - res << "Ⱥ" + to_string(getGroupList().size()) + res << "Ⱥ" + to_string(DD::getGroupIDList().size()) << "Ⱥ¼" + to_string(ChatList.size()) - << "" + to_string(getFriendList().size()) + << "" + to_string(DD::getFriendQQList().size()) << "û¼" + to_string(UserList.size()) << "û" + to_string(today->cnt()) << (!PList.empty() ? "ɫ¼" + to_string(PList.size()) : "") @@ -484,13 +486,9 @@ int FromMsg::AdminEvent(const string& strOption) if (strOption == "blackfriend") { ResList res; - Unpack pack(base64_decode(CQ_getFriendList(getAuthCode()))); //ȡԭʼתΪUnpack - int Cnt = pack.getInt(); //ȡ - while (Cnt--) - { - FriendInfo info(pack.getUnpack()); //ȡ - if (blacklist->get_qq_danger(info.QQID)) - res << info.tostring(); + for(long long qq: DD::getFriendQQList()){ + if (blacklist->get_qq_danger(qq)) + res << printQQ(qq); } if (res.empty()) { @@ -589,7 +587,7 @@ int FromMsg::AdminEvent(const string& strOption) } else if (strOption == "boton") { - if (getGroupList().count(llTargetID)) + if (ChatList.count(llTargetID)) { if (groupset(llTargetID, "ָͣ") > 0) { @@ -819,17 +817,17 @@ int FromMsg::MasterSet() return AdminEvent(strOption); } -int FromMsg::DiceReply() +int FromMsg::BasicOrder() { if (strMsg[0] != '.')return 0; intMsgCnt++; int intT = static_cast(fromChat.second); while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; - strVar["nick"] = getName(fromQQ, fromGroup); - getPCName(*this); - strVar["at"] = intT ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; - isAuth = trusted > 3 || intT != GroupT || getGroupMemberInfo(fromGroup, fromQQ).permissions != 1 || pGrp->inviter == fromQQ; + //strVar["nick"] = getName(fromQQ, fromGroup); + //getPCName(*this); + //strVar["at"] = intT ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; + isAuth = trusted > 3 || intT != GroupT || DD::isGroupAdmin(fromGroup, fromQQ, true) || pGrp->inviter == fromQQ; //ָƥ if (strLowerMessage.substr(intMsgCnt, 9) == "authorize") { @@ -865,7 +863,7 @@ int FromMsg::DiceReply() } return 1; } - if (strLowerMessage.substr(intMsgCnt, 7) == "dismiss") + else if (strLowerMessage.substr(intMsgCnt, 7) == "dismiss") { intMsgCnt += 7; if (!intT) @@ -891,7 +889,7 @@ int FromMsg::DiceReply() grp.leave(getMsg("strAdminDismiss", strVar)); reply(GlobalMsg["strGroupExit"]); } - else if(getGroupMemberInfo(llGroup, fromQQ).permissions > 1 || (grp.inviter == fromQQ)) + else if(DD::isGroupAdmin(llGroup, fromQQ, true) || (grp.inviter == fromQQ)) { reply(GlobalMsg["strDismiss"]); } @@ -902,7 +900,7 @@ int FromMsg::DiceReply() return 1; } string QQNum = readDigit(); - if (QQNum.empty() || QQNum == to_string(console.DiceMaid) || (QQNum.length() == 4 && stoll(QQNum) == getLoginQQ() % 10000)){ + if (QQNum.empty() || QQNum == to_string(console.DiceMaid) || (QQNum.length() == 4 && stoll(QQNum) == DD::getLoginQQ() % 10000)){ if (trusted > 2) { pGrp->leave(getMsg("strAdminDismiss", strVar)); @@ -914,59 +912,38 @@ int FromMsg::DiceReply() } else { - if (!isCalled && (pGrp->isset("ָͣ") || GroupInfo(fromGroup).nGroupSize > 200))AddMsgToQueue(getMsg("strPermissionDeniedErr", strVar), fromQQ); + if (!isCalled && (pGrp->isset("ָͣ") || DD::getGroupSize(fromGroup).siz > 200))AddMsgToQueue(getMsg("strPermissionDeniedErr", strVar), fromQQ); else reply(GlobalMsg["strPermissionDeniedErr"]); } return 1; } return 1; } - if (strLowerMessage.substr(intMsgCnt, 7) == "warning") + else if (strLowerMessage.substr(intMsgCnt, 7) == "warning") { intMsgCnt += 7; string strWarning = readRest(); AddWarning(strWarning, fromQQ, fromGroup); return 1; } - if (strLowerMessage.substr(intMsgCnt, 6) == "master" && console.isMasterMode) + else if (strLowerMessage.substr(intMsgCnt, 6) == "master" && console.isMasterMode) { intMsgCnt += 6; if (!console.master()) { - console.newMaster(fromQQ); - strReply = "Ķǰ汾MasterֲԼûֲᡣ°棬ע汾ŶӦ: https://v2docs.kokona.tech"; - strReply += "\nҪӽ϶ûеȺصIJƼDisabledBlock֤ȺڵľĬ"; - strReply += "\nĬϿȺƳԡˢ¼ļҪرֶ"; - strReply += "\nעƺϵͳĬϿ˹رCloudBlackShare"; string strOption = readRest(); - if (strOption == "public") - { - strReply += "\nģʽ"; + if (strOption == "public"){ console.set("BelieveDiceList", 1); - strReply += "\nԶBelieveDiceListӦбwarning"; console.set("AllowStranger", 1); - strReply += "\nģʽĬͬİ˵ĺ룻"; console.set("LeaveBlackQQ", 1); console.set("BannedLeave", 1); - strReply += "\nѿԶʱÿնʱԶûĹͬȺģûȺȨ޲ԼʱԶȺ"; console.set("BannedBanInviter", 1); console.set("KickedBanInviter", 1); - strReply += "\nȺʱˣֶرգ"; - console.set("DisabledSend", 0); - strReply += "\nsendܣ"; } - else - { + else{ console.set("Private", 1); - strReply += "\nĬϿ˽ģʽ"; - strReply += "\nĬϾܾİ˵ĺ룬Ҫͬ뿪AllowStranger"; - strReply += "\nĬϾܾİ˵Ⱥ룬ֻͬԹԱ룻"; - strReply += "\nѿԶʱÿնʱԶûĹͬȺģûȺȨ޸ԼʱԶȺ"; - strReply += "\n.meĬϲãҪֶ"; - strReply += "\nлʹ.admin publicʼӦã"; - strReply += "\n.master deleteʹ.master public³ʼ"; } - reply(); + console.newMaster(fromQQ); } else if (trusted > 4 || console.master() == fromQQ) { @@ -979,20 +956,19 @@ int FromMsg::DiceReply() } return 1; } - else if (intT != PrivateT && pGrp->isset("ЭЧ")) - { - return 0; + else if (intT != PrivateT && pGrp->isset("ЭЧ")){ + return 1; } if (blacklist->get_qq_danger(fromQQ) || (intT != PrivateT && blacklist->get_group_danger(fromGroup))) { - return 0; + return 1; } if (strLowerMessage.substr(intMsgCnt, 3) == "bot") { intMsgCnt += 3; string Command = readPara(); string QQNum = readDigit(); - if (QQNum.empty() || QQNum == to_string(getLoginQQ()) || (QQNum.length() == 4 && stoll(QQNum) == getLoginQQ() % + if (QQNum.empty() || QQNum == to_string(DD::getLoginQQ()) || (QQNum.length() == 4 && stoll(QQNum) == DD::getLoginQQ() % 10000)) { if (Command == "on") @@ -1015,7 +991,7 @@ int FromMsg::DiceReply() } else { - if (groupset(fromGroup, "ָͣ") > 0 && GroupInfo(fromGroup).nGroupSize > 100)AddMsgToQueue( + if (groupset(fromGroup, "ָͣ") > 0 && DD::getGroupSize(fromGroup).siz > 200)AddMsgToQueue( getMsg("strPermissionDeniedErr", strVar), fromQQ); else reply(GlobalMsg["strPermissionDeniedErr"]); } @@ -1027,7 +1003,7 @@ int FromMsg::DiceReply() { if (groupset(fromGroup, "ָͣ")) { - if (!isCalled && QQNum.empty() && pGrp->isGroup && GroupInfo(fromGroup).nGroupSize > 200)AddMsgToQueue(getMsg("strBotOffAlready", strVar), fromQQ); + if (!isCalled && QQNum.empty() && pGrp->isGroup && DD::getGroupSize(fromGroup).siz > 200)AddMsgToQueue(getMsg("strBotOffAlready", strVar), fromQQ); else reply(GlobalMsg["strBotOffAlready"]); } else @@ -1046,14 +1022,14 @@ int FromMsg::DiceReply() { return 0; } - else if (intT == GroupT && pGrp->isset("ָͣ") && GroupInfo(fromGroup).nGroupSize >= 500 && !isCalled) + else if (intT == GroupT && pGrp->isset("ָͣ") && DD::getGroupSize(fromGroup).siz > 500 && !isCalled) { - AddMsgToQueue(Dice_Full_Ver_For + getMsg("strBotMsg"), fromQQ); + AddMsgToQueue(Dice_Full_Ver_On + getMsg("strBotMsg"), fromQQ); } else { this_thread::sleep_for(1s); - reply(Dice_Full_Ver_For + GlobalMsg["strBotMsg"]); + reply(Dice_Full_Ver_On + GlobalMsg["strBotMsg"]); } } return 1; @@ -1067,39 +1043,6 @@ int FromMsg::DiceReply() } return 0; } - //С4ûдʼ - if (trusted < 4) { - unordered_setsens_words; - switch (int danger = censor.search(strMsg, sens_words) - 1) { - case 3: - if (trusted < danger++) { - console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b1000, - printTTime(fromTime)); - reply(GlobalMsg["strCensorDanger"]); - return 1; - } - case 2: - if (trusted < danger++) { - console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b10, - printTTime(fromTime)); - reply(GlobalMsg["strCensorWarning"]); - break; - } - case 1: - if (trusted < danger++) { - console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b10, - printTTime(fromTime)); - reply(GlobalMsg["strCensorCaution"]); - break; - } - case 0: - console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 1, - printTTime(fromTime)); - break; - default: - break; - } - } if (strLowerMessage.substr(intMsgCnt, 7) == "helpdoc" && trusted > 3) { intMsgCnt += 7; @@ -1178,55 +1121,46 @@ int FromMsg::DiceReply() return 1; } } - if (frame == QQFrame::Mirai) { - std::thread th(&DiceModManager::_help, fmt.get(), shared_from_this()); - th.detach(); - } - else fmt->_help(shared_from_this()); + std::thread th(&DiceModManager::_help, fmt.get(), shared_from_this()); + th.detach(); return true; } - else if (intT == GroupT && ((console["CheckGroupLicense"] && pGrp->isset("δ")) || (console["CheckGroupLicense"] == 2 && - !pGrp->isset("ʹ")))) - { - return 0; + return 0; +} + +int FromMsg::InnerOrder() { + if (strMsg[0] != '.')return 0; + if (strLowerMessage.substr(intMsgCnt, 8) == "setreply") { + return 1; } - if (strLowerMessage.substr(intMsgCnt, 7) == "welcome") - { - if (intT != GroupT) - { + else if (strLowerMessage.substr(intMsgCnt, 7) == "welcome") { + if (fromChat.second != msgtype::Group) { reply(GlobalMsg["strWelcomePrivate"]); return 1; } intMsgCnt += 7; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - if (isAuth) - { + if (isAuth) { string strWelcomeMsg = strMsg.substr(intMsgCnt); - if (strWelcomeMsg.empty()) - { - if (chat(fromGroup).strConf.count("Ⱥӭ")) - { + if (strWelcomeMsg.empty()) { + if (chat(fromGroup).strConf.count("Ⱥӭ")) { chat(fromGroup).rmText("Ⱥӭ"); reply(GlobalMsg["strWelcomeMsgClearNotice"]); } - else - { + else { reply(GlobalMsg["strWelcomeMsgClearErr"]); } } - else if (strWelcomeMsg == "show") - { + else if (strWelcomeMsg == "show") { reply(chat(fromGroup).strConf["Ⱥӭ"]); } - else - { + else { chat(fromGroup).setText("Ⱥӭ", strWelcomeMsg); reply(GlobalMsg["strWelcomeMsgUpdateNotice"]); } } - else - { + else { reply(GlobalMsg["strPermissionDeniedErr"]); } return 1; @@ -1244,29 +1178,24 @@ int FromMsg::DiceReply() sch.push_job(*this); } } - else if (strLowerMessage.substr(intMsgCnt, 6) == "setcoc") - { - if (!isAuth) - { + else if (strLowerMessage.substr(intMsgCnt, 6) == "setcoc") { + if (!isAuth) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } string strRule = readDigit(); - if (strRule.empty()) - { - if (intT)chat(fromGroup).rmConf("rc"); + if (strRule.empty()) { + if (fromChat.second != msgtype::Private)chat(fromGroup).rmConf("rc"); else getUser(fromQQ).rmIntConf("rc"); reply(GlobalMsg["strDefaultCOCClr"]); return 1; } - if (strRule.length() > 1) - { + if (strRule.length() > 1) { reply(GlobalMsg["strDefaultCOCNotFound"]); return 1; } int intRule = stoi(strRule); - switch (intRule) - { + switch (intRule) { case 0: reply(GlobalMsg["strDefaultCOCSet"] + "0 \n1ɹ\n5096-100ʧܣ50100ʧ"); break; @@ -1289,33 +1218,33 @@ int FromMsg::DiceReply() reply(GlobalMsg["strDefaultCOCNotFound"]); return 1; } - if (intT)chat(fromGroup).setConf("rc", intRule); + if (fromChat.second != msgtype::Private)chat(fromGroup).setConf("rc", intRule); else getUser(fromQQ).setConf("rc", intRule); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 6) == "system") - { + else if (strLowerMessage.substr(intMsgCnt, 6) == "system") { intMsgCnt += 6; - if (trusted < 4) - { + if (console && trusted < 4) { reply(GlobalMsg["strNotAdmin"]); return -1; } string strOption = readPara(); - if (strOption == "save") - { + if (strOption == "gui") { + thread th(GUIMain); + th.detach(); + return 1; + } + if (strOption == "save") { dataBackUp(); note("ֶݡ", 0b1); return 1; } - if (strOption == "load") - { + if (strOption == "load") { loadData(); note("ֶݡ", 0b1); return 1; } - if (strOption == "state") - { + if (strOption == "state") { GetLocalTime(&stNow); double mbFreeBytes = 0, mbTotalBytes = 0; long long milDisk(getDiskUsage(mbFreeBytes, mbTotalBytes)); @@ -1330,30 +1259,16 @@ int FromMsg::DiceReply() reply(res.show()); return 1; } - if (strOption == "clrimg") - { - if (frame != QQFrame::CoolQ) - { - reply("ǿQܲҪ˹"); - return -1; - } - if (trusted < 5) - { - reply(GlobalMsg["strNotMaster"]); - return -1; - } - cmd_key = "clrimage"; - sch.push_job(*this); - return 1; + if (strOption == "clrimg") { + reply("ǿQܲҪ˹"); + return -1; } - else if (strOption == "reload") - { - if (trusted < 5 && fromQQ != console.master()) - { + else if (strOption == "reload") { + if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; } - cmd_key = (frame == QQFrame::CoolQ) ? "remake" : "reload"; + cmd_key = "reload"; sch.push_job(*this); return 1; } @@ -1366,10 +1281,8 @@ int FromMsg::DiceReply() sch.push_job(*this); return 1; } - else if (strOption == "die") - { - if (trusted < 5 && fromQQ != console.master()) - { + else if (strOption == "die") { + if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; } @@ -1377,10 +1290,8 @@ int FromMsg::DiceReply() sch.push_job(*this); return 1; } - if (strOption == "rexplorer") - { - if (trusted < 5 && fromQQ != console.master()) - { + if (strOption == "rexplorer") { + if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; } @@ -1389,10 +1300,8 @@ int FromMsg::DiceReply() this_thread::sleep_for(3s); note("Դ\nǰڴռã" + to_string(getRamPort()) + "%"); } - else if (strOption == "cmd") - { - if (fromQQ != console.master()) - { + else if (strOption == "cmd") { + if (fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; } @@ -1402,25 +1311,20 @@ int FromMsg::DiceReply() return 1; } } - else if (strLowerMessage.substr(intMsgCnt, 5) == "admin") - { + else if (strLowerMessage.substr(intMsgCnt, 5) == "admin") { intMsgCnt += 5; return AdminEvent(readPara()); } - else if (strLowerMessage.substr(intMsgCnt, 5) == "cloud") - { + else if (strLowerMessage.substr(intMsgCnt, 5) == "cloud") { intMsgCnt += 5; string strOpt = readPara(); - if (trusted < 4 && fromQQ != console.master()) - { + if (trusted < 4 && fromQQ != console.master()) { reply(GlobalMsg["strNotAdmin"]); return 1; } - if (strOpt == "update") - { + if (strOpt == "update") { strVar["ver"] = readPara(); - if (strVar["ver"].empty()) - { + if (strVar["ver"].empty()) { Cloud::checkUpdate(this); } else if (strVar["ver"] == "dev" || strVar["ver"] == "release") { @@ -1435,20 +1339,17 @@ int FromMsg::DiceReply() return 1; } } - else if (strLowerMessage.substr(intMsgCnt, 5) == "coc7d" || strLowerMessage.substr(intMsgCnt, 4) == "cocd") - { + else if (strLowerMessage.substr(intMsgCnt, 5) == "coc7d" || strLowerMessage.substr(intMsgCnt, 4) == "cocd") { COC7D(strVar["res"]); reply(GlobalMsg["strCOCBuild"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 5) == "coc6d") - { + else if (strLowerMessage.substr(intMsgCnt, 5) == "coc6d") { COC6D(strVar["res"]); reply(GlobalMsg["strCOCBuild"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 5) == "group") - { + else if (strLowerMessage.substr(intMsgCnt, 5) == "group") { intMsgCnt += 5; long long llGroup(fromGroup); readSkipSpace(); @@ -1456,30 +1357,24 @@ int FromMsg::DiceReply() reply(fmt->get_help("group")); return 1; } - if (strLowerMessage.substr(intMsgCnt, 3) == "all") - { - if (trusted < 5) - { + if (strLowerMessage.substr(intMsgCnt, 3) == "all") { + if (trusted < 5) { reply(GlobalMsg["strNotMaster"]); return 1; } intMsgCnt += 3; readSkipSpace(); - if (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-') - { + if (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-') { bool isSet = strMsg[intMsgCnt] == '+'; intMsgCnt++; string strOption = strVar["option"] = readRest(); - if (!mChatConf.count(strVar["option"])) - { + if (!mChatConf.count(strVar["option"])) { reply(GlobalMsg["strGroupSetNotExist"]); return 1; } int Cnt = 0; - if (isSet) - { - for (auto& [id, grp] : ChatList) - { + if (isSet) { + for (auto& [id, grp] : ChatList) { if (grp.isset(strOption))continue; grp.set(strOption); Cnt++; @@ -1487,10 +1382,8 @@ int FromMsg::DiceReply() strVar["cnt"] = to_string(Cnt); note(GlobalMsg["strGroupSetAll"], 0b100); } - else - { - for (auto& [id, grp] : ChatList) - { + else { + for (auto& [id, grp] : ChatList) { if (!grp.isset(strOption))continue; grp.reset(strOption); Cnt++; @@ -1501,60 +1394,49 @@ int FromMsg::DiceReply() } return 1; } - if (string strGroup = readDigit(false); !strGroup.empty()) - { + if (string& strGroup = strVar["group_id"] = readDigit(false); !strGroup.empty()) { llGroup = stoll(strGroup); - if (!ChatList.count(llGroup)) - { + if (!ChatList.count(llGroup)) { reply(GlobalMsg["strGroupNotFound"]); return 1; } - if (getGroupAuth(llGroup) < 0) - { + if (getGroupAuth(llGroup) < 0) { reply(GlobalMsg["strGroupDenied"]); return 1; } } - else if (intT != GroupT)return 0; + else if (fromChat.second != msgtype::Group)return 0; + else strVar["group_id"] = to_string(fromGroup); Chat& grp = chat(llGroup); while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - if (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-') - { + if (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-') { bool isSet = strMsg[intMsgCnt] == '+'; intMsgCnt++; strVar["option"] = readRest(); - if (!mChatConf.count(strVar["option"])) - { + if (!mChatConf.count(strVar["option"])) { reply(GlobalMsg["strGroupSetDenied"]); return 1; } - if (getGroupAuth(llGroup) >= get(mChatConf, strVar["option"], 0)) - { - if (isSet) - { - if (groupset(llGroup, strVar["option"]) < 1) - { + if (getGroupAuth(llGroup) >= get(mChatConf, strVar["option"], 0)) { + if (isSet) { + if (groupset(llGroup, strVar["option"]) < 1) { chat(llGroup).set(strVar["option"]); reply(GlobalMsg["strGroupSetOn"]); - if (strVar["Option"] == "ʹ") - { + if (strVar["Option"] == "ʹ") { AddMsgToQueue(getMsg("strGroupAuthorized", strVar), fromQQ, msgtype::Group); } } - else - { + else { reply(GlobalMsg["strGroupSetOnAlready"]); } return 1; } - if (grp.isset(strVar["option"])) - { + if (grp.isset(strVar["option"])) { chat(llGroup).reset(strVar["option"]); reply(GlobalMsg["strGroupSetOff"]); } - else - { + else { reply(GlobalMsg["strGroupSetOffAlready"]); } return 1; @@ -1562,82 +1444,84 @@ int FromMsg::DiceReply() reply(GlobalMsg["strGroupSetDenied"]); return 1; } - GroupInfo grpinfo; - bool isInGroup = getGroupList().count(llGroup); - if (isInGroup)grpinfo = GroupInfo(llGroup); + bool isInGroup{ fromGroup == llGroup || DD::isGroupMember(llGroup,console.DiceMaid,true) }; string Command = readPara(); - - if (Command == "state") - { - time_t tNow = time(nullptr); - const int intTMonth = 30 * 24 * 60 * 60; - set sInact; - set sBlackQQ; - int cntUser(0); - if (isInGroup) - for (const auto& each : getGroupMemberList(llGroup)) - { - if (!each.LastMsgTime || tNow - each.LastMsgTime > intTMonth) - { - sInact.insert(each.GroupNick + "(" + to_string(each.QQID) + ")"); - } - if (blacklist->get_qq_danger(each.QQID)) - { - sBlackQQ.insert(each.GroupNick + "(" + to_string(each.QQID) + ")"); - } - if (UserList.count(each.QQID))cntUser++; - } + strVar["group"] = DD::printGroupInfo(llGroup); + if (Command == "state") { ResList res; - strVar["group"] = grpinfo.llGroup ? grpinfo.tostring() : printGroup(llGroup); res << "{group}"; res << grp.listBoolConf(); - for (const auto& it : grp.intConf) - { + for (const auto& it : grp.intConf) { res << it.first + "" + to_string(it.second); } res << "¼" + printDate(grp.tCreated); res << "¼" + printDate(grp.tUpdated); if (grp.inviter)res << "ߣ" + printQQ(grp.inviter); - if (isInGroup) - { - res << string("Ⱥӭ") + (grp.isset("Ⱥӭ") ? "" : "δ") - << "ûռȣ" + to_string(cntUser * 100 / (grpinfo.nGroupSize - 1)) + "%"; - res << (!sInact.empty() ? "\n30첻ԾȺԱ" + to_string(sInact.size()) : ""); - if (!sBlackQQ.empty()) - { - if (sBlackQQ.size() > 8) - res << GlobalMsg["strSelfName"] + "ĺԱ" + to_string(sBlackQQ.size()) + ""; - else - { - res << GlobalMsg["strSelfName"] + "ĺԱ:{blackqq}"; - ResList blacks; - for (const auto& each : sBlackQQ) - { - blacks << each; - } - strVar["blackqq"] = blacks.show(); - } - } - else - { - res << "{self}ĺԱ"; - } - } + res << string("Ⱥӭ") + (grp.isset("Ⱥӭ") ? "" : ""); reply(GlobalMsg["strSelfName"] + res.show()); return 1; } - if (!isInGroup && (intT != GroupT || fromGroup != llGroup)) - { + if (!grp.isGroup || (fromGroup == llGroup && fromChat.second != msgtype::Group)) { + reply(GlobalMsg["strGroupNot"]); + return 1; + } + else if (Command == "info") { + reply(DD::printGroupInfo(llGroup), false); + return 1; + } + else if (!isInGroup) { reply(GlobalMsg["strGroupNotIn"]); return 1; } - if (Command == "info") - { - reply(grpinfo.tostring(), false); + else if (Command == "survey") { + int cntDiver(0); + long long dayMax(0); + set sBlackQQ; + int cntUser(0); + size_t cntDice(0); + time_t tNow = time(nullptr); + const int intTDay = 24 * 60 * 60; + int cntSize(0); + set list{ DD::getGroupMemberList(llGroup) }; + if (list.empty()) { + reply("{self}سԱбʧܣ"); + return 1; + } + for (auto each : list) { + if (each == console.DiceMaid)continue; + if (long long lst{ DD::getGroupLastMsg(llGroup,each) }; lst > 0) { + long long dayDive((tNow - lst) / intTDay); + if (dayDive > dayMax)dayMax = dayDive; + if (dayDive > 30)++cntDiver; + } + if (blacklist->get_qq_danger(each) > 1) { + sBlackQQ.insert(printQQ(each)); + } + if (UserList.count(each))++cntUser; + if (DD::isDiceMaid(each))++cntDice; + ++cntSize; + } + ResList res; + res << "{group}" + << "{self}ûռ: " + to_string(cntUser * 100 / (cntSize)) + "%" + << (cntDice ? "ͬϵ: " + to_string(cntDice) : "") + << (cntDiver ? "30DZˮȺԱ: " + to_string(cntDiver) : ""); + if (!sBlackQQ.empty()) { + if (sBlackQQ.size() > 8) + res << GlobalMsg["strSelfName"] + "ĺԱ" + to_string(sBlackQQ.size()) + ""; + else { + res << GlobalMsg["strSelfName"] + "ĺԱ:{blackqq}"; + ResList blacks; + for (const auto& each : sBlackQQ) { + blacks << each; + } + strVar["blackqq"] = blacks.show(); + } + } + reply(res.show()); return 1; } - if (Command == "diver") - { + else if (Command == "diver") { bool bForKick = false; if (strLowerMessage.substr(intMsgCnt, 5) == "4kick") { bForKick = true; @@ -1646,66 +1530,64 @@ int FromMsg::DiceReply() std::priority_queue> qDiver; time_t tNow = time(nullptr); const int intTDay = 24 * 60 * 60; - int intSize = grpinfo.nGroupSize; - for (auto& each : getGroupMemberList(llGroup)) - { - time_t intLastMsg = (tNow - each.LastMsgTime) / intTDay; - if (!each.LastMsgTime || intLastMsg > 30) - { - qDiver.emplace(intLastMsg, (bForKick ? to_string(each.QQID) - : (each.Nick + "(" + to_string(each.QQID) + ")"))); + int cntSize(0); + for (auto each : DD::getGroupMemberList(llGroup)) { + long long lst{ DD::getGroupLastMsg(llGroup,each) }; + time_t intLastMsg = (tNow - lst) / intTDay; + if (lst > 0 || intLastMsg > 30) { + qDiver.emplace(intLastMsg, (bForKick ? to_string(each) + : printQQ(each))); } + ++cntSize; } - if (qDiver.empty()) - { - reply("{self}δDZˮԱԱбʧܣ"); + if (!cntSize) { + reply("{self}سԱбʧܣ"); + return 1; + } + else if (qDiver.empty()) { + reply("{self}δDZˮȺԱ"); return 1; } int intCnt(0); ResList res; - while (!qDiver.empty()) - { + while (!qDiver.empty()) { res << (bForKick ? qDiver.top().second : (qDiver.top().second + to_string(qDiver.top().first) + "")); - if (++intCnt > 15 && intCnt > intSize / 80)break; + if (++intCnt > 15 && intCnt > cntSize / 80)break; qDiver.pop(); } bForKick ? reply("(.group " + to_string(llGroup) + " kick " + res.show(1)) - :reply("DZˮԱб:" + res.show(1)); + : reply("DZˮԱб:" + res.show(1)); return 1; } - if (int intPms = getGroupMemberInfo(llGroup, fromQQ).permissions; Command == "pause") - { - if (intPms < 2 && trusted < 4) - { + if (bool isAdmin = DD::isGroupAdmin(llGroup, fromQQ, false); Command == "pause") { + if (!isAdmin && trusted < 4) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - if (setGroupWholeBan(llGroup))reply(GlobalMsg["strGroupWholeBanErr"]); - else reply(GlobalMsg["strGroupWholeBan"]); + int secDuring(-1); + string strDuring{ readDigit() }; + if (!strDuring.empty())secDuring = stoi(strDuring); + DD::setGroupWholeBan(llGroup, secDuring); + reply(GlobalMsg["strGroupWholeBan"]); return 1; } - else if (Command == "restart") - { - if (intPms < 2 && trusted < 4) - { + else if (Command == "restart") { + if (!isAdmin && trusted < 4) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - if (!setGroupWholeBan(llGroup, 0))reply(GlobalMsg["strGroupWholeUnban"]); + DD::setGroupWholeBan(llGroup, 0); + reply(GlobalMsg["strGroupWholeUnban"]); return 1; } - else if (Command == "card") - { - if (long long llqq = readID()) - { - if (trusted < 4 && intPms < 2 && llqq != fromQQ) - { + else if (Command == "card") { + if (long long llqq = readID()) { + if (trusted < 4 && !isAdmin && llqq != fromQQ) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) - { + if (!DD::isGroupAdmin(llGroup, console.DiceMaid, true)) { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } @@ -1714,112 +1596,75 @@ int FromMsg::DiceReply() while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; strVar["card"] = readRest(); strVar["target"] = getName(llqq, llGroup); - if (setGroupCard(llGroup, llqq, strVar["card"])) - { - reply(GlobalMsg["strGroupCardSetErr"]); - } - else - { - reply(GlobalMsg["strGroupCardSet"]); - } + DD::setGroupCard(llGroup, llqq, strVar["card"]); + reply(GlobalMsg["strGroupCardSet"]); } - else - { + else { reply(GlobalMsg["strQQIDEmpty"]); } return 1; } - else if ((intPms < 2 && (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3 || trusted < 5))) - { + else if ((!isAdmin && (!DD::isGroupOwner(llGroup, console.DiceMaid,true) || trusted < 5))) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - else if (Command == "ban") - { - if (trusted < 4) - { + else if (Command == "ban") { + if (trusted < 4) { reply(GlobalMsg["strNotAdmin"]); return -1; } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) - { + if (!DD::isGroupAdmin(llGroup, console.DiceMaid, true)) { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } - string QQNum = readDigit(); - if (QQNum.empty()) - { + string& QQNum = strVar["ban_qq"] = readDigit(); + if (QQNum.empty()) { reply(GlobalMsg["strQQIDEmpty"]); return -1; } long long llMemberQQ = stoll(QQNum); - GroupMemberInfo Member = getGroupMemberInfo(llGroup, llMemberQQ); - if (Member.QQID == llMemberQQ) - { - strVar["member"] = getName(Member.QQID, llGroup); - if (Member.permissions > 1) - { - reply(GlobalMsg["strSelfPermissionErr"]); - return 1; - } - string strMainDice = readDice(); - if (strMainDice.empty()) - { - reply(GlobalMsg["strValueErr"]); - return -1; - } - const int intDefaultDice = get(getUser(fromQQ).intConf, string("Ĭ"), 100); - RD rdMainDice(strMainDice, intDefaultDice); - rdMainDice.Roll(); - long long intDuration = rdMainDice.intTotal; - strVar["res"] = rdMainDice.FormCompleteString(); - if (setGroupBan(llGroup, llMemberQQ, intDuration * 60) == 0) - if (intDuration <= 0) - reply(GlobalMsg["strGroupUnban"]); - else reply(GlobalMsg["strGroupBan"]); - else reply(GlobalMsg["strGroupBanErr"]); + strVar["member"] = getName(llMemberQQ, llGroup); + string strMainDice = readDice(); + if (strMainDice.empty()) { + reply(GlobalMsg["strValueErr"]); + return -1; } - else reply("{self}޴ȺԱ"); - } - else if (Command == "kick") -{ - if (trusted < 4) - { + const int intDefaultDice = get(getUser(fromQQ).intConf, string("Ĭ"), 100); + RD rdMainDice(strMainDice, intDefaultDice); + rdMainDice.Roll(); + int intDuration{ rdMainDice.intTotal }; + strVar["res"] = rdMainDice.FormShortString(); + DD::setGroupBan(llGroup, llMemberQQ, intDuration * 60); + if (intDuration <= 0) + reply(GlobalMsg["strGroupUnban"]); + else reply(GlobalMsg["strGroupBan"]); + } + else if (Command == "kick") { + if (trusted < 4) { reply(GlobalMsg["strNotAdmin"]); return -1; } - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 2) - { + if (!DD::isGroupAdmin(llGroup, console.DiceMaid, true)) { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } long long llMemberQQ = readID(); - if (!llMemberQQ) - { + if (!llMemberQQ) { reply(GlobalMsg["strQQIDEmpty"]); return -1; } ResList resKicked, resDenied, resNotFound; - GroupMemberInfo Member; - do - { - Member = getGroupMemberInfo(llGroup, llMemberQQ); - if (Member.QQID == llMemberQQ) - { - if (Member.permissions > 1) - { - resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; + do { + if (int auth{ DD::getGroupAuth(llGroup, llMemberQQ,0) }) { + if (auth > 1) { + resDenied << printQQ(llMemberQQ); continue; } - if (setGroupKick(llGroup, llMemberQQ, false) == 0) - { - resKicked << Member.Nick + "(" + to_string(Member.QQID) + ")"; - } - else resDenied << Member.Nick + "(" + to_string(Member.QQID) + ")"; + DD::setGroupKick(llGroup, llMemberQQ); + resKicked << printQQ(llMemberQQ); } - else resNotFound << to_string(llMemberQQ); - } - while ((llMemberQQ = readID())); + else resNotFound << printQQ(llMemberQQ); + } while ((llMemberQQ = readID())); strReply = GlobalMsg["strSelfName"]; if (!resKicked.empty())strReply += "ƳȺԱ" + resKicked.show() + "\n"; if (!resDenied.empty())strReply += "Ƴʧܣ" + resDenied.show() + "\n"; @@ -1827,70 +1672,54 @@ int FromMsg::DiceReply() reply(); return 1; } - else if (Command == "title") -{ - if (getGroupMemberInfo(llGroup, console.DiceMaid).permissions < 3) - { + else if (Command == "title") { + if (!DD::isGroupOwner(llGroup, console.DiceMaid,true)) { reply(GlobalMsg["strSelfPermissionErr"]); return 1; } - if (long long llqq = readID()) - { + if (long long llqq = readID()) { while (!isspace(static_cast(strMsg[intMsgCnt])) && intMsgCnt != strMsg.length()) intMsgCnt++; while (isspace(static_cast(strMsg[intMsgCnt])))intMsgCnt++; strVar["title"] = readRest(); - if (setGroupSpecialTitle(llGroup, llqq, strVar["title"])) - { - reply(GlobalMsg["strGroupTitleSetErr"]); - } - else - { - strVar["target"] = getName(llqq, llGroup); - reply(GlobalMsg["strGroupTitleSet"]); - } + DD::setGroupTitle(llGroup, llqq, strVar["title"]); + strVar["target"] = getName(llqq, llGroup); + reply(GlobalMsg["strGroupTitleSet"]); } - else - { + else { reply(GlobalMsg["strQQIDEmpty"]); } return 1; } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 5) == "reply") - { + else if (strLowerMessage.substr(intMsgCnt, 5) == "reply") { intMsgCnt += 5; - if (trusted < 4) - { + if (trusted < 4) { reply(GlobalMsg["strNotAdmin"]); return -1; } strVar["key"] = readUntilSpace(); - if (strVar["key"].empty()) - { + if (strVar["key"].empty()) { reply(GlobalMsg["strParaEmpty"]); return -1; } vector* Deck = nullptr; CardDeck::mReplyDeck[strVar["key"]] = {}; Deck = &CardDeck::mReplyDeck[strVar["key"]]; - while (intMsgCnt != strMsg.length()) - { + while (intMsgCnt != strMsg.length()) { string item = readItem(); if (!item.empty())Deck->push_back(item); } - if (Deck->empty()) - { - reply(GlobalMsg["strReplyDel"], {strVar["key"]}); + if (Deck->empty()) { + reply(GlobalMsg["strReplyDel"], { strVar["key"] }); CardDeck::mReplyDeck.erase(strVar["key"]); } - else reply(GlobalMsg["strReplySet"], {strVar["key"]}); + else reply(GlobalMsg["strReplySet"], { strVar["key"] }); saveJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 5) == "rules") - { + else if (strLowerMessage.substr(intMsgCnt, 5) == "rules") { intMsgCnt += 5; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; @@ -1898,73 +1727,61 @@ int FromMsg::DiceReply() reply(fmt->get_help("rules")); return 1; } - if (strLowerMessage.substr(intMsgCnt, 3) == "set") - { + if (strLowerMessage.substr(intMsgCnt, 3) == "set") { intMsgCnt += 3; while (isspace(static_cast(strMsg[intMsgCnt])) || strMsg[intMsgCnt] == ':') intMsgCnt++; string strDefaultRule = strMsg.substr(intMsgCnt); - if (strDefaultRule.empty()) - { + if (strDefaultRule.empty()) { getUser(fromQQ).rmStrConf("ĬϹ"); reply(GlobalMsg["strRuleReset"]); } - else - { + else { for (auto& n : strDefaultRule) n = toupper(static_cast(n)); getUser(fromQQ).setConf("ĬϹ", strDefaultRule); reply(GlobalMsg["strRuleSet"]); } } - else - { + else { string strSearch = strMsg.substr(intMsgCnt); for (auto& n : strSearch) n = toupper(static_cast(n)); string strReturn; - if (getUser(fromQQ).strConf.count("ĬϹ") && strSearch.find(':') == string::npos && - GetRule::get(getUser(fromQQ).strConf["ĬϹ"], strSearch, strReturn)) - { + if (getUser(fromQQ).strConf.count("ĬϹ") && strSearch.find(':') == string::npos && + GetRule::get(getUser(fromQQ).strConf["ĬϹ"], strSearch, strReturn)) { reply(strReturn); } - else if (GetRule::analyze(strSearch, strReturn)) - { + else if (GetRule::analyze(strSearch, strReturn)) { reply(strReturn); } - else - { + else { reply(GlobalMsg["strRuleErr"] + strReturn); } } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "coc6") - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "coc6") { intMsgCnt += 4; if (strLowerMessage[intMsgCnt] == 's') intMsgCnt++; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; string strNum; - while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) - { + while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) { strNum += strLowerMessage[intMsgCnt]; intMsgCnt++; } - if (strNum.length() > 2) - { + if (strNum.length() > 2) { reply(GlobalMsg["strCharacterTooBig"]); return 1; } const int intNum = stoi(strNum.empty() ? "1" : strNum); - if (intNum > 10) - { + if (intNum > 10) { reply(GlobalMsg["strCharacterTooBig"]); return 1; } - if (intNum == 0) - { + if (intNum == 0) { reply(GlobalMsg["strCharacterCannotBeZero"]); return 1; } @@ -1972,90 +1789,81 @@ int FromMsg::DiceReply() reply(GlobalMsg["strCOCBuild"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "deck") - { - if (trusted < 4 && console["DisabledDeck"]) - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "deck") { + if (trusted < 4 && console["DisabledDeck"]) { reply(GlobalMsg["strDisabledDeckGlobal"]); return 1; } intMsgCnt += 4; string strRoom = readDigit(false); long long llRoom = strRoom.empty() ? fromSession : stoll(strRoom); + if (llRoom == 0)llRoom = fromSession; if (strMsg.length() == intMsgCnt) { reply(fmt->get_help("deck")); return 1; } string strPara = readPara(); - if (strPara == "show") - { + if (strPara == "show") { if (gm->has_session(llRoom)) - gm->session(llRoom).deck_show(this); + gm->session(llRoom).deck_show(this); else reply(GlobalMsg["strDeckListEmpty"]); - return 1; } - if ((!isAuth || llRoom != fromSession) && !trusted) - { + else if ((!isAuth || llRoom != fromSession) && !trusted) { reply(GlobalMsg["strWhiteQQDenied"]); } - else if (strPara == "set") - { + else if (strPara == "set") { gm->session(llRoom).deck_set(this); } - else if (strPara == "reset") - { + else if (strPara == "reset") { gm->session(llRoom).deck_reset(this); } else if (strPara == "del") { gm->session(llRoom).deck_del(this); } - else if (strPara == "clr") - { + else if (strPara == "clr") { gm->session(llRoom).deck_clr(this); } - else if (strPara == "new") - { + else if (strPara == "new") { gm->session(llRoom).deck_new(this); } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "draw") - { - if (trusted < 4 && console["DisabledDraw"]) - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "draw") { + if (trusted < 4 && console["DisabledDraw"]) { reply(GlobalMsg["strDisabledDrawGlobal"]); return 1; } strVar["option"] = "draw"; - if (intT && groupset(fromGroup, strVar["option"]) > 0) - { + if (fromChat.second != msgtype::Private && groupset(fromGroup, strVar["option"]) > 0) { reply(GlobalMsg["strGroupSetOnAlready"]); return 1; } intMsgCnt += 4; + bool isPrivate(false); + if (strMsg[intMsgCnt] == 'h' && isspace(static_cast(strMsg[intMsgCnt + 1]))) { + isPrivate = true; + ++intMsgCnt; + } while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; vector ProDeck; vector* TempDeck = nullptr; - bool isPrivate(false); string& key{ strVar["deck_name"] = readAttrName() }; if (!strVar["deck_name"].empty() && strVar["deck_name"][0] == '_') { isPrivate = true; strVar["hidden"]; strVar["deck_name"].erase(strVar["deck_name"].begin()); } - if (strVar["deck_name"].empty()){ + if (strVar["deck_name"].empty()) { reply(fmt->get_help("draw")); return 1; } - else - { + else { if (gm->has_session(fromSession) && gm->session(fromSession).has_deck(key)) { - gm->session(fromSession).deck_draw(this); + gm->session(fromSession)._draw(this); return 1; } - else if (strVar["deck_name"][0] == '_' || CardDeck::findDeck(strVar["deck_name"]) == 0) - { + else if (strVar["deck_name"][0] == '_' || CardDeck::findDeck(strVar["deck_name"]) == 0) { strReply = GlobalMsg["strDeckNotFound"]; reply(strReply); return 1; @@ -2064,11 +1872,9 @@ int FromMsg::DiceReply() TempDeck = &ProDeck; } int intCardNum = 1; - switch (readNum(intCardNum)) - { + switch (readNum(intCardNum)) { case 0: - if (intCardNum == 0) - { + if (intCardNum == 0) { reply(GlobalMsg["strNumCannotBeZero"]); return 1; } @@ -2077,12 +1883,11 @@ int FromMsg::DiceReply() case -2: reply(GlobalMsg["strParaIllegal"]); console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "ʹ˷Ƿָ\n" + strMsg, 1, - printSTNow()); + printSTNow()); return 1; } ResList Res; - while (intCardNum--) - { + while (intCardNum--) { Res << CardDeck::drawCard(*TempDeck); if (TempDeck->empty())break; } @@ -2093,24 +1898,22 @@ int FromMsg::DiceReply() reply(GlobalMsg["strDrawHidden"]); replyHidden(GlobalMsg["strDrawCard"]); } - else + else reply(GlobalMsg["strDrawCard"]); - if (intCardNum > 0) - { + if (intCardNum > 0) { reply(GlobalMsg["strDeckEmpty"]); return 1; } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "init" && intT) - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "init") { intMsgCnt += 4; strVar["table_name"] = "ȹ"; string strCmd = readPara(); - if (strCmd.empty()) { + if (strCmd.empty()|| fromChat.second == msgtype::Private) { reply(fmt->get_help("init")); } - else if (!gm->has_session(fromSession)|| !gm->session(fromSession).table_count("ȹ")) { + else if (!gm->has_session(fromSession) || !gm->session(fromSession).table_count("ȹ")) { reply(GlobalMsg["strGMTableNotExist"]); } else if (strCmd == "show" || strCmd == "list") { @@ -2132,10 +1935,8 @@ int FromMsg::DiceReply() } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "jrrp") - { - if (console["DisabledJrrp"]) - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "jrrp") { + if (console["DisabledJrrp"]) { reply("&strDisabledJrrpGlobal"); return 1; } @@ -2143,108 +1944,75 @@ int FromMsg::DiceReply() while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; const string Command = strLowerMessage.substr(intMsgCnt, strMsg.find(' ', intMsgCnt) - intMsgCnt); - if (intT == GroupT) - { - if (Command == "on") - { - if (isAuth) - { - if (groupset(fromGroup, "jrrp") > 0) - { + if (fromChat.second == msgtype::Group) { + if (Command == "on") { + if (isAuth) { + if (groupset(fromGroup, "jrrp") > 0) { chat(fromGroup).reset("jrrp"); reply("ɹڱȺJRRP!"); } - else - { + else { reply("ڱȺJRRPûб!"); } } - else - { + else { reply(GlobalMsg["strPermissionDeniedErr"]); } return 1; } - if (Command == "off") - { - if (isAuth) - { - if (groupset(fromGroup, "jrrp") < 1) - { + if (Command == "off") { + if (isAuth) { + if (groupset(fromGroup, "jrrp") < 1) { chat(fromGroup).set("jrrp"); reply("ɹڱȺнJRRP!"); } - else - { + else { reply("ڱȺJRRPûб!"); } } - else - { + else { reply(GlobalMsg["strPermissionDeniedErr"]); } return 1; } - if (groupset(fromGroup, "jrrp") > 0) - { + if (groupset(fromGroup, "jrrp") > 0) { reply("ڱȺJRRPѱ"); return 1; } } - else if (intT == DiscussT) - { - if (Command == "on") - { - if (groupset(fromGroup, "jrrp") > 0) - { + else if (fromChat.second != msgtype::Discuss) { + if (Command == "on") { + if (groupset(fromGroup, "jrrp") > 0) { chat(fromGroup).reset("jrrp"); reply("ɹڴ˶JRRP!"); } - else - { + else { reply("ڴ˶JRRPûб!"); } return 1; } - if (Command == "off") - { - if (groupset(fromGroup, "jrrp") < 1) - { + if (Command == "off") { + if (groupset(fromGroup, "jrrp") < 1) { chat(fromGroup).set("jrrp"); reply("ɹڴ˶нJRRP!"); } - else - { + else { reply("ڴ˶JRRPûб!"); } return 1; } - if (groupset(fromGroup, "jrrp") > 0) - { + if (groupset(fromGroup, "jrrp") > 0) { reply("ڴ˶JRRPѱ!"); return 1; } } - string data = "QQ=" + to_string(getLoginQQ()) + "&v=20190114" + "&QueryQQ=" + to_string(fromQQ); - char* frmdata = new char[data.length() + 1]; - strcpy_s(frmdata, data.length() + 1, data.c_str()); - bool res = Network::POST("api.kokona.tech", "/jrrp", 5555, frmdata, strVar["res"]); - delete[] frmdata; - if (res) - { - reply(GlobalMsg["strJrrp"], {strVar["nick"], strVar["res"]}); - } - else - { - reply(GlobalMsg["strJrrpErr"], {strVar["res"]}); - } + strVar["res"] = to_string(today->getJrrp(fromQQ)); + reply(GlobalMsg["strJrrp"], { strVar["nick"], strVar["res"] }); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "link") - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "link") { intMsgCnt += 4; - if (trusted < 3) - { + if (trusted < 3) { reply(GlobalMsg["strNotAdmin"]); return true; } @@ -2263,38 +2031,33 @@ int FromMsg::DiceReply() } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "name") - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "name") { intMsgCnt += 4; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; string type = readPara(); string strNum = readDigit(); - if (strNum.length() > 1 && strNum != "10") - { + if (strNum.length() > 1 && strNum != "10") { reply(GlobalMsg["strNameNumTooBig"]); return 1; } int intNum = strNum.empty() ? 1 : stoi(strNum); - if (intNum == 0) - { + if (intNum == 0) { reply(GlobalMsg["strNameNumCannotBeZero"]); return 1; } string strDeckName = (!type.empty() && CardDeck::mPublicDeck.count("_" + type)) ? "_" + type : ""; vector TempDeck(CardDeck::mPublicDeck[strDeckName]); ResList Res; - while (intNum--) - { + while (intNum--) { Res << CardDeck::drawCard(TempDeck, true); } strVar["res"] = Res.dot("").show(); reply(GlobalMsg["strNameGenerator"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "send") - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "send") { intMsgCnt += 4; readSkipSpace(); if (strMsg.length() == intMsgCnt) { @@ -2302,19 +2065,15 @@ int FromMsg::DiceReply() return 1; } //ȿMasterָĿ귢 - if (trusted > 2) - { + if (trusted > 2) { chatType ct; - if (!readChat(ct, true)) - { + if (!readChat(ct, true)) { readSkipColon(); string strFwd(readRest()); - if (strFwd.empty()) - { + if (strFwd.empty()) { reply(GlobalMsg["strSendMsgEmpty"]); } - else - { + else { AddMsgToQueue(strFwd, ct); reply(GlobalMsg["strSendMsg"]); } @@ -2322,19 +2081,16 @@ int FromMsg::DiceReply() } readSkipColon(); } - else if (!console) - { + else if (!console) { reply(GlobalMsg["strSendMsgInvalid"]); return 1; } - else if (console["DisabledSend"] && trusted < 3) - { + else if (console["DisabledSend"] && trusted < 3) { reply(GlobalMsg["strDisabledSendGlobal"]); return 1; } string strInfo = readRest(); - if (strInfo.empty()) - { + if (strInfo.empty()) { reply(GlobalMsg["strSendMsgEmpty"]); return 1; } @@ -2343,13 +2099,11 @@ int FromMsg::DiceReply() reply(GlobalMsg["strSendMasterMsg"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 4) == "user") - { + else if (strLowerMessage.substr(intMsgCnt, 4) == "user") { intMsgCnt += 4; string strOption = readPara(); if (strOption.empty())return 0; - if (strOption == "state") - { + if (strOption == "state") { User& user = getUser(fromQQ); strVar["user"] = printQQ(fromQQ); ResList rep; @@ -2361,31 +2115,25 @@ int FromMsg::DiceReply() reply("{user}" + rep.show()); return 1; } - if (strOption == "trust") - { - if (trusted < 4 && fromQQ != console.master()) - { + if (strOption == "trust") { + if (trusted < 4 && fromQQ != console.master()) { reply(GlobalMsg["strNotAdmin"]); return 1; } string strTarget = readDigit(); - if (strTarget.empty()) - { + if (strTarget.empty()) { reply(GlobalMsg["strQQIDEmpty"]); return 1; } long long llTarget = stoll(strTarget); - if (trustedQQ(llTarget) >= trusted && fromQQ != console.master()) - { + if (trustedQQ(llTarget) >= trusted && fromQQ != console.master()) { reply(GlobalMsg["strUserTrustDenied"]); return 1; } strVar["user"] = printQQ(llTarget); strVar["trust"] = readDigit(); - if (strVar["trust"].empty()) - { - if (!UserList.count(llTarget)) - { + if (strVar["trust"].empty()) { + if (!UserList.count(llTarget)) { reply(GlobalMsg["strUserNotFound"]); return 1; } @@ -2395,13 +2143,11 @@ int FromMsg::DiceReply() } User& user = getUser(llTarget); if (short intTrust = stoi(strVar["trust"]); intTrust < 0 || intTrust > 255 || (intTrust >= trusted && fromQQ - != console.master())) - { + != console.master())) { reply(GlobalMsg["strUserTrustIllegal"]); return 1; } - else - { + else { user.trust(intTrust); } reply(GlobalMsg["strUserTrusted"]); @@ -2412,6 +2158,7 @@ int FromMsg::DiceReply() reply(GlobalMsg["strNotAdmin"]); return 1; } + strVar["note"] = readPara(); long long llTargetID(readID()); if (!llTargetID) { reply(GlobalMsg["strQQIDEmpty"]); @@ -2426,22 +2173,18 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "kill") - { - if (trusted < 4 && fromQQ != console.master()) - { + if (strOption == "kill") { + if (trusted < 4 && fromQQ != console.master()) { reply(GlobalMsg["strNotAdmin"]); return 1; } long long llTarget = readID(); - if (trustedQQ(llTarget) >= trusted && fromQQ != console.master()) - { + if (trustedQQ(llTarget) >= trusted && fromQQ != console.master()) { reply(GlobalMsg["strUserTrustDenied"]); return 1; } strVar["user"] = printQQ(llTarget); - if (!llTarget || !UserList.count(llTarget)) - { + if (!llTarget || !UserList.count(llTarget)) { reply(GlobalMsg["strUserNotFound"]); return 1; } @@ -2449,10 +2192,8 @@ int FromMsg::DiceReply() reply("Ĩ{user}û¼"); return 1; } - if (strOption == "clr") - { - if (trusted < 5) - { + if (strOption == "clr") { + if (trusted < 5) { reply(GlobalMsg["strNotMaster"]); return 1; } @@ -2461,8 +2202,7 @@ int FromMsg::DiceReply() return 1; } } - else if (strLowerMessage.substr(intMsgCnt, 3) == "coc") - { + else if (strLowerMessage.substr(intMsgCnt, 3) == "coc") { intMsgCnt += 3; if (strLowerMessage[intMsgCnt] == '7') intMsgCnt++; @@ -2471,19 +2211,16 @@ int FromMsg::DiceReply() while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; string strNum; - while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) - { + while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) { strNum += strLowerMessage[intMsgCnt]; intMsgCnt++; } - if (strNum.length() > 1 && strNum != "10") - { + if (strNum.length() > 1 && strNum != "10") { reply(GlobalMsg["strCharacterTooBig"]); return 1; } const int intNum = stoi(strNum.empty() ? "1" : strNum); - if (intNum == 0) - { + if (intNum == 0) { reply(GlobalMsg["strCharacterCannotBeZero"]); return 1; } @@ -2491,25 +2228,21 @@ int FromMsg::DiceReply() reply(GlobalMsg["strCOCBuild"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 3) == "dnd") - { + else if (strLowerMessage.substr(intMsgCnt, 3) == "dnd") { intMsgCnt += 3; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; string strNum; - while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) - { + while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) { strNum += strLowerMessage[intMsgCnt]; intMsgCnt++; } - if (strNum.length() > 1 && strNum != "10") - { + if (strNum.length() > 1 && strNum != "10") { reply(GlobalMsg["strCharacterTooBig"]); return 1; } const int intNum = stoi(strNum.empty() ? "1" : strNum); - if (intNum == 0) - { + if (intNum == 0) { reply(GlobalMsg["strCharacterCannotBeZero"]); return 1; } @@ -2519,29 +2252,29 @@ int FromMsg::DiceReply() return 1; } else if (strLowerMessage.substr(intMsgCnt, 3) == "log") { - intMsgCnt += 3; - string strPara = readPara(); - if (strPara.empty()) { - reply(fmt->get_help("log")); - } - else if (DiceSession& game = gm->session(fromSession); strPara == "new") { - game.log_new(this); - }else if(strPara == "on") { - game.log_on(this); - } - else if (strPara == "off") { - game.log_off(this); - } - else if (strPara == "end") { - game.log_end(this); - } - else { - reply(fmt->get_help("log")); + intMsgCnt += 3; + string strPara = readPara(); + if (strPara.empty()) { + reply(fmt->get_help("log")); + } + else if (DiceSession& game = gm->session(fromSession); strPara == "new") { + game.log_new(this); + } + else if (strPara == "on") { + game.log_on(this); + } + else if (strPara == "off") { + game.log_off(this); + } + else if (strPara == "end") { + game.log_end(this); + } + else { + reply(fmt->get_help("log")); + } + return 1; } - return 1; -} - else if (strLowerMessage.substr(intMsgCnt, 3) == "nnn") - { + else if (strLowerMessage.substr(intMsgCnt, 3) == "nnn") { intMsgCnt += 3; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; @@ -2549,12 +2282,11 @@ int FromMsg::DiceReply() string strDeckName = (!type.empty() && CardDeck::mPublicDeck.count("_" + type)) ? "_" + type : ""; strVar["new_nick"] = strip(CardDeck::drawCard(CardDeck::mPublicDeck[strDeckName], true)); getUser(fromQQ).setNick(fromGroup, strVar["new_nick"]); - const string strReply = format(GlobalMsg["strNameSet"], {strVar["nick"], strVar["new_nick"]}); + const string strReply = format(GlobalMsg["strNameSet"], { strVar["nick"], strVar["new_nick"] }); reply(strReply); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 3) == "set") - { + else if (strLowerMessage.substr(intMsgCnt, 3) == "set") { intMsgCnt += 3; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; @@ -2564,13 +2296,11 @@ int FromMsg::DiceReply() if (strDefaultDice.empty()) strDefaultDice = "100"; for (auto charNumElement : strDefaultDice) - if (!isdigit(static_cast(charNumElement))) - { + if (!isdigit(static_cast(charNumElement))) { reply(GlobalMsg["strSetInvalid"]); return 1; } - if (strDefaultDice.length() > 4) - { + if (strDefaultDice.length() > 4) { reply(GlobalMsg["strSetTooBig"]); return 1; } @@ -2583,30 +2313,25 @@ int FromMsg::DiceReply() reply(strSetSuccessReply); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 3) == "str" && trusted > 3) - { + else if (strLowerMessage.substr(intMsgCnt, 3) == "str" && trusted > 3) { string strName; while (!isspace(static_cast(strLowerMessage[intMsgCnt])) && intMsgCnt != strLowerMessage.length() - ) - { + ) { strName += strMsg[intMsgCnt]; intMsgCnt++; } while (strMsg[intMsgCnt] == ' ')intMsgCnt++; - if (intMsgCnt == strMsg.length() || strMsg.substr(intMsgCnt) == "show") - { + if (intMsgCnt == strMsg.length() || strMsg.substr(intMsgCnt) == "show") { AddMsgToQueue(GlobalMsg[strName], fromChat); return 1; } string strMessage = strMsg.substr(intMsgCnt); - if (strMessage == "reset") - { + if (strMessage == "reset") { EditedMsg.erase(strName); GlobalMsg[strName] = ""; note("" + strName + "Զ壬´ָĬá", 0b1); } - else - { + else { if (strMessage == "NULL")strMessage = ""; EditedMsg[strName] = strMessage; GlobalMsg[strName] = strMessage; @@ -2615,8 +2340,7 @@ int FromMsg::DiceReply() saveJMap(DiceDir + "\\conf\\CustomMsg.json", EditedMsg); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "en") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "en") { intMsgCnt += 2; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; @@ -2629,22 +2353,17 @@ int FromMsg::DiceReply() short nCurrentVal; short* pVal = &nCurrentVal; //ȡԭֵ - if (strCurrentValue.empty()) - { - if (PList.count(fromQQ) && !strVar["attr"].empty() && (getPlayer(fromQQ)[fromGroup].stored(strVar["attr"]))) - { + if (strCurrentValue.empty()) { + if (PList.count(fromQQ) && !strVar["attr"].empty() && (getPlayer(fromQQ)[fromGroup].stored(strVar["attr"]))) { pVal = &getPlayer(fromQQ)[fromGroup][strVar["attr"]]; } - else - { + else { reply(GlobalMsg["strEnValEmpty"]); return 1; } } - else - { - if (strCurrentValue.length() > 3) - { + else { + if (strCurrentValue.length() > 3) { reply(GlobalMsg["strEnValInvalid"]); return 1; } @@ -2656,12 +2375,10 @@ int FromMsg::DiceReply() string strEnFail; string strEnSuc = "1D10"; //ԼӼͷȷ뼼ֵ - if (strLowerMessage[intMsgCnt] == '+' || strLowerMessage[intMsgCnt] == '-') - { + if (strLowerMessage[intMsgCnt] == '+' || strLowerMessage[intMsgCnt] == '-') { strEnChange = strLowerMessage.substr(intMsgCnt, strMsg.find(' ', intMsgCnt) - intMsgCnt); //û'/'ʱĬϳɹ仯ֵ - if (strEnChange.find('/') != std::string::npos) - { + if (strEnChange.find('/') != std::string::npos) { strEnFail = strEnChange.substr(0, strEnChange.find('/')); strEnSuc = strEnChange.substr(strEnChange.find('/') + 1); } @@ -2671,18 +2388,15 @@ int FromMsg::DiceReply() const int intTmpRollRes = RandomGenerator::Randint(1, 100); strVar["res"] = "1D100=" + to_string(intTmpRollRes) + "/" + to_string(*pVal) + " "; - if (intTmpRollRes <= *pVal && intTmpRollRes <= 95) - { - if (strEnFail.empty()) - { + if (intTmpRollRes <= *pVal && intTmpRollRes <= 95) { + if (strEnFail.empty()) { strVar["res"] += GlobalMsg["strFailure"]; reply(GlobalMsg["strEnRollNotChange"]); return 1; } strVar["res"] += GlobalMsg["strFailure"]; RD rdEnFail(strEnFail); - if (rdEnFail.Roll()) - { + if (rdEnFail.Roll()) { reply(GlobalMsg["strValueErr"]); return 1; } @@ -2696,8 +2410,7 @@ int FromMsg::DiceReply() } strVar["res"] += GlobalMsg["strSuccess"]; RD rdEnSuc(strEnSuc); - if (rdEnSuc.Roll()) - { + if (rdEnSuc.Roll()) { reply(GlobalMsg["strValueErr"]); return 1; } @@ -2709,141 +2422,110 @@ int FromMsg::DiceReply() reply(GlobalMsg["strEnRollSuccess"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "li") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "li") { string strAns = strVar["pc"] + "ķ-֢ܽ״:\n"; LongInsane(strAns); reply(strAns); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "me") - { - if (trusted < 4 && console["DisabledMe"]) - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "me") { + if (trusted < 4 && console["DisabledMe"]) { reply(GlobalMsg["strDisabledMeGlobal"]); return 1; } intMsgCnt += 2; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - if (intT == 0) - { + if (fromChat.second == msgtype::Private) { string strGroupID = readDigit(); - if (strGroupID.empty()) - { + if (strGroupID.empty()) { reply(GlobalMsg["strGroupIDEmpty"]); return 1; } const long long llGroupID = stoll(strGroupID); - if (groupset(llGroupID, "ָͣ") && trusted < 4) - { + if (groupset(llGroupID, "ָͣ") && trusted < 4) { reply(GlobalMsg["strDisabledErr"]); return 1; } - if (groupset(llGroupID, "me") && trusted < 5) - { + if (groupset(llGroupID, "me") && trusted < 5) { reply(GlobalMsg["strMEDisabledErr"]); return 1; } while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; string strAction = strip(readRest()); - if (strAction.empty()) - { + if (strAction.empty()) { reply(GlobalMsg["strActionEmpty"]); return 1; } string strReply = getName(fromQQ, llGroupID) + strAction; - const int intSendRes = sendGroupMsg(llGroupID, strReply); - if (intSendRes < 0) - { - reply(GlobalMsg["strSendErr"]); - } - else - { - reply(GlobalMsg["strSendSuccess"]); - } + DD::sendGroupMsg(llGroupID, strReply); + reply(GlobalMsg["strSendSuccess"]); return 1; } string strAction = strLowerMessage.substr(intMsgCnt); - if (!isAuth && (strAction == "on" || strAction == "off")) - { + if (!isAuth && (strAction == "on" || strAction == "off")) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } - if (strAction == "off") - { - if (groupset(fromGroup, "me") < 1) - { + if (strAction == "off") { + if (groupset(fromGroup, "me") < 1) { chat(fromGroup).set("me"); reply(GlobalMsg["strMeOff"]); } - else - { + else { reply(GlobalMsg["strMeOffAlready"]); } return 1; } - if (strAction == "on") - { - if (groupset(fromGroup, "me") > 0) - { + if (strAction == "on") { + if (groupset(fromGroup, "me") > 0) { chat(fromGroup).reset("me"); reply(GlobalMsg["strMeOn"]); } - else - { + else { reply(GlobalMsg["strMeOnAlready"]); } return 1; } - if (groupset(fromGroup, "me")) - { + if (groupset(fromGroup, "me")) { reply(GlobalMsg["strMEDisabledErr"]); return 1; } strAction = strip(readRest()); - if (strAction.empty()) - { + if (strAction.empty()) { reply(GlobalMsg["strActionEmpty"]); return 1; } trusted > 4 ? reply(strAction) : reply(strVar["pc"] + strAction); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "nn") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "nn") { intMsgCnt += 2; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; strVar["new_nick"] = strip(strMsg.substr(intMsgCnt)); filter_CQcode(strVar["new_nick"]); - if (strVar["new_nick"].length() > 50) - { + if (strVar["new_nick"].length() > 50) { reply(GlobalMsg["strNameTooLongErr"]); return 1; } if (!strVar["new_nick"].empty()) { getUser(fromQQ).setNick(fromGroup, strVar["new_nick"]); - reply(GlobalMsg["strNameSet"], {strVar["nick"], strVar["new_nick"]}); + reply(GlobalMsg["strNameSet"], { strVar["nick"], strVar["new_nick"] }); } - else - { - if (getUser(fromQQ).rmNick(fromGroup)) - { - reply(GlobalMsg["strNameClr"], {strVar["nick"]}); + else { + if (getUser(fromQQ).rmNick(fromGroup)) { + reply(GlobalMsg["strNameClr"], { strVar["nick"] }); } - else - { + else { reply(GlobalMsg["strNameDelEmpty"]); } } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "ob") - { - if (intT == PrivateT) - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "ob") { + if (fromChat.second == msgtype::Private) { reply(fmt->get_help("ob")); return 1; } @@ -2852,71 +2534,56 @@ int FromMsg::DiceReply() intMsgCnt++; const string strOption = strLowerMessage.substr(intMsgCnt, strMsg.find(' ', intMsgCnt) - intMsgCnt); - if (!isAuth && (strOption == "on" || strOption == "off")) - { + if (!isAuth && (strOption == "on" || strOption == "off")) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; } strVar["option"] = "ob"; - if (strOption == "off") - { - if (groupset(fromGroup, strVar["option"]) < 1) - { + if (strOption == "off") { + if (groupset(fromGroup, strVar["option"]) < 1) { chat(fromGroup).set(strVar["option"]); gm->session(fromSession).clear_ob(); reply(GlobalMsg["strObOff"]); } - else - { + else { reply(GlobalMsg["strObOffAlready"]); } return 1; } - if (strOption == "on") - { - if (groupset(fromGroup, strVar["option"]) > 0) - { + if (strOption == "on") { + if (groupset(fromGroup, strVar["option"]) > 0) { chat(fromGroup).reset(strVar["option"]); reply(GlobalMsg["strObOn"]); } - else - { + else { reply(GlobalMsg["strObOnAlready"]); } return 1; } - if (groupset(fromGroup, strVar["option"]) > 0) - { + if (groupset(fromGroup, strVar["option"]) > 0) { reply(GlobalMsg["strObOffAlready"]); return 1; } - if (strOption == "list") - { + if (strOption == "list") { gm->session(fromSession).ob_list(this); } - else if (strOption == "clr") - { - if (intT == DiscussT || getGroupMemberInfo(fromGroup, fromQQ).permissions >= 2) - { + else if (strOption == "clr") { + if (isAuth) { gm->session(fromSession).ob_clr(this); } - else - { + else { reply(GlobalMsg["strPermissionDeniedErr"]); } } - else if (strOption == "exit") - { + else if (strOption == "exit") { gm->session(fromSession).ob_exit(this); } - else - { + else { gm->session(fromSession).ob_enter(this); } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "pc") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "pc") { intMsgCnt += 2; string strOption = readPara(); if (strOption.empty()) { @@ -2924,11 +2591,9 @@ int FromMsg::DiceReply() return 1; } Player& pl = getPlayer(fromQQ); - if (strOption == "tag") - { + if (strOption == "tag") { strVar["char"] = readRest(); - switch (pl.changeCard(strVar["char"], fromGroup)) - { + switch (pl.changeCard(strVar["char"], fromGroup)) { case 1: reply(GlobalMsg["strPcCardReset"]); break; @@ -2944,8 +2609,7 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "show") - { + if (strOption == "show") { string strName = readRest(); strVar["char"] = pl.getCard(strName, fromGroup).Name; strVar["type"] = pl.getCard(strName, fromGroup).Type; @@ -2953,12 +2617,10 @@ int FromMsg::DiceReply() reply(GlobalMsg["strPcCardShow"]); return 1; } - if (strOption == "new") - { + if (strOption == "new") { strVar["char"] = strip(readRest()); filter_CQcode(strVar["char"]); - switch (pl.newCard(strVar["char"], fromGroup)) - { + switch (pl.newCard(strVar["char"], fromGroup)) { case 0: strVar["type"] = pl[fromGroup].Type; strVar["show"] = pl[fromGroup].show(true); @@ -2983,12 +2645,10 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "build") - { + if (strOption == "build") { strVar["char"] = strip(readRest()); filter_CQcode(strVar["char"]); - switch (pl.buildCard(strVar["char"], false, fromGroup)) - { + switch (pl.buildCard(strVar["char"], false, fromGroup)) { case 0: strVar["show"] = pl[strVar["char"]].show(true); reply(GlobalMsg["strPcCardBuild"]); @@ -3008,24 +2668,20 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "list") - { + if (strOption == "list") { strVar["show"] = pl.listCard(); reply(GlobalMsg["strPcCardList"]); return 1; } - if (strOption == "nn") - { + if (strOption == "nn") { strVar["new_name"] = strip(readRest()); filter_CQcode(strVar["char"]); - if (strVar["new_name"].empty()) - { + if (strVar["new_name"].empty()) { reply(GlobalMsg["strPCNameEmpty"]); return 1; } strVar["old_name"] = pl[fromGroup].Name; - switch (pl.renameCard(strVar["old_name"], strVar["new_name"])) - { + switch (pl.renameCard(strVar["old_name"], strVar["new_name"])) { case 0: reply(GlobalMsg["strPcCardRename"]); break; @@ -3041,11 +2697,9 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "del") - { + if (strOption == "del") { strVar["char"] = strip(readRest()); - switch (pl.removeCard(strVar["char"])) - { + switch (pl.removeCard(strVar["char"])) { case 0: reply(GlobalMsg["strPcCardDel"]); break; @@ -3061,30 +2715,26 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "redo") - { + if (strOption == "redo") { strVar["char"] = strip(readRest()); pl.buildCard(strVar["char"], true, fromGroup); strVar["show"] = pl[strVar["char"]].show(true); reply(GlobalMsg["strPcCardRedo"]); return 1; } - if (strOption == "grp") - { + if (strOption == "grp") { strVar["show"] = pl.listMap(); reply(GlobalMsg["strPcGroupList"]); return 1; } - if (strOption == "cpy") - { + if (strOption == "cpy") { string strName = strip(readRest()); filter_CQcode(strName); strVar["char1"] = strName.substr(0, strName.find('=')); strVar["char2"] = (strVar["char1"].length() < strName.length() - 1) - ? strip(strName.substr(strVar["char1"].length() + 1)) - : pl[fromGroup].Name; - switch (pl.copyCard(strVar["char1"], strVar["char2"], fromGroup)) - { + ? strip(strName.substr(strVar["char1"].length() + 1)) + : pl[fromGroup].Name; + switch (pl.copyCard(strVar["char1"], strVar["char2"], fromGroup)) { case 0: reply(GlobalMsg["strPcCardCpy"]); break; @@ -3103,8 +2753,7 @@ int FromMsg::DiceReply() } return 1; } - if (strOption == "clr") - { + if (strOption == "clr") { PList.erase(fromQQ); reply(GlobalMsg["strPcClr"]); return 1; @@ -3112,31 +2761,32 @@ int FromMsg::DiceReply() reply(fmt->get_help("pc")); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "ra" || strLowerMessage.substr(intMsgCnt, 2) == "rc") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "ra" || strLowerMessage.substr(intMsgCnt, 2) == "rc") { intMsgCnt += 2; - readSkipSpace(); if (strMsg.length() == intMsgCnt) { reply(fmt->get_help("rc")); return 1; } - int intRule = intT - ? get(chat(fromGroup).intConf, string("rc"), 0) - : get(getUser(fromQQ).intConf, string("rc"), 0); + int intRule = fromChat.second != msgtype::Private + ? get(chat(fromGroup).intConf, string("rc"), 0) + : get(getUser(fromQQ).intConf, string("rc"), 0); int intTurnCnt = 1; bool isHidden(false); - if (strMsg[intMsgCnt] == '_') { + if (strMsg[intMsgCnt] == 'h' && isspace(static_cast(strMsg[intMsgCnt + 1]))) { isHidden = true; ++intMsgCnt; } - if (strMsg.find('#') != string::npos) - { + else if (readSkipSpace(); strMsg[intMsgCnt] == '_') { + isHidden = true; + ++intMsgCnt; + } + readSkipSpace(); + if (strMsg.find('#') != string::npos) { string strTurnCnt = strMsg.substr(intMsgCnt, strMsg.find('#') - intMsgCnt); //#ܷʶЧ if (strTurnCnt.empty())intMsgCnt++; else if ((strTurnCnt.length() == 1 && isdigit(static_cast(strTurnCnt[0]))) || strTurnCnt == - "10") - { + "10") { intMsgCnt += strTurnCnt.length() + 1; intTurnCnt = stoi(strTurnCnt); } @@ -3153,12 +2803,10 @@ int FromMsg::DiceReply() int intSkillDivisor = 1; //Զɹ bool isAutomatic = false; - if ((strLowerMessage[intMsgCnt] == 'p' || strLowerMessage[intMsgCnt] == 'b') && strLowerMessage[intMsgCnt - 1] != ' ') - { + if ((strLowerMessage[intMsgCnt] == 'p' || strLowerMessage[intMsgCnt] == 'b') && strLowerMessage[intMsgCnt - 1] != ' ') { strMainDice = strLowerMessage[intMsgCnt]; intMsgCnt++; - while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) - { + while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) { strMainDice += strLowerMessage[intMsgCnt]; intMsgCnt++; } @@ -3168,179 +2816,149 @@ int FromMsg::DiceReply() isHidden = true; ++intMsgCnt; } - if (strMsg.length() == intMsgCnt) - { + if (strMsg.length() == intMsgCnt) { strVar["attr"] = GlobalMsg["strEnDefaultName"]; - reply(GlobalMsg["strUnknownPropErr"], {strVar["attr"]}); + reply(GlobalMsg["strUnknownPropErr"], { strVar["attr"] }); return 1; } strVar["attr"] = strMsg.substr(intMsgCnt); if (PList.count(fromQQ) && PList[fromQQ][fromGroup].count(strVar["attr"]))intMsgCnt = strMsg.length(); else strVar["attr"] = readAttrName(); - if (strVar["attr"].find("Զɹ") == 0) - { + if (strVar["attr"].find("Զɹ") == 0) { strDifficulty = strVar["attr"].substr(0, 8); strVar["attr"] = strVar["attr"].substr(8); isAutomatic = true; } - if (strVar["attr"].find("") == 0 || strVar["attr"].find("") == 0) - { + if (strVar["attr"].find("") == 0 || strVar["attr"].find("") == 0) { strDifficulty += strVar["attr"].substr(0, 4); intDifficulty = (strVar["attr"].substr(0, 4) == "") ? 2 : 5; strVar["attr"] = strVar["attr"].substr(4); } - if (strLowerMessage[intMsgCnt] == '*' && isdigit(strLowerMessage[intMsgCnt + 1])) - { + if (strLowerMessage[intMsgCnt] == '*' && isdigit(strLowerMessage[intMsgCnt + 1])) { intMsgCnt++; readNum(intSkillMultiple); } while ((strLowerMessage[intMsgCnt] == '+' || strLowerMessage[intMsgCnt] == '-') && isdigit( - strLowerMessage[intMsgCnt + 1])) - { + strLowerMessage[intMsgCnt + 1])) { if (!readNum(intSkillModify))strSkillModify = to_signed_string(intSkillModify); } - if (strLowerMessage[intMsgCnt] == '/' && isdigit(strLowerMessage[intMsgCnt + 1])) - { + if (strLowerMessage[intMsgCnt] == '/' && isdigit(strLowerMessage[intMsgCnt + 1])) { intMsgCnt++; readNum(intSkillDivisor); - if (intSkillDivisor == 0) - { + if (intSkillDivisor == 0) { reply(GlobalMsg["strValueErr"]); return 1; } } while (isspace(static_cast(strLowerMessage[intMsgCnt])) || strLowerMessage[intMsgCnt] == '=' || - strLowerMessage[intMsgCnt] == - ':') + strLowerMessage[intMsgCnt] == + ':') intMsgCnt++; string strSkillVal; - while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) - { + while (isdigit(static_cast(strLowerMessage[intMsgCnt]))) { strSkillVal += strLowerMessage[intMsgCnt]; intMsgCnt++; } - while (isspace(static_cast(strLowerMessage[intMsgCnt]))) - { + while (isspace(static_cast(strLowerMessage[intMsgCnt]))) { intMsgCnt++; } strVar["reason"] = readRest(); int intSkillVal; - if (strSkillVal.empty()) - { - if (PList.count(fromQQ) && PList[fromQQ][fromGroup].count(strVar["attr"])) - { + if (strSkillVal.empty()) { + if (PList.count(fromQQ) && PList[fromQQ][fromGroup].count(strVar["attr"])) { intSkillVal = PList[fromQQ][fromGroup].call(strVar["attr"]); } - else - { - if (!PList.count(fromQQ) && SkillNameReplace.count(strVar["attr"])) - { + else { + if (!PList.count(fromQQ) && SkillNameReplace.count(strVar["attr"])) { strVar["attr"] = SkillNameReplace[strVar["attr"]]; } - if (!PList.count(fromQQ) && SkillDefaultVal.count(strVar["attr"])) - { + if (!PList.count(fromQQ) && SkillDefaultVal.count(strVar["attr"])) { intSkillVal = SkillDefaultVal[strVar["attr"]]; } - else - { - reply(GlobalMsg["strUnknownPropErr"], {strVar["attr"]}); + else { + reply(GlobalMsg["strUnknownPropErr"], { strVar["attr"] }); return 1; } } } - else if (strSkillVal.length() > 3) - { + else if (strSkillVal.length() > 3) { reply(GlobalMsg["strPropErr"]); return 1; } - else - { + else { intSkillVal = stoi(strSkillVal); } int intFianlSkillVal = (intSkillVal * intSkillMultiple + intSkillModify) / intSkillDivisor / intDifficulty; - if (intFianlSkillVal < 0 || intFianlSkillVal > 1000) - { + if (intFianlSkillVal < 0 || intFianlSkillVal > 1000) { reply(GlobalMsg["strSuccessRateErr"]); return 1; } RD rdMainDice(strMainDice); const int intFirstTimeRes = rdMainDice.Roll(); - if (intFirstTimeRes == ZeroDice_Err) - { + if (intFirstTimeRes == ZeroDice_Err) { reply(GlobalMsg["strZeroDiceErr"]); return 1; } - if (intFirstTimeRes == DiceTooBig_Err) - { + if (intFirstTimeRes == DiceTooBig_Err) { reply(GlobalMsg["strDiceTooBigErr"]); return 1; } strVar["attr"] = strDifficulty + strVar["attr"] + ( (intSkillMultiple != 1) ? "" + to_string(intSkillMultiple) : "") + strSkillModify + ((intSkillDivisor != 1) - ? "/" + to_string( - intSkillDivisor) - : ""); - if (strVar["reason"].empty()) - { - strReply = format(GlobalMsg["strRollSkill"], {strVar["pc"], strVar["attr"]}); + ? "/" + to_string( + intSkillDivisor) + : ""); + if (strVar["reason"].empty()) { + strReply = format(GlobalMsg["strRollSkill"], { strVar["pc"], strVar["attr"] }); } - else strReply = format(GlobalMsg["strRollSkillReason"], {strVar["pc"], strVar["attr"], strVar["reason"]}); + else strReply = format(GlobalMsg["strRollSkillReason"], { strVar["pc"], strVar["attr"], strVar["reason"] }); ResList Res; string strAns; - if (intTurnCnt == 1) - { + if (intTurnCnt == 1) { rdMainDice.Roll(); strAns = rdMainDice.FormCompleteString() + "/" + to_string(intFianlSkillVal) + " "; int intRes = RollSuccessLevel(rdMainDice.intTotal, intFianlSkillVal, intRule); - switch (intRes) - { + switch (intRes) { case 0: strAns += GlobalMsg["strRollFumble"]; break; case 1: strAns += isAutomatic ? GlobalMsg["strRollRegularSuccess"] : GlobalMsg["strRollFailure"]; break; case 5: strAns += GlobalMsg["strRollCriticalSuccess"]; break; - case 4: if (intDifficulty == 1) - { - strAns += GlobalMsg["strRollExtremeSuccess"]; - break; - } - case 3: if (intDifficulty == 1) - { - strAns += GlobalMsg["strRollHardSuccess"]; - break; - } + case 4: if (intDifficulty == 1) { + strAns += GlobalMsg["strRollExtremeSuccess"]; + break; + } + case 3: if (intDifficulty == 1) { + strAns += GlobalMsg["strRollHardSuccess"]; + break; + } case 2: strAns += GlobalMsg["strRollRegularSuccess"]; break; } strReply += strAns; } - else - { + else { Res.dot("\n"); - while (intTurnCnt--) - { + while (intTurnCnt--) { rdMainDice.Roll(); strAns = rdMainDice.FormCompleteString() + "/" + to_string(intFianlSkillVal) + " "; int intRes = RollSuccessLevel(rdMainDice.intTotal, intFianlSkillVal, intRule); - switch (intRes) - { + switch (intRes) { case 0: strAns += GlobalMsg["strFumble"]; break; case 1: strAns += isAutomatic ? GlobalMsg["strSuccess"] : GlobalMsg["strFailure"]; break; case 5: strAns += GlobalMsg["strCriticalSuccess"]; break; - case 4: if (intDifficulty == 1) - { - strAns += GlobalMsg["strExtremeSuccess"]; - break; - } - case 3: if (intDifficulty == 1) - { - strAns += GlobalMsg["strHardSuccess"]; - break; - } + case 4: if (intDifficulty == 1) { + strAns += GlobalMsg["strExtremeSuccess"]; + break; + } + case 3: if (intDifficulty == 1) { + strAns += GlobalMsg["strHardSuccess"]; + break; + } case 2: strAns += GlobalMsg["strSuccess"]; break; } @@ -3356,17 +2974,17 @@ int FromMsg::DiceReply() reply(); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "ri" && intT) - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "ri") { + if (fromChat.second == msgtype::Private) { + reply(fmt->get_help("ri")); + return 1; + } intMsgCnt += 2; - readSkipSpace(); string strinit = "D20"; - if (strLowerMessage[intMsgCnt] == '+' || strLowerMessage[intMsgCnt] == '-') - { + if (strLowerMessage[intMsgCnt] == '+' || strLowerMessage[intMsgCnt] == '-') { strinit += readDice(); } - else if (isRollDice()) - { + else if (isRollDice()) { strinit = readDice(); } readSkipSpace(); @@ -3377,43 +2995,35 @@ int FromMsg::DiceReply() strname = strip(strname); RD initdice(strinit, 20); const int intFirstTimeRes = initdice.Roll(); - if (intFirstTimeRes == Value_Err) - { + if (intFirstTimeRes == Value_Err) { reply(GlobalMsg["strValueErr"]); return 1; } - if (intFirstTimeRes == Input_Err) - { + if (intFirstTimeRes == Input_Err) { reply(GlobalMsg["strInputErr"]); return 1; } - if (intFirstTimeRes == ZeroDice_Err) - { + if (intFirstTimeRes == ZeroDice_Err) { reply(GlobalMsg["strZeroDiceErr"]); return 1; } - if (intFirstTimeRes == ZeroType_Err) - { + if (intFirstTimeRes == ZeroType_Err) { reply(GlobalMsg["strZeroTypeErr"]); return 1; } - if (intFirstTimeRes == DiceTooBig_Err) - { + if (intFirstTimeRes == DiceTooBig_Err) { reply(GlobalMsg["strDiceTooBigErr"]); return 1; } - if (intFirstTimeRes == TypeTooBig_Err) - { + if (intFirstTimeRes == TypeTooBig_Err) { reply(GlobalMsg["strTypeTooBigErr"]); return 1; } - if (intFirstTimeRes == AddDiceVal_Err) - { + if (intFirstTimeRes == AddDiceVal_Err) { reply(GlobalMsg["strAddDiceValErr"]); return 1; } - if (intFirstTimeRes != 0) - { + if (intFirstTimeRes != 0) { reply(GlobalMsg["strUnknownErr"]); return 1; } @@ -3422,8 +3032,7 @@ int FromMsg::DiceReply() reply(strReply); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "sc") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "sc") { intMsgCnt += 2; string SanCost = readUntilSpace(); while (isspace(static_cast(strLowerMessage[intMsgCnt]))) @@ -3432,65 +3041,55 @@ int FromMsg::DiceReply() reply(fmt->get_help("sc")); return 1; } - if(SanCost.find('/') == string::npos){ + if (SanCost.find('/') == string::npos) { reply(GlobalMsg["strSanCostInvalid"]); return 1; } string attr = ""; int intSan = 0; auto* pSan = (short*)&intSan; - if (readNum(intSan)) - { - if (PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].count(attr)) - { + if (readNum(intSan)) { + if (PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].count(attr)) { pSan = &getPlayer(fromQQ)[fromGroup][attr]; } - else - { + else { reply(GlobalMsg["strSanEmpty"]); return 1; } } string strSanCostSuc = SanCost.substr(0, SanCost.find('/')); string strSanCostFail = SanCost.substr(SanCost.find('/') + 1); - for (const auto& character : strSanCostSuc) - { + for (const auto& character : strSanCostSuc) { if (!isdigit(static_cast(character)) && character != 'D' && character != 'd' && character != - '+' && character != '-') - { + '+' && character != '-') { reply(GlobalMsg["strSanCostInvalid"]); return 1; } } - for (const auto& character : SanCost.substr(SanCost.find('/') + 1)) - { + for (const auto& character : SanCost.substr(SanCost.find('/') + 1)) { if (!isdigit(static_cast(character)) && character != 'D' && character != 'd' && character != - '+' && character != '-') - { + '+' && character != '-') { reply(GlobalMsg["strSanCostInvalid"]); return 1; } } RD rdSuc(strSanCostSuc); RD rdFail(strSanCostFail); - if (rdSuc.Roll() != 0 || rdFail.Roll() != 0) - { + if (rdSuc.Roll() != 0 || rdFail.Roll() != 0) { reply(GlobalMsg["strSanCostInvalid"]); return 1; } - if (*pSan == 0) - { + if (*pSan == 0) { reply(GlobalMsg["strSanInvalid"]); return 1; } const int intTmpRollRes = RandomGenerator::Randint(1, 100); strVar["res"] = "1D100=" + to_string(intTmpRollRes) + "/" + to_string(*pSan) + " "; //÷ - int intRule = intT - ? get(chat(fromGroup).intConf, string("rc"), 0) - : get(getUser(fromQQ).intConf, string("rc"), 0); - switch (RollSuccessLevel(intTmpRollRes, *pSan, intRule)) - { + int intRule = fromGroup + ? get(chat(fromGroup).intConf, string("rc"), 0) + : get(getUser(fromQQ).intConf, string("rc"), 0); + switch (RollSuccessLevel(intTmpRollRes, *pSan, intRule)) { case 5: case 4: case 3: @@ -3515,75 +3114,62 @@ int FromMsg::DiceReply() reply(GlobalMsg["strSanRollRes"]); return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "st") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "st") { intMsgCnt += 2; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - if (intMsgCnt == strLowerMessage.length()) - { + if (intMsgCnt == strLowerMessage.length()) { reply(fmt->get_help("st")); return 1; } - if (strLowerMessage.substr(intMsgCnt, 3) == "clr") - { - if (!PList.count(fromQQ)) - { + if (strLowerMessage.substr(intMsgCnt, 3) == "clr") { + if (!PList.count(fromQQ)) { reply(getMsg("strPcNotExistErr")); return 1; } getPlayer(fromQQ)[fromGroup].clear(); strVar["char"] = getPlayer(fromQQ)[fromGroup].Name; - reply(GlobalMsg["strPropCleared"], {strVar["char"]}); + reply(GlobalMsg["strPropCleared"], { strVar["char"] }); return 1; } - if (strLowerMessage.substr(intMsgCnt, 3) == "del") - { - if (!PList.count(fromQQ)) - { + if (strLowerMessage.substr(intMsgCnt, 3) == "del") { + if (!PList.count(fromQQ)) { reply(getMsg("strPcNotExistErr")); return 1; } intMsgCnt += 3; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - if (strMsg[intMsgCnt] == '&') - { + if (strMsg[intMsgCnt] == '&') { intMsgCnt++; } strVar["attr"] = readAttrName(); - if (getPlayer(fromQQ)[fromGroup].erase(strVar["attr"])) - { - reply(GlobalMsg["strPropDeleted"], {strVar["pc"], strVar["attr"]}); + if (getPlayer(fromQQ)[fromGroup].erase(strVar["attr"])) { + reply(GlobalMsg["strPropDeleted"], { strVar["pc"], strVar["attr"] }); } - else - { - reply(GlobalMsg["strPropNotFound"], {strVar["attr"]}); + else { + reply(GlobalMsg["strPropNotFound"], { strVar["attr"] }); } return 1; } CharaCard& pc = getPlayer(fromQQ)[fromGroup]; - if (strLowerMessage.substr(intMsgCnt, 4) == "show") - { + if (strLowerMessage.substr(intMsgCnt, 4) == "show") { intMsgCnt += 4; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; strVar["attr"] = readAttrName(); - if (strVar["attr"].empty()) - { + if (strVar["attr"].empty()) { strVar["char"] = pc.Name; strVar["type"] = pc.Type; strVar["show"] = pc.show(false); reply(GlobalMsg["strPropList"]); return 1; } - if (pc.show(strVar["attr"], strVar["val"]) > -1) - { - reply(format(GlobalMsg["strProp"], {strVar["pc"], strVar["attr"], strVar["val"]})); + if (pc.show(strVar["attr"], strVar["val"]) > -1) { + reply(format(GlobalMsg["strProp"], { strVar["pc"], strVar["attr"], strVar["val"] })); } - else - { - reply(GlobalMsg["strPropNotFound"], {strVar["attr"]}); + else { + reply(GlobalMsg["strPropNotFound"], { strVar["attr"] }); } return 1; } @@ -3591,44 +3177,36 @@ int FromMsg::DiceReply() bool isDetail = false; bool isModify = false; //ѭ¼ - while (intMsgCnt != strLowerMessage.length()) - { + while (intMsgCnt != strLowerMessage.length()) { //ȡ readSkipSpace(); - if (strMsg[intMsgCnt] == '&') - { + if (strMsg[intMsgCnt] == '&') { intMsgCnt++; strVar["attr"] = readToColon(); - if (pc.setExp(strVar["attr"], readExp())) - { + if (pc.setExp(strVar["attr"], readExp())) { reply(GlobalMsg["strPcTextTooLong"]); return 1; } continue; } string strSkillName = readAttrName(); - if (strSkillName.empty()) - { - strVar["val"] = readDigit(); + if (strSkillName.empty()) { + strVar["val"] = readDigit(false); continue; } - if (pc.pTemplet->sInfoList.count(strSkillName)) - { + if (pc.pTemplet->sInfoList.count(strSkillName)) { while (isspace(static_cast(strLowerMessage[intMsgCnt])) || strLowerMessage[intMsgCnt] == - '=' || strLowerMessage[intMsgCnt] == ':')intMsgCnt++; - if (pc.setInfo(strSkillName, readUntilTab())) - { + '=' || strLowerMessage[intMsgCnt] == ':')intMsgCnt++; + if (pc.setInfo(strSkillName, readUntilTab())) { reply(GlobalMsg["strPcTextTooLong"]); return 1; } continue; } - if (strSkillName == "note") - { + if (strSkillName == "note") { while (isspace(static_cast(strLowerMessage[intMsgCnt])) || strLowerMessage[intMsgCnt] == - '=' || strLowerMessage[intMsgCnt] == ':')intMsgCnt++; - if (pc.setNote(readRest())) - { + '=' || strLowerMessage[intMsgCnt] == ':')intMsgCnt++; + if (pc.setNote(readRest())) { reply(GlobalMsg["strPcNoteTooLong"]); return 1; } @@ -3636,36 +3214,31 @@ int FromMsg::DiceReply() } //ȡֵ readSkipSpace(); - if ((strLowerMessage[intMsgCnt] == '-' || strLowerMessage[intMsgCnt] == '+')) - { + if ((strLowerMessage[intMsgCnt] == '-' || strLowerMessage[intMsgCnt] == '+')) { isDetail = true; isModify = true; short& nVal = pc[strSkillName]; RD Mod((nVal == 0 ? "" : to_string(nVal)) + readDice()); - if (Mod.Roll()) - { + if (Mod.Roll()) { reply(GlobalMsg["strValueErr"]); return 1; } strReply += "\n" + strSkillName + "" + Mod.FormCompleteString(); - if (Mod.intTotal < -32767) - { + if (Mod.intTotal < -32767) { strReply += "-32767"; nVal = -32767; } - else if (Mod.intTotal > 32767) - { + else if (Mod.intTotal > 32767) { strReply += "32767"; nVal = 32767; } else nVal = Mod.intTotal; while (isspace(static_cast(strLowerMessage[intMsgCnt])) || strLowerMessage[intMsgCnt] == - '|')intMsgCnt++; + '|')intMsgCnt++; continue; } string strSkillVal = readDigit(); - if (strSkillName.empty() || strSkillVal.empty() || strSkillVal.length() > 5) - { + if (strSkillName.empty() || strSkillVal.empty() || strSkillVal.length() > 5) { boolError = true; break; } @@ -3675,144 +3248,118 @@ int FromMsg::DiceReply() while (isspace(static_cast(strLowerMessage[intMsgCnt])) || strLowerMessage[intMsgCnt] == '|') intMsgCnt++; } - if (boolError) - { + if (boolError) { reply(GlobalMsg["strPropErr"]); } - else if (isModify) - { - reply(format(GlobalMsg["strStModify"], {strVar["pc"]}) + strReply); + else if (isModify) { + reply(format(GlobalMsg["strStModify"], { strVar["pc"] }) + strReply); } - else - { + else { reply(GlobalMsg["strSetPropSuccess"]); } return 1; } - else if (strLowerMessage.substr(intMsgCnt, 2) == "ti") - { + else if (strLowerMessage.substr(intMsgCnt, 2) == "ti") { string strAns = strVar["pc"] + "ķ-ʱ֢״:\n"; TempInsane(strAns); reply(strAns); return 1; } - else if (strLowerMessage[intMsgCnt] == 'w') - { + else if (strLowerMessage[intMsgCnt] == 'w') { intMsgCnt++; bool boolDetail = false; - if (strLowerMessage[intMsgCnt] == 'w') - { + if (strLowerMessage[intMsgCnt] == 'w') { intMsgCnt++; boolDetail = true; } bool isHidden = false; - if (strLowerMessage[intMsgCnt] == 'h') - { + if (strLowerMessage[intMsgCnt] == 'h') { isHidden = true; intMsgCnt += 1; } - if (intT == 0)isHidden = false; + if (!fromGroup)isHidden = false; string strMainDice = readDice(); readSkipSpace(); strVar["reason"] = strMsg.substr(intMsgCnt); int intTurnCnt = 1; const int intDefaultDice = get(getUser(fromQQ).intConf, string("Ĭ"), 100); - if (strMainDice.find('#') != string::npos) - { + if (strMainDice.find('#') != string::npos) { string strTurnCnt = strMainDice.substr(0, strMainDice.find('#')); if (strTurnCnt.empty()) strTurnCnt = "1"; strMainDice = strMainDice.substr(strMainDice.find('#') + 1); RD rdTurnCnt(strTurnCnt, intDefaultDice); const int intRdTurnCntRes = rdTurnCnt.Roll(); - if (intRdTurnCntRes != 0) - { - if (intRdTurnCntRes == Value_Err) - { + if (intRdTurnCntRes != 0) { + if (intRdTurnCntRes == Value_Err) { reply(GlobalMsg["strValueErr"]); return 1; } - if (intRdTurnCntRes == Input_Err) - { + if (intRdTurnCntRes == Input_Err) { reply(GlobalMsg["strInputErr"]); return 1; } - if (intRdTurnCntRes == ZeroDice_Err) - { + if (intRdTurnCntRes == ZeroDice_Err) { reply(GlobalMsg["strZeroDiceErr"]); return 1; } - if (intRdTurnCntRes == ZeroType_Err) - { + if (intRdTurnCntRes == ZeroType_Err) { reply(GlobalMsg["strZeroTypeErr"]); return 1; } - if (intRdTurnCntRes == DiceTooBig_Err) - { + if (intRdTurnCntRes == DiceTooBig_Err) { reply(GlobalMsg["strDiceTooBigErr"]); return 1; } - if (intRdTurnCntRes == TypeTooBig_Err) - { + if (intRdTurnCntRes == TypeTooBig_Err) { reply(GlobalMsg["strTypeTooBigErr"]); return 1; } - if (intRdTurnCntRes == AddDiceVal_Err) - { + if (intRdTurnCntRes == AddDiceVal_Err) { reply(GlobalMsg["strAddDiceValErr"]); return 1; } reply(GlobalMsg["strUnknownErr"]); return 1; } - if (rdTurnCnt.intTotal > 10) - { + if (rdTurnCnt.intTotal > 10) { reply(GlobalMsg["strRollTimeExceeded"]); return 1; } - if (rdTurnCnt.intTotal <= 0) - { + if (rdTurnCnt.intTotal <= 0) { reply(GlobalMsg["strRollTimeErr"]); return 1; } intTurnCnt = rdTurnCnt.intTotal; - if (strTurnCnt.find('d') != string::npos) - { + if (strTurnCnt.find('d') != string::npos) { string strTurnNotice = strVar["pc"] + ": " + rdTurnCnt.FormShortString() + ""; - if (!isHidden || intT == PrivateT) - { + if (!isHidden) { reply(strTurnNotice); } - else - { + else { strTurnNotice = "" + printChat(fromChat) + " " + strTurnNotice; AddMsgToQueue(strTurnNotice, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) - { - if (qq != fromQQ) - { + for (auto qq : gm->session(fromSession).get_ob()) { + if (qq != fromQQ) { AddMsgToQueue(strTurnNotice, qq, msgtype::Private); } } } } } - if (strMainDice.empty()) - { + if (strMainDice.empty()) { reply(GlobalMsg["strEmptyWWDiceErr"]); return 1; } string strFirstDice = strMainDice.substr(0, strMainDice.find('+') < strMainDice.find('-') - ? strMainDice.find('+') - : strMainDice.find('-')); + ? strMainDice.find('+') + : strMainDice.find('-')); strFirstDice = strFirstDice.substr(0, strFirstDice.find('x') < strFirstDice.find('*') - ? strFirstDice.find('x') - : strFirstDice.find('*')); + ? strFirstDice.find('x') + : strFirstDice.find('*')); bool boolAdda10 = true; - for (auto i : strFirstDice) - { - if (!isdigit(static_cast(i))) - { + for (auto i : strFirstDice) { + if (!isdigit(static_cast(i))) { boolAdda10 = false; break; } @@ -3822,54 +3369,44 @@ int FromMsg::DiceReply() RD rdMainDice(strMainDice, intDefaultDice); const int intFirstTimeRes = rdMainDice.Roll(); - if (intFirstTimeRes != 0) - { - if (intFirstTimeRes == Value_Err) - { + if (intFirstTimeRes != 0) { + if (intFirstTimeRes == Value_Err) { reply(GlobalMsg["strValueErr"]); return 1; } - if (intFirstTimeRes == Input_Err) - { + if (intFirstTimeRes == Input_Err) { reply(GlobalMsg["strInputErr"]); return 1; } - if (intFirstTimeRes == ZeroDice_Err) - { + if (intFirstTimeRes == ZeroDice_Err) { reply(GlobalMsg["strZeroDiceErr"]); return 1; } - if (intFirstTimeRes == ZeroType_Err) - { + if (intFirstTimeRes == ZeroType_Err) { reply(GlobalMsg["strZeroTypeErr"]); return 1; } - if (intFirstTimeRes == DiceTooBig_Err) - { + if (intFirstTimeRes == DiceTooBig_Err) { reply(GlobalMsg["strDiceTooBigErr"]); return 1; } - if (intFirstTimeRes == TypeTooBig_Err) - { + if (intFirstTimeRes == TypeTooBig_Err) { reply(GlobalMsg["strTypeTooBigErr"]); return 1; } - if (intFirstTimeRes == AddDiceVal_Err) - { + if (intFirstTimeRes == AddDiceVal_Err) { reply(GlobalMsg["strAddDiceValErr"]); return 1; } reply(GlobalMsg["strUnknownErr"]); return 1; } - if (!boolDetail && intTurnCnt != 1) - { + if (!boolDetail && intTurnCnt != 1) { if (strVar["reason"].empty())strReply = GlobalMsg["strRollMuiltDice"]; else strReply = GlobalMsg["strRollMuiltDiceReason"]; vector vintExVal; strVar["res"] = "{ "; - while (intTurnCnt--) - { + while (intTurnCnt--) { // ˴ֵ // ReSharper disable once CppExpressionWithoutSideEffects rdMainDice.Roll(); @@ -3877,107 +3414,87 @@ int FromMsg::DiceReply() if (intTurnCnt != 0) strVar["res"] = ","; if ((rdMainDice.strDice == "D100" || rdMainDice.strDice == "1D100") && (rdMainDice.intTotal <= 5 || - rdMainDice.intTotal >= 96)) + rdMainDice.intTotal >= 96)) vintExVal.push_back(rdMainDice.intTotal); } strVar["res"] += " }"; - if (!vintExVal.empty()) - { + if (!vintExVal.empty()) { strVar["res"] += ",ֵ: "; - for (auto it = vintExVal.cbegin(); it != vintExVal.cend(); ++it) - { + for (auto it = vintExVal.cbegin(); it != vintExVal.cend(); ++it) { strVar["res"] += to_string(*it); if (it != vintExVal.cend() - 1)strVar["res"] += ","; } } - if (!isHidden || intT == PrivateT) - { + if (!isHidden) { reply(); } - else - { + else { strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) - { - if (qq != fromQQ) - { + for (auto qq : gm->session(fromSession).get_ob()) { + if (qq != fromQQ) { AddMsgToQueue(strReply, qq, msgtype::Private); } } } } - else - { - while (intTurnCnt--) - { + else { + while (intTurnCnt--) { // ˴ֵ // ReSharper disable once CppExpressionWithoutSideEffects rdMainDice.Roll(); strVar["res"] = boolDetail ? rdMainDice.FormCompleteString() : rdMainDice.FormShortString(); if (strVar["reason"].empty()) - strReply = format(GlobalMsg["strRollDice"], {strVar["pc"], strVar["res"]}); - else strReply = format(GlobalMsg["strRollDiceReason"], {strVar["pc"], strVar["res"], strVar["reason"]}); - if (!isHidden || intT == PrivateT) - { + strReply = format(GlobalMsg["strRollDice"], { strVar["pc"], strVar["res"] }); + else strReply = format(GlobalMsg["strRollDiceReason"], { strVar["pc"], strVar["res"], strVar["reason"] }); + if (!isHidden) { reply(); } - else - { + else { strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) - { - if (qq != fromQQ) - { + for (auto qq : gm->session(fromSession).get_ob()) { + if (qq != fromQQ) { AddMsgToQueue(strReply, qq, msgtype::Private); } } } } } - if (isHidden) - { - reply(GlobalMsg["strRollHidden"], {strVar["pc"]}); + if (isHidden) { + reply(GlobalMsg["strRollHidden"], { strVar["pc"] }); } return 1; } - else if (strLowerMessage[intMsgCnt] == 'r' || strLowerMessage[intMsgCnt] == 'h') - { + else if (strLowerMessage[intMsgCnt] == 'r' || strLowerMessage[intMsgCnt] == 'h') { bool isHidden = false; if (strLowerMessage[intMsgCnt] == 'h') isHidden = true; intMsgCnt += 1; bool boolDetail = true; - if (strMsg[intMsgCnt] == 's') - { + if (strMsg[intMsgCnt] == 's') { boolDetail = false; intMsgCnt++; } - if (strLowerMessage[intMsgCnt] == 'h') - { + if (strLowerMessage[intMsgCnt] == 'h') { isHidden = true; intMsgCnt += 1; } - if (intT == 0)isHidden = false; + if (!fromGroup)isHidden = false; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; string strMainDice; strVar["reason"] = strMsg.substr(intMsgCnt); - if (PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].countExp(strMsg.substr(intMsgCnt))) - { + if (PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].countExp(strMsg.substr(intMsgCnt))) { strMainDice = getPlayer(fromQQ)[fromGroup].getExp(strVar["reason"]); } - else - { + else { strMainDice = readDice(); bool isExp = false; - for (auto ch : strMainDice) - { - if (!isdigit(ch)) - { + for (auto ch : strMainDice) { + if (!isdigit(ch)) { isExp = true; break; } @@ -3987,16 +3504,14 @@ int FromMsg::DiceReply() } int intTurnCnt = 1; const int intDefaultDice = get(getUser(fromQQ).intConf, string("Ĭ"), 100); - if (strMainDice.find('#') != string::npos) - { + if (strMainDice.find('#') != string::npos) { strVar["turn"] = strMainDice.substr(0, strMainDice.find('#')); if (strVar["turn"].empty()) strVar["turn"] = "1"; strMainDice = strMainDice.substr(strMainDice.find('#') + 1); RD rdTurnCnt(strVar["turn"], intDefaultDice); const int intRdTurnCntRes = rdTurnCnt.Roll(); - switch (intRdTurnCntRes) - { + switch (intRdTurnCntRes) { case 0: break; case Value_Err: reply(GlobalMsg["strValueErr"]); @@ -4023,47 +3538,38 @@ int FromMsg::DiceReply() reply(GlobalMsg["strUnknownErr"]); return 1; } - if (rdTurnCnt.intTotal > 10) - { + if (rdTurnCnt.intTotal > 10) { reply(GlobalMsg["strRollTimeExceeded"]); return 1; } - if (rdTurnCnt.intTotal <= 0) - { + if (rdTurnCnt.intTotal <= 0) { reply(GlobalMsg["strRollTimeErr"]); return 1; } intTurnCnt = rdTurnCnt.intTotal; - if (strVar["turn"].find('d') != string::npos) - { + if (strVar["turn"].find('d') != string::npos) { strVar["turn"] = rdTurnCnt.FormShortString(); - if (!isHidden) - { - reply(GlobalMsg["strRollTurn"], {strVar["pc"], strVar["turn"]}); + if (!isHidden) { + reply(GlobalMsg["strRollTurn"], { strVar["pc"], strVar["turn"] }); } - else - { + else { strReply = format("" + printChat(fromChat) + " " + GlobalMsg["strRollTurn"], GlobalMsg, strVar); AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) - { - if (qq != fromQQ) - { + for (auto qq : gm->session(fromSession).get_ob()) { + if (qq != fromQQ) { AddMsgToQueue(strReply, qq, msgtype::Private); } } } } } - if (strMainDice.empty() && PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].countExp(strVar["reason"])) - { + if (strMainDice.empty() && PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].countExp(strVar["reason"])) { strMainDice = getPlayer(fromQQ)[fromGroup].getExp(strVar["reason"]); } RD rdMainDice(strMainDice, intDefaultDice); const int intFirstTimeRes = rdMainDice.Roll(); - switch (intFirstTimeRes) - { + switch (intFirstTimeRes) { case 0: break; case Value_Err: reply(GlobalMsg["strValueErr"]); @@ -4092,15 +3598,13 @@ int FromMsg::DiceReply() } strVar["dice_exp"] = rdMainDice.strDice; string strType = (intTurnCnt != 1 - ? (strVar["reason"].empty() ? "strRollMultiDice" : "strRollMultiDiceReason") - : (strVar["reason"].empty() ? "strRollDice" : "strRollDiceReason")); - if (!boolDetail && intTurnCnt != 1) - { + ? (strVar["reason"].empty() ? "strRollMultiDice" : "strRollMultiDiceReason") + : (strVar["reason"].empty() ? "strRollDice" : "strRollDiceReason")); + if (!boolDetail && intTurnCnt != 1) { strReply = GlobalMsg[strType]; vector vintExVal; strVar["res"] = "{ "; - while (intTurnCnt--) - { + while (intTurnCnt--) { // ˴ֵ // ReSharper disable once CppExpressionWithoutSideEffects rdMainDice.Roll(); @@ -4108,49 +3612,39 @@ int FromMsg::DiceReply() if (intTurnCnt != 0) strVar["res"] += ","; if ((rdMainDice.strDice == "D100" || rdMainDice.strDice == "1D100") && (rdMainDice.intTotal <= 5 || - rdMainDice.intTotal >= 96)) + rdMainDice.intTotal >= 96)) vintExVal.push_back(rdMainDice.intTotal); } strVar["res"] += " }"; - if (!vintExVal.empty()) - { + if (!vintExVal.empty()) { strVar["res"] += ",ֵ: "; - for (auto it = vintExVal.cbegin(); it != vintExVal.cend(); ++it) - { + for (auto it = vintExVal.cbegin(); it != vintExVal.cend(); ++it) { strVar["res"] += to_string(*it); if (it != vintExVal.cend() - 1) strVar["res"] += ","; } } - if (!isHidden) - { + if (!isHidden) { reply(); } - else - { + else { strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) - { - if (qq != fromQQ) - { + for (auto qq : gm->session(fromSession).get_ob()) { + if (qq != fromQQ) { AddMsgToQueue(strReply, qq, msgtype::Private); } } } } - else - { + else { ResList dices; - if (intTurnCnt > 1) - { - while (intTurnCnt--) - { + if (intTurnCnt > 1) { + while (intTurnCnt--) { rdMainDice.Roll(); string strForm = to_string(rdMainDice.intTotal); - if (boolDetail) - { + if (boolDetail) { string strCombined = rdMainDice.FormStringCombined(); string strSeparate = rdMainDice.FormStringSeparate(); if (strCombined != strForm)strForm = strCombined + "=" + strForm; @@ -4162,28 +3656,23 @@ int FromMsg::DiceReply() strVar["res"] = dices.dot(", ").line(7).show(); } else strVar["res"] = boolDetail ? rdMainDice.FormCompleteString() : rdMainDice.FormShortString(); - strReply = format(GlobalMsg[strType], {strVar["pc"], strVar["res"], strVar["reason"]}); - if (!isHidden) - { + strReply = format(GlobalMsg[strType], { strVar["pc"], strVar["res"], strVar["reason"] }); + if (!isHidden) { reply(); } - else - { + else { strReply = format(strReply, GlobalMsg, strVar); strReply = "" + printChat(fromChat) + " " + strReply; AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) - { - if (qq != fromQQ) - { + for (auto qq : gm->session(fromSession).get_ob()) { + if (qq != fromQQ) { AddMsgToQueue(strReply, qq, msgtype::Private); } } } } - if (isHidden) - { - reply(GlobalMsg["strRollHidden"], {strVar["pc"]}); + if (isHidden) { + reply(GlobalMsg["strRollHidden"], { strVar["pc"] }); } return 1; } @@ -4196,14 +3685,8 @@ int FromMsg::CustomReply() if (auto deck = CardDeck::mReplyDeck.find(strKey); deck != CardDeck::mReplyDeck.end() || (!isDisabled && (deck = CardDeck::mReplyDeck.find(strMsg)) != CardDeck::mReplyDeck.end())) { - if (strVar.empty()) - { - strVar["nick"] = getName(fromQQ, fromGroup); - getPCName(*this); - strVar["at"] = fromChat.second != msgtype::Private ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; - } reply(CardDeck::drawCard(deck->second, true)); - AddFrq(fromQQ, fromTime, fromChat); + if(!isVirtual)AddFrq(fromQQ, fromTime, fromChat); return 1; } return 0; @@ -4216,7 +3699,7 @@ bool FromMsg::DiceFilter() strMsg.erase(strMsg.begin()); init(strMsg); bool isOtherCalled = false; - string strAt = CQ_AT + to_string(getLoginQQ()) + "]"; + string strAt = CQ_AT + to_string(DD::getLoginQQ()) + "]"; while (strMsg.find(CQ_AT) == 0) { if (strMsg.find(strAt) == 0) @@ -4245,8 +3728,8 @@ bool FromMsg::DiceFilter() trusted = trustedQQ(fromQQ); fwdMsg(); if (fromChat.second == msgtype::Private) isCalled = true; - isDisabled = (console["DisabledGlobal"] && (trusted < 4 || !isCalled)) || (!(isCalled && console["DisabledListenAt"]) && groupset(fromGroup, "ЭЧ") > 0); - if (DiceReply()) + isDisabled = ((console["DisabledGlobal"] && trusted < 4) || groupset(fromGroup, "ЭЧ") > 0); + if (BasicOrder()) { if (isAns) { @@ -4256,11 +3739,71 @@ bool FromMsg::DiceFilter() if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); return 1; } - if (groupset(fromGroup, "ûظ") < 1 && !isDisabled && CustomReply())return true; + //С4ûдʼ + if (trusted < 4) { + unordered_setsens_words; + switch (int danger = censor.search(strMsg, sens_words) - 1) { + case 3: + if (trusted < danger++) { + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b1000, + printTTime(fromTime)); + reply(GlobalMsg["strCensorDanger"]); + return 1; + } + case 2: + if (trusted < danger++) { + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b10, + printTTime(fromTime)); + reply(GlobalMsg["strCensorWarning"]); + break; + } + case 1: + if (trusted < danger++) { + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 0b10, + printTTime(fromTime)); + reply(GlobalMsg["strCensorCaution"]); + break; + } + case 0: + console.log(":" + printQQ(fromQQ) + "" + GlobalMsg["strSelfName"] + "˺дָ:\n" + strMsg, 1, + printTTime(fromTime)); + break; + default: + break; + } + } + if (fromChat.second == msgtype::Group && ((console["CheckGroupLicense"] > 0 && pGrp->isset("δ")) + || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ")) + || blacklist->get_group_danger(fromGroup))) { + isDisabled = true; + } + if (blacklist->get_qq_danger(fromQQ))isDisabled = true; + if (!isDisabled && (isCalled || !pGrp->isset("ָͣ"))) { + if ((fmt->listen_order(this) || InnerOrder()) && !isVirtual) { + AddFrq(fromQQ, fromTime, fromChat); + getUser(fromQQ).update(fromTime); + if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); + return true; + } + } + if (!isDisabled && (isCalled || !pGrp->isset("ûظ")) && CustomReply())return true; if (isDisabled)return console["DisabledBlock"]; return false; } +void FromMsg::operator()() { + isVirtual = true; + DiceFilter(); + delete this; +} + +int FromMsg::getGroupAuth(long long group) { + if (trusted > 0)return trusted; + if (ChatList.count(group)) { + return DD::isGroupAdmin(group, fromQQ, true) ? 0 : -1; + } + return -2; +} void FromMsg::readSkipColon() { readSkipSpace(); while (intMsgCnt < strMsg.length() && (strMsg[intMsgCnt] == ':' || strMsg[intMsgCnt] == '='))intMsgCnt++; @@ -4302,15 +3845,15 @@ int FromMsg::readChat(chatType& ct, bool isReroll) } else if (strT == "qq") { - ct.second = CQ::msgtype::Private; + ct.second = msgtype::Private; } else if (strT == "group") { - ct.second = CQ::msgtype::Group; + ct.second = msgtype::Group; } else if (strT == "discuss") { - ct.second = CQ::msgtype::Discuss; + ct.second = msgtype::Discuss; } else { diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index bdc8c384..30b8a970 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -8,7 +8,6 @@ #include #include #include -#include "CQAPI_EX.h" #include "MsgMonitor.h" #include "DiceSchedule.h" #include "DiceMsgSend.h" @@ -24,10 +23,10 @@ class FromMsg : public DiceJobDetail { long long fromSession; Chat* pGrp = nullptr; string strReply; - FromMsg(std::string message, long long qq) :DiceJobDetail(qq, { qq,CQ::msgtype::Private }, message){ + FromMsg(std::string message, long long qq) :DiceJobDetail(qq, { qq,msgtype::Private }, message){ fromSession = ~fromQQ; } - FromMsg(std::string message, long long fromGroup, CQ::msgtype msgType, long long qq) :DiceJobDetail(qq, { fromGroup,msgType }, message), fromGroup(fromGroup), fromSession(fromGroup){ + FromMsg(std::string message, long long fromGroup, msgtype msgType, long long qq) :DiceJobDetail(qq, { fromGroup,msgType }, message), fromGroup(fromGroup), fromSession(fromGroup){ pGrp = &chat(fromGroup); } @@ -57,7 +56,7 @@ class FromMsg : public DiceJobDetail { const string note = getName(fromQQ) + strMsg; for (const auto& [ct,level] : console.NoticeList) { - if (!(level & note_lv) || pair(fromQQ, CQ::msgtype::Private) == ct || ct == fromChat)continue; + if (!(level & note_lv) || pair(fromQQ, msgtype::Private) == ct || ct == fromChat)continue; AddMsgToQueue(note, ct); } } @@ -66,8 +65,8 @@ class FromMsg : public DiceJobDetail { std::string printFrom() { std::string strFwd; - if (fromChat.second == CQ::msgtype::Group)strFwd += "[Ⱥ:" + to_string(fromGroup) + "]"; - if (fromChat.second == CQ::msgtype::Discuss)strFwd += "[:" + to_string(fromGroup) + "]"; + if (fromChat.second == msgtype::Group)strFwd += "[Ⱥ:" + to_string(fromGroup) + "]"; + if (fromChat.second == msgtype::Discuss)strFwd += "[:" + to_string(fromGroup) + "]"; strFwd += getName(fromQQ, fromGroup) + "(" + to_string(fromQQ) + "):"; return strFwd; } @@ -76,30 +75,24 @@ class FromMsg : public DiceJobDetail { void fwdMsg(); int AdminEvent(const string& strOption); int MasterSet(); - int DiceReply(); + int BasicOrder(); + int InnerOrder(); + //int CustomOrder(); int CustomReply(); //жǷӦ bool DiceFilter(); + void operator()(); short trusted = 0; private: + bool isVirtual = false; //ǷӦ bool isAns = false; bool isDisabled = false; bool isCalled = false; bool isAuth = false; - short getGroupAuth(long long group = 0) - { - if (trusted > 0)return trusted; - if (ChatList.count(group)) - { - const int per = CQ::getGroupMemberInfo(group, fromQQ).permissions; - if (per > 1)return 0; - if (per)return -1; - } - return -2; - } + int getGroupAuth(long long group = 0); public: unsigned int intMsgCnt = 0; //ո diff --git a/Dice/DiceFile.cpp b/Dice/DiceFile.cpp index d5ed3bc9..9cb32b99 100644 --- a/Dice/DiceFile.cpp +++ b/Dice/DiceFile.cpp @@ -127,6 +127,31 @@ void fwrite(ofstream& fout, const std::string& s) fout.write(reinterpret_cast(&len), sizeof(short)); fout.write(s.c_str(), sizeof(char) * s.length()); } +void fwrite(ofstream& fout, const var& val) { + short idx(-1); + switch (val.index()) { + case 0: + fout.write(reinterpret_cast(&idx), sizeof(short)); + break; + case 1: { + idx = -2; + fout.write(reinterpret_cast(&idx), sizeof(short)); + int i = std::get(val); + fout.write(reinterpret_cast(&i), sizeof(int)); + break; + } + case 2: { + idx = -3; + fout.write(reinterpret_cast(&idx), sizeof(short)); + double f = std::get(val); + fout.write(reinterpret_cast(&f), sizeof(double)); + break; + } + case 3: + fwrite(fout, std::get(val)); + break; + } +} void readini(ifstream& fin, std::string& s) { @@ -141,7 +166,7 @@ void readini(ifstream& fin, std::string& s) using namespace std; -std::ifstream& operator>>(std::ifstream& fin, CQ::msgtype& t) +std::ifstream& operator>>(std::ifstream& fin, msgtype& t) { fin >> reinterpret_cast(t); return fin; @@ -151,7 +176,7 @@ std::ifstream& operator>>(std::ifstream& fin, chatType& ct) { int t; fin >> ct.first >> t; - ct.second = static_cast(t); + ct.second = static_cast(t); return fin; } diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index c05cc0ae..c4300012 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -12,12 +12,13 @@ #include #include #include +#include #include #include #include #include "DiceXMLTree.h" #include "StrExtern.hpp" - +#include "MsgFormat.h" using std::ifstream; using std::ofstream; @@ -77,6 +78,8 @@ bool fscan(std::ifstream& fin, C& obj) return false; } +using var = std::variant; + // ȡļ template std::enable_if_t, T> fread(ifstream& fin) @@ -97,6 +100,25 @@ std::enable_if_t, T> fread(ifstream& fin) delete[] buff; return s; } +template +std::enable_if_t, T> fread(ifstream& fin) { + const short len = fread(fin); + if (len >= 0) { + char* buff = new char[len]; + fin.read(buff, sizeof(char) * len); + std::string s(buff, len); + delete[] buff; + return s; + } + switch (len) { + case -1: + return fread(fin); + case -2: + return fread(fin); + default: + return {}; + } +} // ȡļreadb template @@ -363,7 +385,7 @@ int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2 //ȡļ template -int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& tmp, std::string& strLog, +int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& tmp, ResList& logList, bool isSubdir = false) { int intFile = 0, intFailure = 0, intItem = 0; @@ -380,13 +402,13 @@ int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& t } if (!intFile)return 0; - strLog += "ȡ" + strDir + "е" + std::to_string(intFile) + "ļ, " + std::to_string(intItem) + "Ŀ\n"; + logList << "ȡ" + strDir + "е" + std::to_string(intFile) + "ļ, " + std::to_string(intItem) + "Ŀ"; if (intFailure) { - strLog += "ȡʧ" + std::to_string(intFailure) + ":\n"; + logList << "ȡʧ" + std::to_string(intFailure) + ":"; for (auto& it : files) { - strLog += it + "\n"; + logList << it; } } return intFile; @@ -440,6 +462,7 @@ typename std::enable_if::value, void>::type fwrite(ofstream& f void fwrite(ofstream& fout, const std::string& s); +void fwrite(ofstream& fout, const var& var); template void fwrite(ofstream& fout, C& obj) diff --git a/Dice/DiceGUI.cpp b/Dice/DiceGUI.cpp index df3064ee..8e9cdf22 100644 --- a/Dice/DiceGUI.cpp +++ b/Dice/DiceGUI.cpp @@ -14,6 +14,7 @@ #include "GlobalVar.h" #include "Jsonio.h" #include "resource.h" +#include "DDAPI.h" #pragma comment(lib, "comctl32.lib") @@ -668,9 +669,8 @@ LRESULT DiceGUI::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) console.newMaster(qq); } } - else - { - MessageBoxA(nullptr, "Masterģʽѿ", "Masterģʽл", MB_OK | MB_ICONINFORMATION); + else { + MessageBoxA(nullptr, console["Private"] ? getMsg("strNewMasterPrivate").c_str() : getMsg("strNewMasterPublic").c_str(), "Masterģʽʼ", MB_OK | MB_ICONINFORMATION); console.newMaster(qq); console.isMasterMode = true; } @@ -727,7 +727,7 @@ LRESULT DiceGUI::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) if (ret == -1) { - string nickname = CQ::getStrangerInfo(qq).nick; + string nickname = DD::getQQNick(qq); ListViewUserTrust.AddTextRow({str, nickname, trust}); } else @@ -1007,7 +1007,7 @@ LRESULT DiceGUI::CreateAboutPage() StaticImageDiceLogo.Create(nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_REALSIZECONTROL, 0, 40, 40, 300, 300, m_hwnd); - StaticVersionInfo.Create(Dice_Full_Ver_For.c_str(), WS_CHILD | WS_VISIBLE, 0, + StaticVersionInfo.Create(Dice_Full_Ver_On.c_str(), WS_CHILD | WS_VISIBLE, 0, 40, 350, 300, 50, m_hwnd); StaticAuthorInfo.Create("Ҫ: Shiki\r\nAGPLv3Э¿Դ", WS_CHILD | WS_VISIBLE, 0, @@ -1118,18 +1118,13 @@ int WINAPI GUIMain() SendMessageA(progress, PBM_SETSTEP, static_cast(1), 0); ShowWindow(progress, SW_SHOWDEFAULT); - const std::map FriendMp = CQ::getFriendList(); + const std::set FriendMp{ DD::getFriendQQList() }; // ȡNickname for (const auto& item : UserList) { - if (FriendMp.count(item.first)) - { - nicknameMp[item.first] = FriendMp.at(item.first).nick; - } - else if (LoadStranger) - { - nicknameMp[item.first] = CQ::getStrangerInfo(item.first).nick; + if (FriendMp.count(item.first) || LoadStranger) { + nicknameMp[item.first] = DD::getQQNick(item.first); } SendMessageA(progress, PBM_STEPIT, 0, 0); MSG msg; diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 126df682..a62881c5 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -4,7 +4,7 @@ #include #include #include "StrExtern.hpp" -#include "CQAPI.h" +#include "DDAPI.h" #include "ManagerSystem.h" #include "DiceCloud.h" #include "BlackListManager.h" @@ -16,53 +16,18 @@ #pragma warning(disable:28159) using namespace std; -using namespace CQ; int sendSelf(const string msg) { - static long long selfQQ = CQ::getLoginQQ(); - return CQ::sendPrivateMsg(selfQQ, msg); + static long long selfQQ = DD::getLoginQQ(); + DD::sendPrivateMsg(selfQQ, msg); + return 0; } void cq_exit(DiceJob& job) { job.note("" + getMsg("self") + "5ɱ", 1); - if (frame == QQFrame::CoolQ) { - int pid = _getpid(); - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(pe32); - HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hProcessSnap == INVALID_HANDLE_VALUE) { - job.note("ʧܣ̿մʧܣ", 1); - } - BOOL bResult = Process32First(hProcessSnap, &pe32); - int ppid(0); - while (bResult) { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - break; - } - bResult = Process32Next(hProcessSnap, &pe32); - } - if (!ppid) { - job.note("ʧܣδҵ̣", 1); - } - string strCMD("taskkill /f /pid " + to_string(ppid)); - std::this_thread::sleep_for(5s); - job.echo(strCMD); - Enabled = false; - dataBackUp(); - system(strCMD.c_str()); - } - else if (frame == QQFrame::Mirai) { - std::this_thread::sleep_for(5s); - //Enabled = false; - dataBackUp(); - cout << "stop" << endl; - string cmd = "stop"; - for (auto key : cmd) { - keybd_event(key, 0, 0, 0); - } - keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, 0), 0, 0); - } + std::this_thread::sleep_for(5s); + dataBackUp(); + DD::killme(); } inline PROCESSENTRY32 getProcess(int pid) { @@ -78,119 +43,19 @@ void frame_restart(DiceJob& job) { sch.add_job_for(60 * 60, job); return; } - else if (int tWait = console["AutoFrameRemake"] * 60 * 60 - (clock() - llStartTime) / 1000; tWait > 0) { + else if (int tWait{ console["AutoFrameRemake"] * 60 * 60 - int(clock() - llStartTime) / 1000 }; tWait > 0) { sch.add_job_for(tWait, job); return; } } - string strSelfName; - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(pe32); - HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hProcessSnap == INVALID_HANDLE_VALUE) { - job.note("ʧܣ̿մʧܣ", 1); - } - BOOL bResult = Process32First(hProcessSnap, &pe32); - int ppid(0); - if (frame == QQFrame::Mirai) { - strSelfName = "MiraiOK.exe"; - char buffer[MAX_PATH]; - const DWORD length = GetModuleFileNameA(nullptr, buffer, sizeof buffer); - std::string pathSelf(buffer, length); - pathSelf = pathSelf.substr(0, pathSelf.find("jre\\bin\\java.exe")) + strSelfName; - char pathFull[MAX_PATH]; - while (bResult) { - if (strSelfName == pe32.szExeFile) { - HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); - GetModuleFileNameEx(hProcess, NULL, pathFull, sizeof(pathFull)); - if (pathSelf != pathFull)continue; - ppid = pe32.th32ProcessID; - job.echo("ȷϽ" + pathSelf + "\nid:" + to_string(ppid)); - break; - } - bResult = Process32Next(hProcessSnap, &pe32); - } - if (!ppid) { - job.echo("δҵ" + pathSelf); - return; - } - } - else if (frame == QQFrame::CoolQ) { - int pid = _getpid(); - while (bResult) { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - PROCESSENTRY32 pp32 = getProcess(ppid); - strSelfName = pp32.szExeFile; - job.echo("ȷϽ" + strSelfName + "\nid:" + to_string(pe32.th32ProcessID) + "\nid:" + to_string(ppid)); - break; - } - bResult = Process32Next(hProcessSnap, &pe32); - } - } - else { - ppid = _getpid(); - while (bResult) { - if (pe32.th32ProcessID == ppid) { - strSelfName = pe32.szExeFile; - job.echo("ȷϽ" + strSelfName + "\nid:" + to_string(pe32.th32ProcessID)); - break; - } - bResult = Process32Next(hProcessSnap, &pe32); - } - } - if (!ppid) { - job.note("ʧܣδҵ̣", 1); - return; - } - string command = "taskkill /f /pid " + to_string(ppid) + " /t\nstart " + strSelfName; - if (frame == QQFrame::CoolQ) command += " /account " + to_string(console.DiceMaid); - //else if (frame == QQFrame::XianQu) command = "start .\\remake.exe " + to_string(ppid) + " " + strSelfName; - else if (frame == QQFrame::XianQu) command = "start .\\.exe\ntaskkill /f /pid " + to_string(ppid); - ofstream fout("remake.bat"); - fout << command << std::endl; - fout.close(); - job.note(command, 0); - std::this_thread::sleep_for(3s); Enabled = false; dataBackUp(); std::this_thread::sleep_for(3s); - //if (frame == QQFrame::Mirai) { - // WinExec(("remake.exe " + to_string(ppid) + " " + strSelfName).c_str(), SW_SHOW); - // return; - //} - /* - switch (UINT res = -1; res = WinExec(".\\remake.bat", SW_SHOW)) { - case 0: - job.note("ʧܣڴԴѺľ", 1); - break; - case ERROR_FILE_NOT_FOUND: - job.note("ʧܣָļδҵ", 1); - break; - case ERROR_PATH_NOT_FOUND: - job.note("ʧܣָ·δҵ", 1); - break; - default: - if (res > 31)job.note("ɹ", 0); - else job.note("ʧܣδ֪" + to_string(res), 0); - break; - } - */ - ShellExecute(NULL, "open", "remake.bat", NULL, NULL, SW_SHOWNORMAL); + DD::remake(); } void frame_reload(DiceJob& job){ - using cq_reload_type = int(__stdcall*)(int32_t); - HMODULE hModule = GetModuleHandleA("CQP.dll"); - cq_reload_type cq_reload = (cq_reload_type)GetProcAddress(hModule, "CQ_reload"); - if (!cq_reload) { - if (frame == QQFrame::Mirai) - job.note("MiraiNativeʧܡ\nʹ˹ɻCQP.dll\n뱣֤汾MiraiNativeɾCQP.dll", 0b10); - else if (frame == QQFrame::XianQu) - job.note("CQXQʧܡ\n汾ɣ뱣֤汾CQXQ", 0b10); - return; - } - if(cq_reload(getAuthCode())) + if(DD::reload()) job.note("" + getMsg("self") + "ɡ", 1); else job.note("" + getMsg("self") + "ʧܡ", 0b10); @@ -293,7 +158,7 @@ void clear_group(DiceJob& job) { if (job.strVar["clear_mode"] == "unpower") { for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; - if (grp.isGroup && getGroupMemberInfo(id, console.DiceMaid).permissions == 1) { + if (grp.isGroup && !DD::isGroupAdmin(id, console.DiceMaid, true)) { res << printGroup(id); grp.leave(getMsg("strLeaveNoPower")); intCnt++; @@ -309,7 +174,7 @@ void clear_group(DiceJob& job) { for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; time_t tLast = grp.tUpdated; - if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0 && tLMT > tLast)tLast = tLMT; + if (long long tLMT; grp.isGroup && ((tLMT = DD::getGroupLastMsg(id, console.DiceMaid)) > 0 && tLMT > tLast))tLast = tLMT; if (!tLast)continue; int intDay = (int)(tNow - tLast) / 86400; if (intDay > intDayLim) { @@ -324,28 +189,29 @@ void clear_group(DiceJob& job) { } else if (job.strVar["clear_mode"] == "black") { try { - for (auto& [id, grp_name] : getGroupList()) { - Chat& grp = chat(id).group().name(grp_name); + for (auto id : DD::getGroupIDList()) { + Chat& grp = chat(id).group().name(DD::getGroupName(id)); if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("") || grp.isset("ЭЧ"))continue; if (blacklist->get_group_danger(id)) { res << printGroup(id) + "" + "Ⱥ"; if (console["LeaveBlackGroup"])grp.leave(getMsg("strBlackGroup")); } - vector MemberList = getGroupMemberList(id); + set MemberList{ DD::getGroupMemberList(id) }; + int authSelf{ DD::getGroupAuth(id, console.DiceMaid, 1) }; for (auto eachQQ : MemberList) { - if (blacklist->get_qq_danger(eachQQ.QQID) > 1) { - if (eachQQ.permissions < getGroupMemberInfo(id, getLoginQQ()).permissions) { + if (blacklist->get_qq_danger(eachQQ) > 1) { + if (auto authBlack{DD::getGroupAuth(id, eachQQ, 1)};authBlack < authSelf) { continue; } - else if (eachQQ.permissions > getGroupMemberInfo(id, getLoginQQ()).permissions) { - res << printChat(grp) + "" + printQQ(eachQQ.QQID) + "ԷȺȨ޽ϸ"; - grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); + else if (authBlack > authSelf) { + res << printChat(grp) + "" + printQQ(eachQQ) + "ԷȺȨ޽ϸ"; + grp.leave("ֺԱ" + printQQ(eachQQ) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); intCnt++; break; } else if (console["LeaveBlackQQ"]) { - res << printChat(grp) + "" + printQQ(eachQQ.QQID); - grp.leave("ֺԱ" + printQQ(eachQQ.QQID) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); + res << printChat(grp) + "" + printQQ(eachQQ); + grp.leave("ֺԱ" + printQQ(eachQQ) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); intCnt++; break; } @@ -366,7 +232,7 @@ void clear_group(DiceJob& job) { else if (job["clear_mode"] == "preserve") { for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("ʹ") || grp.isset("") || grp.isset("ЭЧ"))continue; - if (grp.isGroup && getGroupMemberInfo(id, console.master()).permissions) { + if (grp.isGroup && DD::isGroupAdmin(id, console.master(), false)) { grp.set("ʹ"); continue; } @@ -393,13 +259,14 @@ void list_group(DiceJob& job) { } job.reply("{self}" + job["list_mode"] + "Ⱥ¼" + to_string(res.size()) + "" + res.head(":").show()); } - else if (job["list_mode"] == "idle") { + else if (set grps(DD::getGroupIDList()); job["list_mode"] == "idle") { std::priority_queue> qDiver; time_t tNow = time(NULL); for (auto& [id, grp] : ChatList) { + if (grp.isGroup && !grps.empty() && !grps.count(id))grp.set(""); if (grp.isset("") || grp.isset("δ"))continue; time_t tLast = grp.tUpdated; - if (int tLMT; grp.isGroup && (tLMT = getGroupMemberInfo(id, console.DiceMaid).LastMsgTime) > 0 && tLMT > tLast)tLast = tLMT; + if (long long tLMT; grp.isGroup && (tLMT = DD::getGroupLastMsg(grp.ID, console.DiceMaid)) > 0 && tLMT > tLast)tLast = tLMT; if (!tLast)continue; int intDay = (int)(tNow - tLast) / 86400; qDiver.emplace(intDay, printGroup(id)); @@ -420,10 +287,11 @@ void list_group(DiceJob& job) { std::priority_queue> qSize; time_t tNow = time(NULL); for (auto& [id, grp] : ChatList) { + if (grp.isGroup && !grps.empty() && !grps.count(id))grp.set(""); if (grp.isset("") || grp.isset("δ") || !grp.isGroup)continue; - GroupInfo ginfo(id); - if (!ginfo.nGroupSize)continue; - qSize.emplace(ginfo.nGroupSize, printGroup(id)); + Size size(DD::getGroupSize(id)); + if (!size.siz)continue; + qSize.emplace(size.siz, printGroup(id)); } if (qSize.empty()) { job.reply("{self}ȺĻȺϢʧܣ"); @@ -441,73 +309,18 @@ void list_group(DiceJob& job) { // void cloud_beat(DiceJob& job) { - Cloud::update(); + Cloud::heartbeat(); sch.add_job_for(5 * 60, job); } void dice_update(DiceJob& job) { job.note("ʼDice\n汾:" + job.strVar["ver"], 1); - if (frame == QQFrame::Mirai) { - mkDir(dirExe + "plugins/MiraiNative/pluginsnew"); - char pathDll[] = "plugins/MiraiNative/pluginsnew/com.w4123.dice.dll"; - char pathJson[] = "plugins/MiraiNative/pluginsnew/com.w4123.dice.json"; - string urlDll("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "/com.w4123.dice.dll?" + to_string(job.fromTime)); - string urlJson("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "/com.w4123.dice.json?" + to_string(job.fromTime)); - switch (Cloud::DownloadFile(urlDll.c_str(), pathDll)) { - case -1: - job.echo("ʧ:" + urlDll); - break; - case -2: - job.note("Diceʧ!dllļδصָλ", 0b1); - break; - case 0: - default: - switch (Cloud::DownloadFile(urlJson.c_str(), pathJson)) { - case -1: - job.echo("ʧ:" + urlJson); - break; - case -2: - job.note("Diceʧ!jsonļδصָλ", 0b1); - break; - case 0: - default: - job.note("Dice!" + job.strVar["ver"] + "ɹ", 1); - } - } - } - else if (frame == QQFrame::XianQu) { - mkDir(dirExe + "CQPlugins/"); - char pathDll[] = "CQPlugins/com.w4123.dice.dll"; - string urlDll("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "/com.w4123.dice.dll?" + to_string(job.fromTime)); - switch (Cloud::DownloadFile(urlDll.c_str(), pathDll)) { - case -1: - job.echo("ʧ:" + urlDll); - break; - case -2: - job.note("Diceʧ!dllļδصָλ", 0b1); - break; - case 0: - default: - job.note("Dice!" + job.strVar["ver"] + "ɹ", 1); - } + string ret; + if (DD::updateDice(job.strVar["ver"], ret)) { + job.note("Dice!" + job.strVar["ver"] + "ɹ", 1); } else { - char** path = new char* (); - _get_pgmptr(path); - string strAppPath(*path); - delete path; - strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; - string strURL("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); - switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { - case -1: - job.echo("ʧ:" + strURL); - break; - case -2: - job.note("Diceʧ!ļδҵ:" + strAppPath, 0b10); - break; - case 0: - job.note("Dice!" + job.strVar["ver"] + "ɹ\n.system reload Ӧø", 1); - } + job.echo("ʧ:" + ret); } } @@ -525,7 +338,7 @@ void dice_cloudblack(DiceJob& job) { isSuccess = true; } else - job.echo("ͬƲ¼ͬʧ:" + strURL); + job.echo("ͬƲ¼ͬʧ:" + des); } break; case -2: @@ -561,6 +374,7 @@ void log_put(DiceJob& job) { } } + string print_master() { if (!console.master())return ""; return printQQ(console.master()); @@ -572,3 +386,19 @@ string list_deck() { string list_extern_deck() { return listKey(CardDeck::mExternPublicDeck); } +string list_order_ex() { + return fmt->list_order(); +} +string list_dice_sister() { + std::setlist{ DD::getDiceSisters() }; + if (list.size() <= 1)return {}; + else { + list.erase(console.DiceMaid); + ResList li; + li << printQQ(console.DiceMaid) + "Ľ:"; + for (auto dice : list) { + li << printQQ(dice); + } + return li.show(); + } +} \ No newline at end of file diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 325e6c8f..7756dd66 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -22,9 +22,13 @@ void cloud_beat(DiceJob& job); void dice_update(DiceJob& job); void dice_cloudblack(DiceJob& job); -void log_put(DiceJob& job); +void log_put(DiceJob& job); +void global_exit(); + string print_master(); string list_deck(); -string list_extern_deck(); \ No newline at end of file +string list_extern_deck(); +string list_order_ex(); +string list_dice_sister(); \ No newline at end of file diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp new file mode 100644 index 00000000..afcf94db --- /dev/null +++ b/Dice/DiceLua.cpp @@ -0,0 +1,291 @@ +//#pragma comment(lib, "lua.lib") +extern "C"{ +#include +#include +#include +}; +#include "ManagerSystem.h" +#include "DiceEvent.h" +#include "RandomGenerator.h" +#include "CharacterCard.h" +#include "CardDeck.h" +#include "DiceSession.h" + +unordered_set UTF8Luas; + +class LuaState { + lua_State* state; + //bool isValid; +public: + LuaState(const char* file); + //operator bool()const { return isValid; } + operator lua_State* () { return state; } + ~LuaState() { + if(state)lua_close(state); + UTF8Luas.erase(state); + } + void regist(); +}; + +void lua_push_string(lua_State* L, const string& str) { + if (UTF8Luas.count(L)) + lua_pushstring(L, GBKtoUTF8(str).c_str()); + else + lua_pushstring(L, str.c_str()); +} + +void lua_push_msg(lua_State* L, FromMsg* msg) { + lua_newtable(L); + //lua_pushnumber(L, (double)msg->fromQQ); + lua_push_string(L, to_string(msg->fromQQ)); + lua_setfield(L, -2, "fromQQ"); + //lua_pushnumber(L, (double)msg->fromGroup); + lua_push_string(L, to_string(msg->fromGroup)); + lua_setfield(L, -2, "fromGroup"); + lua_push_string(L, msg->strMsg); + lua_setfield(L, -2, "fromMsg"); +} + +//ȡָluaļĺ +bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { + LuaState L(file); + if (!L)return false; + lua_getglobal(L, func); + lua_push_msg(L, msg); + if (lua_pcall(L, 1, 2, 0)) { + const char* pErrorMsg = lua_tostring(L, -1); + console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ʧ!\n" + pErrorMsg, 1); + msg->reply(GlobalMsg["strOrderLuaErr"]); + return false; + } + if (lua_gettop(L) && lua_type(L, 1) != LUA_TNIL) { + if (!lua_isstring(L, 1)) { + console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ֵʽ!", 1); + msg->reply(GlobalMsg["strOrderLuaErr"]); + return false; + } + if (!(msg->strVar["msg_reply"] = UTF8toGBK(lua_tostring(L, 1), true)).empty()) { + msg->reply(msg->strVar["msg_reply"]); + } + if (lua_type(L, 2) != LUA_TNIL) { + if (!lua_isstring(L, 2)) { + console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ֵʽ!", 1); + return false; + } + if (!(msg->strVar["msg_hidden"] = UTF8toGBK(lua_tostring(L, 2), true)).empty()) { + msg->replyHidden(msg->strVar["msg_hidden"]); + } + } + } + return true; +} + +/** + * luaõĺ + */ + + //ȡDiceMaid +int getDiceQQ(lua_State* L) { + lua_push_string(L, to_string(console.DiceMaid)); + return 1; +} +//ȡDiceDir浵Ŀ¼ +int getDiceDir(lua_State* L) { + lua_push_string(L, DiceDir); + return 1; +} +int mkDirs(lua_State* L) { + string dir{ UTF8toGBK(lua_tostring(L, -1),true) }; + mkDir(dir); + return 0; +} +int getUserConf(lua_State* L) { + long long qq{ (long long)lua_tonumber(L, 1) }; + string item{ UTF8toGBK(lua_tostring(L, 2),true) }; + User& user{ getUser(qq) }; + if (user.intConf.count(item)) { + lua_pushnumber(L, (double)user.intConf[item]); + } + else if (user.strConf.count(item)) { + lua_push_string(L, user.strConf[item].c_str()); + } + else { + lua_pushnil(L); + lua_insert(L, 3); + } + return 1; +} +int setUserConf(lua_State* L) { + long long qq{ (long long)lua_tonumber(L, -3) }; + string item{ UTF8toGBK(lua_tostring(L, -2),true) }; + if (lua_isnumber(L, -1)) { + getUser(qq).setConf(item, (int)lua_tonumber(L, -1)); + } + else if (lua_isstring(L, -1)) { + getUser(qq).setConf(item, UTF8toGBK(lua_tostring(L, -1), true)); + } + return 0; +} +int getUserToday(lua_State* L) { + long long qq{ (long long)lua_tonumber(L, 1) }; + string item{ UTF8toGBK(lua_tostring(L, 2),true) }; + if (item == "jrrp") + lua_pushnumber(L, today->getJrrp(qq)); + else + lua_pushnumber(L, today->get(qq, item)); + return 1; +} +int setUserToday(lua_State* L) { + long long qq{ (long long)lua_tonumber(L, -3) }; + string item{ UTF8toGBK(lua_tostring(L, -2),true) }; + int val{ (int)lua_tonumber(L, -1) }; + today->set(qq, item, val); + return 0; +} +int getPlayerCardAttr(lua_State* L) { + long long plQQ{ (long long)lua_tonumber(L, 1) }; + long long group{ (long long)lua_tonumber(L, 2) }; + string item{ UTF8toGBK(lua_tostring(L, 3),true) }; + CharaCard& pc = getPlayer(plQQ)[group]; + string val; + if (pc.show(item, val) > -1) { + lua_push_string(L, val); + } + else { + lua_pushnil(L); + lua_insert(L, 4); + } + return 1; +} +int setPlayerCardAttr(lua_State* L) { + long long plQQ{ (long long)lua_tonumber(L, 1) }; + long long group{ (long long)lua_tonumber(L, 2) }; + string item{ UTF8toGBK(lua_tostring(L, 3),true) }; + CharaCard& pc = getPlayer(plQQ)[group]; + if (lua_isnumber(L, -1)) { + pc.set(item, (int)lua_tonumber(L, -1)); + } + else if (lua_isstring(L, -1)) { + pc.setInfo(item, UTF8toGBK(lua_tostring(L, -1), true)); + } + return 0; +} + +//ȡ +int ranint(lua_State* L) { + int l{ (int)lua_tonumber(L, -2) }; + int r{ (int)lua_tonumber(L, -1) }; + lua_pushnumber(L, RandomGenerator::Randint(l,r)); + return 1; +} + +int drawDeck(lua_State* L) { + long long fromGroup{ (long long)lua_tonumber(L, -3) }; + long long fromQQ{ (long long)lua_tonumber(L, -2) }; + string nameDeck{ UTF8toGBK(lua_tostring(L, -1),true) }; + long long fromSession{ fromGroup ? fromGroup : ~fromQQ }; + if (gm->has_session(fromSession)) { + lua_push_string(L, gm->session(fromSession).deck_draw(nameDeck)); + } + else if (CardDeck::findDeck(nameDeck)) { + vector& deck = CardDeck::mPublicDeck[nameDeck]; + lua_push_string(L, CardDeck::draw(deck[RandomGenerator::Randint(0, deck.size() - 1)])); + } + else { + lua_push_string(L, "{" + nameDeck + "}"); + } + return 1; +} + +int eventMsg(lua_State* L) { + string fromMsg{ UTF8toGBK(lua_tostring(L, -3),true) }; + long long fromGroup{ (long long)lua_tonumber(L, -2) }; + long long fromQQ{ (long long)lua_tonumber(L, -1) }; + msgtype type{ fromGroup ? + chat(fromGroup).isGroup ? msgtype::Group : msgtype::Discuss + : msgtype::Private }; + FromMsg* Msg = new FromMsg(fromGroup ? FromMsg(fromMsg, fromGroup, type, fromQQ) + : FromMsg(fromMsg, fromQQ)); + std::thread th(*Msg); + th.detach(); + return 0; +} + +void LuaState::regist() { + const luaL_Reg Dicelibs[] = { + {"getDiceQQ", getDiceQQ}, + {"getDiceDir", getDiceDir}, + {"mkDirs", mkDirs}, + {"getUserConf", getUserConf}, + {"setUserConf", setUserConf}, + {"getUserToday", getUserToday}, + {"setUserToday", setUserToday}, + {"getPlayerCardAttr", getPlayerCardAttr}, + {"setPlayerCardAttr", setPlayerCardAttr}, + {"ranint", ranint}, + {"drawDeck", drawDeck}, + {"eventMsg", eventMsg}, + {nullptr, nullptr}, + }; + for (const luaL_Reg* lib = Dicelibs; lib->func; lib++) { + lua_register(state, lib->name, lib->func); + } + lua_getglobal(state, "package"); + lua_getfield(state, -1, "path"); + string strPath(DiceDir + "\\plugin\\?.lua;" + lua_tostring(state, -1)); + lua_pushstring(state, strPath.c_str()); + lua_setfield(state, -3, "path"); + lua_pop(state, 2); + SetCurrentDirectory((DiceDir + "\\plugin\\").c_str()); +} + +LuaState::LuaState(const char* file) {//:isValid(false) { + state = luaL_newstate(); + if (!state)return; + if (luaL_loadfile(state, file)) { + console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + file + "ʧ!", 0b10); + lua_close(state); + state = nullptr; + return; + } + luaL_openlibs(state); + regist(); + if (lua_pcall(state, 0, 0, 0)) { + console.log(GlobalMsg["strSelfName"] + "luaļ" + file + "ʧ!", 0b10); + lua_close(state); + state = nullptr; + return; + } + ifstream fs(file); + stringstream ss; + ss << fs.rdbuf(); + string strCheck = ss.str(); + if (checkUTF8(strCheck))UTF8Luas.insert(state); +} + +int lua_readStringTable(const char* file, const char* var, std::unordered_map& tab) { + LuaState L(file); + if (!L)return -1; + lua_getglobal(L, var); + if (!lua_gettop(L)) { + return 0; + } + if (lua_type(L, 1) != LUA_TTABLE) { + return -2; + } + try { + lua_pushnil(L); + while (lua_next(L, -2)) { + if (!lua_isstring(L, -1) || !lua_isstring(L, -2)) { + return -1; + } + string key = UTF8toGBK(lua_tostring(L, -2), true); + string value = UTF8toGBK(lua_tostring(L, -1), true); + tab[key] = value; + lua_pop(L, 1); + } + return tab.size(); + } catch (...) { + return -3; + } +} \ No newline at end of file diff --git a/Dice/DiceLua.h b/Dice/DiceLua.h new file mode 100644 index 00000000..f1b09bae --- /dev/null +++ b/Dice/DiceLua.h @@ -0,0 +1,13 @@ +/** + * luaűǶ + * Զǰ׺ָ + * Copyright (C) 2020 String.Empty + */ +#pragma once +#include +#include + +class Lua_State; +class FromMsg; +bool lua_msg_order(FromMsg*, const char*, const char*); +int lua_readStringTable(const char*, const char*, std::unordered_map&); \ No newline at end of file diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 376633d6..4490a373 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -4,8 +4,19 @@ #include "ManagerSystem.h" #include "Jsonio.h" #include "DiceFile.hpp" +#include "DiceLua.h" using std::set; +bool DiceMsgOrder::exec(FromMsg* msg) { + if (type == OrderType::Lua) { + //std::thread th(lua_msg_order, msg, fileLua.c_str(), funcLua.c_str()); + //th.detach(); + lua_msg_order(msg, fileLua.c_str(), funcLua.c_str()); + return true; + } + return false; +} + DiceModManager::DiceModManager() : helpdoc(HelpDoc) { } @@ -83,18 +94,25 @@ void DiceModManager::_help(const shared_ptr& job) { job->reply(format(it->second, helpdoc)); } else if (unordered_set keys = querier.search((*job)["help_word"]);!keys.empty()) { - std::priority_queue, help_sorter> qKey; - for (auto key : keys) { - qKey.emplace(".help " + key); + if (keys.size() == 1) { + (*job)["redirect_key"] = *keys.begin(); + (*job)["redirect_res"] = get_help(*keys.begin()); + job->reply("{strHelpRedirect}"); } - ResList res; - while (!qKey.empty()) { - res << qKey.top(); - qKey.pop(); - if (res.size() > 20)break; + else { + std::priority_queue, help_sorter> qKey; + for (auto key : keys) { + qKey.emplace(".help " + key); + } + ResList res; + while (!qKey.empty()) { + res << qKey.top(); + qKey.pop(); + if (res.size() > 20)break; + } + (*job)["res"] = res.dot("/").show(1); + job->reply("{strHelpSuggestion}"); } - (*job)["res"] = res.dot("/").show(1); - job->reply("{strHelpSuggestion}"); } else job->reply("{strHelpNotFound}"); cntHelp[(*job)["help_word"]] += 1; @@ -112,43 +130,81 @@ void DiceModManager::rm_help(const string& key) helpdoc.erase(key); } -int DiceModManager::load(string& strLog) +bool DiceModManager::listen_order(DiceJobDetail* msg) { + string nameOrder; + if(!gOrder.match_head(msg->strMsg, nameOrder))return false; + msgorder[nameOrder].exec((FromMsg*)msg); + return true; +} +string DiceModManager::list_order() { + return "չָ:" + listKey(msgorder); +} + +int DiceModManager::load(ResList* resLog) { - vector sFile; - vector sFileErr; - int cntFile = listDir(DiceDir + "\\mod\\", sFile, true); + vector sModFile; + //ȡmod + vector sModErr; + int cntFile = listDir(DiceDir + "\\mod\\", sModFile); int cntItem{0}; - if (cntFile <= 0)return cntFile; - for (auto& filename : sFile) - { - nlohmann::json j = freadJson(filename); - if (j.is_null()) - { - sFileErr.push_back(filename.filename().string()); - continue; - } - if (j.count("dice_build")) { - if (j["dice_build"] > Dice_Build) { - sFileErr.push_back(filename.filename().string()+"(Dice汾)"); + if (cntFile > 0) { + for (auto& pathFile : sModFile) { + nlohmann::json j = freadJson(pathFile); + if (j.is_null()) { + sModErr.push_back(pathFile.filename().string()); continue; } + if (j.count("dice_build")) { + if (j["dice_build"] > Dice_Build) { + sModErr.push_back(pathFile.filename().string() + "(Dice汾)"); + continue; + } + } + if (j.count("helpdoc")) { + cntItem += readJMap(j["helpdoc"], helpdoc); + } + if (j.count("global_char")) { + cntItem += readJMap(j["global_char"], GlobalChar); + } } - if (j.count("helpdoc")) - { - cntItem += readJMap(j["helpdoc"], helpdoc); + *resLog << "ȡ\\mod\\е" + std::to_string(cntFile) + "ļ, " + std::to_string(cntItem) + "Ŀ"; + if (!sModErr.empty()) { + *resLog << "ȡʧ" + std::to_string(sModErr.size()) + ":"; + for (auto& it : sModErr) { + *resLog << it; + } } - if (j.count("global_char")) - { - cntItem += readJMap(j["global_char"], GlobalChar); + } + //ȡplugin + vector sLuaFile; + int cntLuaFile = listDir(DiceDir + "\\plugin\\", sLuaFile); + int cntOrder{ 0 }; + if (cntLuaFile <= 0)return cntLuaFile; + vector sLuaErr; + msgorder.clear(); + for (auto& pathFile : sLuaFile) { + string fileLua = pathFile.string(); + if (fileLua.rfind(".lua") != fileLua.length() - 4) { + sLuaErr.push_back(pathFile.filename().string()); + continue; + } + std::unordered_map mOrder; + int cnt = lua_readStringTable(fileLua.c_str(), "msg_order", mOrder); + if (cnt > 0) { + for (auto& [key, func] : mOrder) { + msgorder[key] = { fileLua,func }; + } + cntOrder += mOrder.size(); + } + else if (cnt < 0) { + sLuaErr.push_back(pathFile.filename().string()); } } - strLog += "ȡ" + DiceDir + "\\mod\\е" + std::to_string(cntFile) + "ļ, " + std::to_string(cntItem) + "Ŀ\n"; - if (!sFileErr.empty()) - { - strLog += "ȡʧ" + std::to_string(sFileErr.size()) + ":\n"; - for (auto& it : sFileErr) - { - strLog += it + "\n"; + *resLog << "ȡ\\plugin\\е" + std::to_string(cntLuaFile) + "ű, " + std::to_string(cntOrder) + "ָ"; + if (!sLuaErr.empty()) { + *resLog << "ȡʧ" + std::to_string(sLuaErr.size()) + ":"; + for (auto& it : sLuaErr) { + *resLog << it; } } std::thread factory(&DiceModManager::init,this); @@ -164,9 +220,11 @@ void DiceModManager::init() { for (auto& [key, word] : helpdoc) { querier.insert(key); } + gOrder.build(msgorder); isIniting = false; } void DiceModManager::clear() { helpdoc.clear(); + msgorder.clear(); } diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index 7657f98f..48824412 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -10,6 +10,7 @@ #include #include "STLExtern.hpp" #include "SHKQuerier.h" +#include "SHKTrie.h" #include "DiceSchedule.h" using std::string; using std::vector; @@ -33,6 +34,21 @@ class BaseDeck vector cards; }; +class FromMsg; +class DiceMsgOrder { + enum class OrderType{Nil,Lua}; + //֧lua + OrderType type{ OrderType::Nil }; + string fileLua; + string funcLua; +public: + DiceMsgOrder() = default; + DiceMsgOrder(const string& file, const string& func): fileLua(file), funcLua(func){ + type = OrderType::Lua; + } + bool exec(FromMsg*); +}; + class DiceMod { protected: @@ -44,6 +60,8 @@ class DiceMod using dir = map; dir mod_helpdoc; map> mod_public_deck; + using orders = map; + orders mod_msg_order; /*map m_generator;*/ public: DiceMod() = default; @@ -63,13 +81,17 @@ class DiceModFactory :public DiceMod { MOD_BUILD(unsigned int, build) MOD_BUILD(unsigned int, Dice_build) MOD_BUILD(dir, helpdoc) + MOD_BUILD(orders, msg_order) }; +class ResList; class DiceModManager { map mNameIndex; map helpdoc; + map msgorder; WordQuerier querier; + TrieG gOrder; public: DiceModManager(); friend void loadData(); @@ -80,7 +102,10 @@ class DiceModManager void _help(const shared_ptr&); void set_help(const string&, const string&); void rm_help(const string&); - int load(string&); + + bool listen_order(DiceJobDetail*); + string list_order(); + int load(ResList*); void init(); void clear(); }; diff --git a/Dice/DiceMsgSend.cpp b/Dice/DiceMsgSend.cpp index b0739374..562847f6 100644 --- a/Dice/DiceMsgSend.cpp +++ b/Dice/DiceMsgSend.cpp @@ -24,13 +24,13 @@ #include #include #include -#include "CQAPI_EX.h" +#include "DDAPI.h" #include "DiceMsgSend.h" #include "MsgFormat.h" #include "GlobalVar.h" #include "DiceConsole.h" using namespace std; -using namespace CQ; + // Ϣʹ洢ṹ struct msg_t @@ -90,15 +90,15 @@ void SendMsg() } if (msg.msg_type == msgtype::Private) { - sendPrivateMsg(msg.target_id, msg.msg); + DD::sendPrivateMsg(msg.target_id, msg.msg); } else if (msg.msg_type == msgtype::Group) { - sendGroupMsg(msg.target_id, msg.msg); + DD::sendGroupMsg(msg.target_id, msg.msg); } else { - sendDiscussMsg(msg.target_id, msg.msg); + DD::sendDiscussMsg(msg.target_id, msg.msg); } } if (msgQueue.size() > 2)this_thread::sleep_for(chrono::milliseconds(console["SendIntervalBusy"])); diff --git a/Dice/DiceMsgSend.h b/Dice/DiceMsgSend.h index 8bdabc7e..33ad408d 100644 --- a/Dice/DiceMsgSend.h +++ b/Dice/DiceMsgSend.h @@ -24,10 +24,10 @@ #ifndef DICE_MSG_SEND #define DICE_MSG_SEND #include -#include "CQMsgSend.h" -using chatType = std::pair; -std::ifstream& operator>>(std::ifstream& fin, CQ::msgtype& t); +enum class msgtype : int { Private = 0, Group = 1, Discuss = 2 }; +using chatType = std::pair; +std::ifstream& operator>>(std::ifstream& fin, msgtype& t); std::ifstream& operator>>(std::ifstream& fin, chatType& ct); std::ofstream& operator<<(std::ofstream& fout, const chatType& ct); /* @@ -37,7 +37,7 @@ std::ofstream& operator<<(std::ofstream& fout, const chatType& ct); * long long target_id ĿID(QQ,ȺŻuin) * MsgType msg_type Ϣ */ -void AddMsgToQueue(const std::string& msg, long long target_id, CQ::msgtype msg_type = CQ::msgtype::Private); +void AddMsgToQueue(const std::string& msg, long long target_id, msgtype msg_type = msgtype::Private); void AddMsgToQueue(const std::string& msg, chatType ct); /* diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 634667ff..ea4f9904 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -1,11 +1,14 @@ #include -#include #include +#include "DDAPI.h" #include "GlobalVar.h" #include "DiceJob.h" #include "ManagerSystem.h" #include "Jsonio.h" #include "DiceSchedule.h" +#include "DiceNetwork.h" +#include "RandomGenerator.h" +#include unordered_map mCommand = { {"syscheck",check_system}, @@ -35,14 +38,14 @@ void DiceJob::exec() { void DiceJob::echo(const std::string& msg) { if (!fromChat.first)return; switch (fromChat.second) { - case CQ::msgtype::Private: - CQ::sendPrivateMsg(fromQQ, msg); + case msgtype::Private: + DD::sendPrivateMsg(fromQQ, msg); break; - case CQ::msgtype::Group: - CQ::sendGroupMsg(fromChat.first, msg); + case msgtype::Group: + DD::sendGroupMsg(fromChat.first, msg); break; - case CQ::msgtype::Discuss: - CQ::sendDiscussMsg(fromChat.first, msg); + case msgtype::Discuss: + DD::sendDiscussMsg(fromChat.first, msg); break; } } @@ -56,7 +59,7 @@ void DiceJob::note(const std::string& strMsg, int note_lv = 0b1) { echo(strMsg); string note = fromQQ ? getName(fromQQ) + strMsg : strMsg; for (const auto& [ct, level] : console.NoticeList) { - if (!(level & note_lv) || pair(fromQQ, CQ::msgtype::Private) == ct || ct == fromChat)continue; + if (!(level & note_lv) || pair(fromQQ, msgtype::Private) == ct || ct == fromChat)continue; AddMsgToQueue(note, ct); } } @@ -64,8 +67,8 @@ void DiceJob::note(const std::string& strMsg, int note_lv = 0b1) { // std::queue queueJob; std::mutex mtQueueJob; -std::condition_variable cvJob; -std::condition_variable cvJobWaited; +//std::condition_variable cvJob; +//std::condition_variable cvJobWaited; //ʱ using waited_job = pair; std::priority_queue,std::greater> queueJobWaited; @@ -79,10 +82,11 @@ void jobHandle() { queueJob.pop(); lock_queue.unlock(); job.exec(); - cvJobWaited.notify_one(); + //cvJobWaited.notify_one(); } else{ - cvJob.wait_for(lock_queue, 2s, []() {return !queueJob.empty(); }); + //cvJob.wait_for(lock_queue, 2s, []() {return !Enabled || !queueJob.empty(); }); + std::this_thread::sleep_for(1s); } } } @@ -94,7 +98,8 @@ void jobWait() { queueJobWaited.pop(); } else { - cvJobWaited.wait_for(lock_queue, 1s); + //cvJobWaited.wait_for(lock_queue, 1s); + std::this_thread::sleep_for(1s); } today->daily_clear(); } @@ -107,7 +112,7 @@ void DiceScheduler::push_job(const DiceJobDetail& job) { std::unique_lock lock_queue(mtQueueJob); queueJob.push(job); } - cvJob.notify_one(); + //cvJob.notify_one(); } void DiceScheduler::push_job(const char* job_name, bool isSelf, unordered_mapvars) { if (!Enabled)return; @@ -115,7 +120,7 @@ void DiceScheduler::push_job(const char* job_name, bool isSelf, unordered_map lock_queue(mtQueueJob); queueJob.emplace(job_name, isSelf, vars); } - cvJob.notify_one(); + //cvJob.notify_one(); } //ȴ void DiceScheduler::add_job_for(unsigned int waited, const DiceJobDetail& job) { @@ -160,6 +165,24 @@ void DiceScheduler::start() { void DiceScheduler::end() { } +int DiceToday::getJrrp(long long qq) { + if (cntUser.count(qq) && cntUser[qq].count("jrrp")) + return cntUser[qq]["jrrp"]; + string frmdata = "QQ=" + to_string(console.DiceMaid) + "&v=20190114" + "&QueryQQ=" + to_string(qq); + string res; + if (Network::POST("api.kokona.tech", "/jrrp", 5555, frmdata.data(), res)) { + return cntUser[qq]["jrrp"] = stoi(res); + } + else { + if (!cntUser[qq].count("jrrp_local")) + cntUser[qq]["jrrp_local"] = RandomGenerator::Randint(1, 100); + console.log(getMsg("strJrrpErr", + { {"res", res} } + ), 1); + return cntUser[qq]["jrrp_local"]; + } +} + void DiceToday::daily_clear() { GetLocalTime(&stNow); if (stToday.tm_mday != stNow.wDay) { @@ -171,10 +194,14 @@ void DiceToday::daily_clear() { void DiceToday::save() { json jFile; - jFile["date"] = { stToday.tm_year + 1900,stToday.tm_mon + 1,stToday.tm_mday }; - jFile["global"] = cntGlobal; - jFile["user_cnt"] = cntUser; - fwriteJson(pathFile, jFile); + try { + jFile["date"] = { stToday.tm_year + 1900,stToday.tm_mon + 1,stToday.tm_mday }; + jFile["global"] = GBKtoUTF8(cntGlobal); + jFile["user_cnt"] = GBKtoUTF8(cntUser); + fwriteJson(pathFile, jFile); + } catch (...) { + console.log("ÿռ¼ʧ:json!", 0b10); + } } void DiceToday::load() { json jFile = freadJson(pathFile); @@ -190,8 +217,14 @@ void DiceToday::load() { stToday.tm_mon -= 1; jFile["date"][2].get_to(stToday.tm_mday); } - if (jFile.count("global")) { jFile["global"].get_to(cntGlobal); } - if (jFile.count("user_cnt")) { jFile["user_cnt"].get_to(cntUser); } + if (jFile.count("global")) { + jFile["global"].get_to(cntGlobal); + cntGlobal = UTF8toGBK(cntGlobal); + } + if (jFile.count("user_cnt")) { + jFile["user_cnt"].get_to(cntUser); + cntUser = UTF8toGBK(cntUser); + } } string printTTime(time_t tt) { diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index f28e3aa5..84c46814 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -80,10 +80,12 @@ class DiceToday { } void load(); void save(); + void set(long long qq, const string& key, int cnt) { cntUser[qq][key] = cnt; save(); } void inc(const string& key) { cntGlobal[key]++; save(); } void inc(long long qq, const string& key, int cnt = 1) { cntUser[qq][key] += cnt; save(); } int& get(const string& key) { return cntGlobal[key]; } int& get(long long qq, const string& key) { return cntUser[qq][key]; } + int getJrrp(long long qq); size_t cnt(const string& key = "") { return cntUser.size(); } void daily_clear(); }; diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index b2535134..dc6cfd4e 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -12,6 +12,7 @@ #include "DiceEvent.h" #include "CardDeck.h" #include "RandomGenerator.h" +#include "DDAPI.h" std::shared_mutex sessionMutex; @@ -285,14 +286,13 @@ void DiceSession::deck_set(FromMsg* msg) { } else { vector DeckSet = {}; - if ((strCiteDeck == "ȺԱ" || (strCiteDeck == "member" && !(strCiteDeck = "ȺԱ").empty())) && msg->fromChat.second == CQ::msgtype::Group) { - vector list = CQ::getGroupMemberList(msg->fromGroup); - if (list.empty()) { + if ((strCiteDeck == "ȺԱ" || (strCiteDeck == "member" && !(strCiteDeck = "ȺԱ").empty())) && msg->fromChat.second == msgtype::Group) { + + if (auto list{ DD::getGroupMemberList(msg->fromGroup) }; list.empty()) { msg->reply("ȺԱбȡʧܡ"); } - for (auto& each : list) { - DeckSet.push_back( - (each.GroupNick.empty() ? each.Nick : each.GroupNick) + "(" + to_string(each.QQID) + ")"); + else for (auto each : list) { + DeckSet.push_back(printQQ(each)); } decks[key] = DeckSet; } @@ -380,13 +380,14 @@ void DiceSession::deck_new(FromMsg* msg) { } string DiceSession::deck_draw(const string& key) { if (decks.count(key)) { - if (!decks[key].sizRes)return "{strDeckRestEmpty}"; + if (!decks[key].sizRes)return getMsg("strDeckRestEmpty", { {"deck_name",key} }); return decks[key].draw(); } else if (CardDeck::mPublicDeck.count(key)) { - return CardDeck::draw(key); + vector& deck = CardDeck::mPublicDeck[key]; + return CardDeck::draw(deck[RandomGenerator::Randint(0, deck.size() - 1)]); } - return ""; + return "{key}"; } void DiceSession::_draw(FromMsg* msg) { if (msg->strVar["deck_name"].empty())msg->strVar["deck_name"] = msg->readAttrName(); @@ -423,7 +424,7 @@ void DiceSession::_draw(FromMsg* msg) { msg->reply(GlobalMsg["strDrawCard"]); update(); } - if (deck.idxs.empty()) { + if (!deck.sizRes) { msg->reply(GlobalMsg["strDeckRestEmpty"]); } } @@ -665,4 +666,4 @@ int DiceTableMaster::load() mSession[pSession->room] = std::move(pSession); } return cnt; -} +} \ No newline at end of file diff --git a/Dice/EncodingConvert.cpp b/Dice/EncodingConvert.cpp index 05d6b5f3..a7db5ddf 100644 --- a/Dice/EncodingConvert.cpp +++ b/Dice/EncodingConvert.cpp @@ -31,9 +31,48 @@ #include #include +bool checkUTF8(const std::string& strUTF8) { + size_t cntUTF8(0); + int num = 0; + size_t i = 0; + while (i < strUTF8.length()) { + if ((strUTF8[i] & 0x80) == 0x00) { + i++; + continue; + } + else if ((strUTF8[i] & 0xc0) == 0xc0 && (strUTF8[i] & 0xfe) != 0xfe) { + // 110X_XXXX 10XX_XXXX + // 1110_XXXX 10XX_XXXX 10XX_XXXX + // 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX + // 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX + // 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX + unsigned char mask = 0x80; + for (num = 0; num < 8; ++num) { + if ((strUTF8[i] & mask) == mask) { + mask = mask >> 1; + } + else + break; + } + for (int j = 0; j < num - 1; j++) { + if ((strUTF8[++i] & 0xc0) != 0x80) { + return false; + } + } + ++i; + if (++cntUTF8 > 10)return true; + } + else { + return false; + } + } + return cntUTF8; +} + // GBK -std::string GBKtoUTF8(const std::string& strGBK) +std::string GBKtoUTF8(const std::string& strGBK, bool isTrial) { + if (isTrial && checkUTF8(strGBK))return strGBK; const int UTF16len = MultiByteToWideChar(CP_GBK, 0, strGBK.c_str(), -1, nullptr, 0); auto* const strUTF16 = new wchar_t[UTF16len]; MultiByteToWideChar(CP_GBK, 0, strGBK.c_str(), -1, strUTF16, UTF16len); @@ -54,8 +93,9 @@ std::vector GBKtoUTF8(const std::vector& strGBK) } // ʵGB18030 -std::string UTF8toGBK(const std::string& strUTF8) +std::string UTF8toGBK(const std::string& strUTF8, bool isTrial) { + if (isTrial && !checkUTF8(strUTF8))return strUTF8; const int UTF16len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, nullptr, 0); auto* const strUTF16 = new wchar_t[UTF16len]; MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, strUTF16, UTF16len); diff --git a/Dice/EncodingConvert.h b/Dice/EncodingConvert.h index 30d9b470..4183f170 100644 --- a/Dice/EncodingConvert.h +++ b/Dice/EncodingConvert.h @@ -25,27 +25,45 @@ #define DICE_ENCODING_CONVERT #include #include -std::string GBKtoUTF8(const std::string& strGBK); -std::vector GBKtoUTF8(const std::vector& strGBK); +#include + +bool checkUTF8(const std::string& strUTF8); template && - !std::is_convertible_v>>> -T GBKtoUTF8(T TGBK) -{ - return TGBK; + typename = std::enable_if_t && + !std::is_convertible_v> >> + T GBKtoUTF8(const T& TGBK) { + return TGBK; +} +std::string GBKtoUTF8(const std::string& strGBK, bool isTrial = false); +std::vector GBKtoUTF8(const std::vector& strGBK); +template +std::unordered_map GBKtoUTF8(const std::unordered_map& TGBK) { + std::unordered_map TUTF8; + for (auto& [key, val] : TGBK) { + TUTF8[GBKtoUTF8(key)] = GBKtoUTF8(val); + } + return TUTF8; } -std::string UTF8toGBK(const std::string& strUTF8); -std::vector UTF8toGBK(const std::vector& strUTF8); template && - !std::is_convertible_v>>> -T UTF8toGBK(T TUTF8) -{ + typename = std::enable_if_t && + !std::is_convertible_v> >> + T UTF8toGBK(const T& TUTF8) { return TUTF8; } +std::string UTF8toGBK(const std::string& strUTF8, bool isTrial = false); +std::vector UTF8toGBK(const std::vector& strUTF8); +template +std::unordered_map UTF8toGBK(const std::unordered_map& TUTF8) { + std::unordered_map TGBK; + for (auto& [key, val] : TUTF8) { + TGBK[UTF8toGBK(key)] = UTF8toGBK(val); + } + return TGBK; +} + std::string UrlEncode(const std::string& str); std::string UrlDecode(const std::string& str); diff --git a/Dice/GetRule.cpp b/Dice/GetRule.cpp index b210ab08..b0db46cc 100644 --- a/Dice/GetRule.cpp +++ b/Dice/GetRule.cpp @@ -28,10 +28,10 @@ #endif /*_MSC_VER*/ #include +#include "DDAPI.h" #include "GetRule.h" #include "GlobalVar.h" #include "EncodingConvert.h" -#include "CQAPI_EX.h" #include "DiceNetwork.h" @@ -73,7 +73,7 @@ namespace GetRule const string ruleName = GBKtoUTF8(rule); const string itemName = GBKtoUTF8(name); - string data = "Name=" + UrlEncode(itemName) + "&QQ=" + to_string(CQ::getLoginQQ()) + "&v=20190114"; + string data = "Name=" + UrlEncode(itemName) + "&QQ=" + to_string(DD::getLoginQQ()) + "&v=20190114"; if (!ruleName.empty()) { data += "&Type=Rules-" + UrlEncode(ruleName); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 51554a0d..a54c9c0a 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -22,15 +22,14 @@ */ #define WIN32_LEAN_AND_MEAN #include -#include "CQLogger.h" #include "GlobalVar.h" #include "MsgFormat.h" bool Enabled = false; -QQFrame frame{ QQFrame::CoolQ }; +//QQFrame frame{ QQFrame::CoolQ }; -std::string Dice_Full_Ver_For = Dice_Full_Ver + " For CoolQ]"; +std::string Dice_Full_Ver_On; std::string strModulePath; @@ -38,8 +37,6 @@ HMODULE hDllModule = nullptr; bool msgSendThreadRunning = false; -CQ::logger DiceLogger("Dice!"); - std::map GlobalMsg { {"strParaEmpty","Ϊա"}, //͵ܻظ @@ -84,14 +81,12 @@ std::map GlobalMsg {"strGroupWholeBanErr","{self}ȫֽʧܡ"}, {"strGroupUnban","{self}ö:{member}ԡ"}, {"strGroupBan","{self}ö:{member}{res}ӡ"}, - {"strGroupBanErr","{self}{member}ʧܡ"}, {"strGroupNotFound","{self}޸Ⱥ¼"}, - {"strGroupNotIn","{self}ǰڸȺȺ"}, + {"strGroupNot","{group}Ⱥ"}, + {"strGroupNotIn","{self}ǰ{group}ڡ"}, {"strGroupExit","{self}˳Ⱥ"}, {"strGroupCardSet","{self}ѽ{target}ȺƬ޸Ϊ{card}"}, - {"strGroupCardSetErr","{self}{target}ȺƬʧܡ"}, {"strGroupTitleSet","{self}ѽ{target}ͷ޸Ϊ{title}"}, - {"strGroupTitleSetErr","{self}{target}ͷʧܡ"}, {"strPcNewEmptyCard","Ϊ{nick}½{type}հ׿{char}"}, {"strPcNewCardShow","Ϊ{nick}½{type}{char}{show}"},//Ԥѡ {"strPcCardSet","ѽ{nick}ǰɫΪ{char}"},//{nick}-ûdz {pc}-ԭɫ {char}-½ɫ @@ -191,6 +186,7 @@ std::map GlobalMsg {"strHlpNameEmpty","MasterҪԶʲôѽ"}, {"strHelpNotFound","{self}δҵ{help_word}صĴ"}, {"strHelpSuggestion","{self}{nick}Ҫҵ:{res}"}, + {"strHelpRedirect","{self}ҵ{redirect_key}:\n{redirect_res}" }, {"strClockToWork","{self}Ѱʱá"}, {"strClockOffWork","{self}Ѱʱرա"}, {"strNameGenerator","{pc}ƣ{res}"}, @@ -306,8 +302,8 @@ std::map GlobalMsg {"strPreserve", "{self}˽˽ã\n뷢!authorize +[Ⱥ] ;:[ **д** ] ˽Dice!÷ϸĶ֤{strSelfName}ûЭ飬ָͣʹ[ **дָ** ]úʹ[ **дָ** ]ͳȺ"}, {"strJrrp", "{nick}Ʒֵ: {res}"}, {"strJrrpErr", "JRRPȡʧ! Ϣ: \n{res}"}, - { "strFriendDenyNotUser", "źûʹ{self}ļ¼" }, - { "strFriendDenyNoTrust", "ź㲻{self}εû" }, + { "strFriendDenyNotUser", "źûж{self}ʹָļ¼" }, + { "strFriendDenyNoTrust", "ź㲻{self}εûʹÿϵ{master_QQ}" }, {"strAddFriendWhiteQQ", "{strAddFriend}"}, //ûӺʱظ˾ { "strAddFriend", @@ -329,7 +325,21 @@ std::map GlobalMsg .help趨 ȷ趨 .help 鿴Դĵ ȺĬΪͬЭ飬֪ԻƳĺ)" - }, + }, + { "strNewMaster","ʣ{strSelfName}Master\nĶǰ汾MasterֲԼûֲᡣע汾ŶӦ: https://v2docs.kokona.tech\f{strSelfName}ĬϿȺƳԡˢ¼ļҪرֶ\nעƺϵͳĬϿ˹رCloudBlackShare" }, + { "strNewMasterPublic",R"({strSelfName}ʼģʽ +ԶBelieveDiceListӦбwarning +ģʽĬͬ¼ûĺ룬ҪΪͬκʹ.admin AllowStranger=2 +ѿԶʱÿնʱԶûĹͬȺģûȺȨ޲ԼʱԶȺ +ѿȺʱˣ +sendܽû͵Ϣ)" }, + { "strNewMasterPrivate",R"({strSelfName}ĬϿ˽ģʽ +ĬϾܾİ˵Ⱥ룬ֻͬԹԱû룻 +ĬϾܾİ˵ĺ룬Ҫͬ뿪AllowStranger +ѿԶʱÿնʱԶûĹͬȺģûȺȨ޸ԼʱԶȺ +.meĬϲãҪֶ +лʹ.admin publicʼӦã +.master deleteʹ.master public³ʼ)" }, {"strSelfName", ""}, {"strSelfCall", "&strSelfName"}, {"self", "&strSelfCall"}, @@ -351,6 +361,9 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +572:űдɫ +571:¿ܣ࿪ +570:.luaűԶָ 569:.rc/.draw 568:.deckԶƶ 567:дʼ @@ -375,10 +388,11 @@ const std::map HelpDoc = { 537:.send)"}, {"Э","0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, {"","Dice!̳: https://kokona.tech \n Dice!̳: https://forum.kokona.tech"}, -{"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ{}\n{}\nûȺ:{ûȺ}\nٷ(ˮ)Ⱥ: 882747577\n˽Ⱥ863062599 192499947\nȺ1029435374"}, +{"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ{}\n{}{}\nûȺ:{ûȺ}\nٷ(ˮ)Ⱥ: 882747577\n˽Ⱥ863062599 192499947\nȺ1029435374"}, {"ûȺ","δá"}, {"",""}, {"","δá"}, +{"","{list_dice_sister}"}, {"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, {"ָ",R"(atָָﵥӦat.bot off ָҪӲ.helpӦָ ȡϸϢ.help jrrp @@ -539,6 +553,7 @@ const std::map HelpDoc = { }, { "չƶ","{list_extern_deck}" }, { "ȫƶб","{list_all_deck}" }, + { "չָ","{list_extern_order}" }, {"ȹ", "&ri"}, {"ri", "ȹȺ޶.ri([ֵ])([dz])\n.ri -1 ijpc\t//Զȹб\n.ri +5 boss"}, {"ȹб", "&init"}, diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 45348485..8e352006 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -26,7 +26,6 @@ #define DICE_GLOBAL_VAR #define WIN32_LEAN_AND_MEAN #include -#include "CQLogger.h" #include #include #include "STLExtern.hpp" @@ -36,9 +35,9 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 569u; -inline const std::string Dice_Ver_Without_Build = "2.4.2beta2"; -constexpr auto DiceRequestHeader = "Dice/2.4.2beta2"; +const unsigned short Dice_Build = 572u; +inline const std::string Dice_Ver_Without_Build = "2.5.0beta2"; +constexpr auto DiceRequestHeader = "Dice/2.5.0"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; @@ -56,7 +55,7 @@ const std::string Dice_Full_Ver = Dice_Short_Ver + " [CLANG " + std::to_string(_ #ifdef _MSC_VER const std::string Dice_Full_Ver = std::string(Dice_Short_Ver) + "[MSVC " + std::to_string(_MSC_VER) + " " + __DATE__ + - " " + __TIME__; + " " + __TIME__ + "]"; #elif defined(__GNUC__) const std::string Dice_Full_Ver = Dice_Short_Ver + " [GNUC " + std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__) + " " + __DATE__ + " " + __TIME__; #else @@ -72,11 +71,11 @@ extern HMODULE hDllModule; extern bool Enabled; // л -enum class QQFrame { CoolQ, Mirai, XianQu }; -extern QQFrame frame; +//enum class QQFrame { CoolQ, Mirai, XianQu }; +//extern QQFrame frame; // Diceİ汾ַ -extern std::string Dice_Full_Ver_For; +extern std::string Dice_Full_Ver_On; // ִļλ extern std::string strModulePath; @@ -84,9 +83,6 @@ extern std::string strModulePath; // Ϣ߳Ƿ extern bool msgSendThreadRunning; -// ȫֿQLogger -extern CQ::logger DiceLogger; - // ظϢ, ݿͨCustomMsg޸Ķ޸Դ extern std::map GlobalMsg; // ޸ĺGlobal diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 4941fef5..ae5766b8 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -9,6 +9,8 @@ #include "CardDeck.h" #include "GlobalVar.h" +#include "DDAPI.h" +#include "CQTools.h" string dirExe; string DiceDir = "DiceData"; @@ -70,8 +72,9 @@ string getName(long long QQ, long long GroupID) if (QQ == console.DiceMaid)return getMsg("strSelfCall"); string nick; if (UserList.count(QQ) && getUser(QQ).getNick(nick, GroupID))return nick; - if (GroupID && !(nick = strip(CQ::getGroupMemberInfo(GroupID, QQ).GroupNick)).empty())return nick; - if (!(nick = strip(CQ::getStrangerInfo(QQ).nick)).empty())return nick; + if (GroupID && !(nick = DD::getGroupNick(GroupID, QQ)).empty() + && !(nick = strip(msg_decode(nick))).empty())return nick; + if (nick = DD::getQQNick(QQ); !(nick = strip(msg_decode(nick))).empty())return nick; return GlobalMsg["stranger"] + "(" + to_string(QQ) + ")"; } void filter_CQcode(string& nick, long long fromGroup) @@ -135,11 +138,10 @@ Chat& chat(long long id) Chat& Chat::id(long long grp) { ID = grp; if (!Enabled)return *this; - if (CQ::GroupInfo ginfo(grp); ginfo.llGroup || CQ::getGroupList().count(grp)) { - - Name = ginfo.strGroupName; + if (DD::getGroupIDList().count(grp)) { + Name = DD::getGroupName(grp); isGroup = true; - if (ExceptGroups.count(grp) || ginfo.nGroupSize > 499) { + if (ExceptGroups.count(grp) || DD::getGroupSize(grp).siz > 499) { boolConf.insert("ЭЧ"); } } @@ -149,21 +151,29 @@ Chat& Chat::id(long long grp) { return *this; } +void Chat::leave(const string& msg) { + if (!msg.empty()) { + if (isGroup)DD::sendGroupMsg(ID, msg); + else DD::sendDiscussMsg(ID, msg); + Sleep(500); + } + isGroup ? DD::setGroupLeave(ID) : DD::setDiscussLeave(ID); + set(""); +} bool Chat::is_except()const { return boolConf.count("") || boolConf.count("ЭЧ"); } -int groupset(long long id, string st) +int groupset(long long id, const string& st) { if (!ChatList.count(id))return -1; - return ChatList[id].isset(std::move(st)); + return ChatList[id].isset(st); } string printChat(Chat& grp) { - if (CQ::getGroupList().count(grp.ID))return "[" + CQ::getGroupList()[grp.ID] + "](" + to_string(grp.ID) + ")"; + if (DD::getGroupIDList().count(grp.ID))return "[" + DD::getGroupName(grp.ID) + "](" + to_string(grp.ID) + ")"; if (!grp.Name.empty())return "[" + grp.Name + "](" + to_string(grp.ID) + ")"; - if (grp.isset("Ⱥ"))return "[" + grp.strConf["Ⱥ"] + "](" + to_string(grp.ID) + ")"; if (grp.isGroup) return "Ⱥ(" + to_string(grp.ID) + ")"; return "(" + to_string(grp.ID) + ")"; } diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index 28d2f192..68b16f15 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -97,10 +97,10 @@ class User intConf[key] = val; } - void setConf(const string& key, string val) + void setConf(const string& key, const string& val) { std::lock_guard lock_queue(ex_user); - strConf[key] = std::move(val); + strConf[key] = val; } void rmIntConf(const string& key) @@ -243,17 +243,7 @@ class Chat return *this; } - void leave(const string& msg = "") - { - if (!msg.empty()) - { - if (isGroup)CQ::sendGroupMsg(ID, msg); - else CQ::sendDiscussMsg(ID, msg); - Sleep(500); - } - isGroup ? CQ::setGroupLeave(ID) : CQ::setDiscussLeave(ID); - set(""); - } + void leave(const string& msg = ""); [[nodiscard]] bool isset(const string& key) const { @@ -349,7 +339,7 @@ class Chat inline unordered_map ChatList; Chat& chat(long long id); -int groupset(long long id, string st); +int groupset(long long id, const string& st); string printChat(Chat& grp); ifstream& operator>>(ifstream& fin, Chat& grp); ofstream& operator<<(ofstream& fout, const Chat& grp); diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index aeaa9efd..5fe7e67d 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -36,6 +36,8 @@ std::map strFuncs{ {"master_QQ",print_master}, {"list_extern_deck",list_extern_deck}, {"list_all_deck",list_deck}, + {"list_extern_order",list_order_ex}, + {"list_dice_sister",list_dice_sister}, }; std::string format(std::string str, const std::initializer_list& replace_str) @@ -81,7 +83,7 @@ std::string to_binary(int b) return res.dot("+").show(); } -unsigned int ResList::intPageLen = 255; +unsigned int ResList::intPageLen = 512; ResList& ResList::operator<<(std::string s) { while (isspace(static_cast(s[0])))s.erase(s.begin()); if (s.empty())return *this; diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index 5c39ae0d..72885df1 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -81,7 +81,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT if (!console["ListenSpam"] || trustedQQ(fromQQ) > 1 || console.is_self(QQ))return; if (mFrequence[fromQQ] > 60 && mWarnLevel[fromQQ] < 60) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; - const std::string strMsg = "ѣ\n" + (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + + const std::string strMsg = "ѣ\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 18 ? "/5min" : (mCntOrder[fromQQ] > 8 ? "/min" : "/30s")); @@ -90,7 +90,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT } else if (mFrequence[fromQQ] > 120 && mWarnLevel[fromQQ] < 120) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; - const std::string strMsg = "棺\n" + (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + + const std::string strMsg = "棺\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 36 ? "/5min" : (mCntOrder[fromQQ] > 15 ? "/min" : "/30s")); @@ -99,7 +99,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT else if (mFrequence[fromQQ] > 200 && mWarnLevel[fromQQ] < 200) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; std::string strNow = printSTNow(); - std::string strNote = (CT.second != CQ::msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + + std::string strNote = (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 60 ? "/5min" : (mCntOrder[fromQQ] > 25 ? "/min" : "/30s")); @@ -123,33 +123,7 @@ int FrqMonitor::getFrqTotal() { return EarlyMsgQueue.size() + EarlierMsgQueue.size() / 2 + EarliestMsgQueue.size() / 10; } - -/*EVE_Status_EX(statusUptime) { - //ʼ - long long llDuration = clock() / 1000; - //long long llDuration = (clock() - llStartTime) / 1000; - if (llDuration < 0) { - eve.data = "N"; - eve.dataf = "/A"; - } - else if (llDuration < 60 * 5) { - eve.data = std::to_string(llDuration); - eve.dataf = "s"; - } - else if (llDuration < 60 * 60 * 5) { - eve.data = std::to_string(llDuration / 60); - eve.dataf = "min"; - } - else if (llDuration < 60 * 60 * 24 * 5) { - eve.data = std::to_string(llDuration / 60 / 60); - eve.dataf = "h"; - } - else{ - eve.data = std::to_string(llDuration / 60 / 60 / 24); - eve.dataf = "day"; - } - eve.color_green(); -}*/ +/* EVE_Status_EX(statusFrq) { if (!Enabled) @@ -185,3 +159,4 @@ EVE_Status_EX(statusFrq) } } } +*/ \ No newline at end of file diff --git a/Dice/RD.cpp b/Dice/RD.cpp index 03314603..4c3f00cc 100644 --- a/Dice/RD.cpp +++ b/Dice/RD.cpp @@ -20,14 +20,12 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#include "CQEVE_ALL.h" #include "CQTools.h" #include #include "RD.h" #include "RDConstant.h" #include "MsgFormat.h" using namespace std; -using namespace CQ; void init(string& msg) { diff --git a/Dice/RD.h b/Dice/RD.h index 403a4a69..e3c1af8c 100644 --- a/Dice/RD.h +++ b/Dice/RD.h @@ -789,12 +789,15 @@ class RD std::string FormShortString() const { std::string strReturnString = strDice; - strReturnString.append("="); - strReturnString.append(FormStringCombined()); - if (FormStringCombined() != std::to_string(intTotal)) + std::string stringCombined{ FormStringCombined() }; + if (strDice != stringCombined) { + strReturnString.append("="); + strReturnString.append(stringCombined); + } + if (std::string strTotal{ std::to_string(intTotal) };stringCombined != strTotal) { strReturnString.append("="); - strReturnString.append(std::to_string(intTotal)); + strReturnString.append(strTotal); } return strReturnString; } diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index 98092d86..5c72a59b 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -25,6 +25,7 @@ #define DICE_RD_CONSTANT #include #include +#include //Error Handle #define Value_Err (-1) diff --git a/Dice/SHKTrie.h b/Dice/SHKTrie.h index eae4556a..f07fb0fc 100644 --- a/Dice/SHKTrie.h +++ b/Dice/SHKTrie.h @@ -78,6 +78,18 @@ class TrieG { make_fail(root); } //ǰ׺ƥ + bool match_head(const string& s, string& res)const { + const Node* p = &root; + for (const auto& ch : s) { + //if (ignored(ch))continue; + if (!p->next.count(ch))break; + p = &(p->next.find(ch)->second); + if (p->isleaf) { + res = p->value; + } + } + return !res.empty(); + } bool match_head(const string& s, unordered_set& res)const { const Node* p = &root; for (const auto& ch : s) { diff --git a/Dice/STLExtern.hpp b/Dice/STLExtern.hpp index 2c2ed45c..66bee980 100644 --- a/Dice/STLExtern.hpp +++ b/Dice/STLExtern.hpp @@ -3,6 +3,7 @@ * Copyright (C) 2019-2020 String.Empty */ #pragma once +#include #include #include #include diff --git a/Dice/com.w4123.dice.json b/Dice/com.w4123.dice.json index 622e37de..f996fbd0 100644 --- a/Dice/com.w4123.dice.json +++ b/Dice/com.w4123.dice.json @@ -2,8 +2,8 @@ "ret": 1, "apiver": 9, "name": "Dice!", - "version": "2.4.2beta2", - "version_id": 569, + "version": "2.4.2beta3", + "version_id": 570, "author": "w4123溯洄 & Shiki", "description": "跑团用骰子 本程序使用AGPLv3开源协议授权 Copyright (c) 2018-2020 w4123溯洄 & Shiki", "event": [ diff --git a/Lua/lapi.c b/Lua/lapi.c new file mode 100644 index 00000000..9048245f --- /dev/null +++ b/Lua/lapi.c @@ -0,0 +1,1424 @@ +/* +** $Id: lapi.c $ +** Lua API +** See Copyright Notice in lua.h +*/ + +#define lapi_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" + + + +const char lua_ident[] = + "$LuaVersion: " LUA_COPYRIGHT " $" + "$LuaAuthors: " LUA_AUTHORS " $"; + + + +/* +** Test for a valid index. +** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed. +** However, it covers the most common cases in a faster way. +*/ +#define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue) + + +/* test for pseudo index */ +#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) + +/* test for upvalue */ +#define isupvalue(i) ((i) < LUA_REGISTRYINDEX) + + +static TValue *index2value (lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + StkId o = ci->func + idx; + api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index"); + if (o >= L->top) return &G(L)->nilvalue; + else return s2v(o); + } + else if (!ispseudo(idx)) { /* negative index */ + api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); + return s2v(L->top + idx); + } + else if (idx == LUA_REGISTRYINDEX) + return &G(L)->l_registry; + else { /* upvalues */ + idx = LUA_REGISTRYINDEX - idx; + api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); + if (ttislcf(s2v(ci->func))) /* light C function? */ + return &G(L)->nilvalue; /* it has no upvalues */ + else { + CClosure *func = clCvalue(s2v(ci->func)); + return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue; + } + } +} + + +static StkId index2stack (lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + StkId o = ci->func + idx; + api_check(L, o < L->top, "unacceptable index"); + return o; + } + else { /* non-positive index */ + api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); + api_check(L, !ispseudo(idx), "invalid index"); + return L->top + idx; + } +} + + +LUA_API int lua_checkstack (lua_State *L, int n) { + int res; + CallInfo *ci; + lua_lock(L); + ci = L->ci; + api_check(L, n >= 0, "negative 'n'"); + if (L->stack_last - L->top > n) /* stack large enough? */ + res = 1; /* yes; check is OK */ + else { /* no; need to grow stack */ + int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; + if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ + res = 0; /* no */ + else /* try to grow stack */ + res = luaD_growstack(L, n, 0); + } + if (res && ci->top < L->top + n) + ci->top = L->top + n; /* adjust frame top */ + lua_unlock(L); + return res; +} + + +LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checknelems(from, n); + api_check(from, G(from) == G(to), "moving among independent states"); + api_check(from, to->ci->top - to->top >= n, "stack overflow"); + from->top -= n; + for (i = 0; i < n; i++) { + setobjs2s(to, to->top, from->top + i); + to->top++; /* stack already checked by previous 'api_check' */ + } + lua_unlock(to); +} + + +LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API lua_Number lua_version (lua_State *L) { + UNUSED(L); + return LUA_VERSION_NUM; +} + + + +/* +** basic stack manipulation +*/ + + +/* +** convert an acceptable stack index into an absolute index +*/ +LUA_API int lua_absindex (lua_State *L, int idx) { + return (idx > 0 || ispseudo(idx)) + ? idx + : cast_int(L->top - L->ci->func) + idx; +} + + +LUA_API int lua_gettop (lua_State *L) { + return cast_int(L->top - (L->ci->func + 1)); +} + + +LUA_API void lua_settop (lua_State *L, int idx) { + CallInfo *ci; + StkId func; + ptrdiff_t diff; /* difference for new top */ + lua_lock(L); + ci = L->ci; + func = ci->func; + if (idx >= 0) { + api_check(L, idx <= ci->top - (func + 1), "new top too large"); + diff = ((func + 1) + idx) - L->top; + for (; diff > 0; diff--) + setnilvalue(s2v(L->top++)); /* clear new slots */ + } + else { + api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); + diff = idx + 1; /* will "subtract" index (as it is negative) */ + } + if (diff < 0 && hastocloseCfunc(ci->nresults)) + luaF_close(L, L->top + diff, LUA_OK); + L->top += diff; /* correct top only after closing any upvalue */ + lua_unlock(L); +} + + +/* +** Reverse the stack segment from 'from' to 'to' +** (auxiliary to 'lua_rotate') +** Note that we move(copy) only the value inside the stack. +** (We do not move additional fields that may exist.) +*/ +static void reverse (lua_State *L, StkId from, StkId to) { + for (; from < to; from++, to--) { + TValue temp; + setobj(L, &temp, s2v(from)); + setobjs2s(L, from, to); + setobj2s(L, to, &temp); + } +} + + +/* +** Let x = AB, where A is a prefix of length 'n'. Then, +** rotate x n == BA. But BA == (A^r . B^r)^r. +*/ +LUA_API void lua_rotate (lua_State *L, int idx, int n) { + StkId p, t, m; + lua_lock(L); + t = L->top - 1; /* end of stack segment being rotated */ + p = index2stack(L, idx); /* start of segment */ + api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); + m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ + reverse(L, p, m); /* reverse the prefix with length 'n' */ + reverse(L, m + 1, t); /* reverse the suffix */ + reverse(L, p, t); /* reverse the entire segment */ + lua_unlock(L); +} + + +LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { + TValue *fr, *to; + lua_lock(L); + fr = index2value(L, fromidx); + to = index2value(L, toidx); + api_check(L, isvalid(L, to), "invalid index"); + setobj(L, to, fr); + if (isupvalue(toidx)) /* function upvalue? */ + luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr); + /* LUA_REGISTRYINDEX does not need gc barrier + (collector revisits it before finishing collection) */ + lua_unlock(L); +} + + +LUA_API void lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top, index2value(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int lua_type (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (isvalid(L, o) ? ttype(o) : LUA_TNONE); +} + + +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type"); + return ttypename(t); +} + + +LUA_API int lua_iscfunction (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttislcf(o) || (ttisCclosure(o))); +} + + +LUA_API int lua_isinteger (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return ttisinteger(o); +} + + +LUA_API int lua_isnumber (lua_State *L, int idx) { + lua_Number n; + const TValue *o = index2value(L, idx); + return tonumber(o, &n); +} + + +LUA_API int lua_isstring (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttisstring(o) || cvt2str(o)); +} + + +LUA_API int lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttisfulluserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { + const TValue *o1 = index2value(L, index1); + const TValue *o2 = index2value(L, index2); + return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0; +} + + +LUA_API void lua_arith (lua_State *L, int op) { + lua_lock(L); + if (op != LUA_OPUNM && op != LUA_OPBNOT) + api_checknelems(L, 2); /* all other operations expect two operands */ + else { /* for unary operations, add fake 2nd operand */ + api_checknelems(L, 1); + setobjs2s(L, L->top, L->top - 1); + api_incr_top(L); + } + /* first operand at top - 2, second at top - 1; result go to top - 2 */ + luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2); + L->top--; /* remove second operand */ + lua_unlock(L); +} + + +LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { + const TValue *o1; + const TValue *o2; + int i = 0; + lua_lock(L); /* may call tag method */ + o1 = index2value(L, index1); + o2 = index2value(L, index2); + if (isvalid(L, o1) && isvalid(L, o2)) { + switch (op) { + case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break; + case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; + case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; + default: api_check(L, 0, "invalid option"); + } + } + lua_unlock(L); + return i; +} + + +LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { + size_t sz = luaO_str2num(s, s2v(L->top)); + if (sz != 0) + api_incr_top(L); + return sz; +} + + +LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) { + lua_Number n = 0; + const TValue *o = index2value(L, idx); + int isnum = tonumber(o, &n); + if (pisnum) + *pisnum = isnum; + return n; +} + + +LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) { + lua_Integer res = 0; + const TValue *o = index2value(L, idx); + int isnum = tointeger(o, &res); + if (pisnum) + *pisnum = isnum; + return res; +} + + +LUA_API int lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { + TValue *o; + lua_lock(L); + o = index2value(L, idx); + if (!ttisstring(o)) { + if (!cvt2str(o)) { /* not convertible? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaO_tostring(L, o); + luaC_checkGC(L); + o = index2value(L, idx); /* previous call may reallocate the stack */ + } + if (len != NULL) + *len = vslen(o); + lua_unlock(L); + return svalue(o); +} + + +LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VSHRSTR: return tsvalue(o)->shrlen; + case LUA_VLNGSTR: return tsvalue(o)->u.lnglen; + case LUA_VUSERDATA: return uvalue(o)->len; + case LUA_VTABLE: return luaH_getn(hvalue(o)); + default: return 0; + } +} + + +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + if (ttislcf(o)) return fvalue(o); + else if (ttisCclosure(o)) + return clCvalue(o)->f; + else return NULL; /* not a C function */ +} + + +static void *touserdata (const TValue *o) { + switch (ttype(o)) { + case LUA_TUSERDATA: return getudatamem(uvalue(o)); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API void *lua_touserdata (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return touserdata(o); +} + + +LUA_API lua_State *lua_tothread (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +/* +** Returns a pointer to the internal representation of an object. +** Note that ANSI C does not allow the conversion of a pointer to +** function to a 'void*', so the conversion here goes through +** a 'size_t'. (As the returned pointer is only informative, this +** conversion should not be a problem.) +*/ +LUA_API const void *lua_topointer (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o))); + case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA: + return touserdata(o); + default: { + if (iscollectable(o)) + return gcvalue(o); + else + return NULL; + } + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(s2v(L->top)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setfltvalue(s2v(L->top), n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setivalue(s2v(L->top), n); + api_incr_top(L); + lua_unlock(L); +} + + +/* +** Pushes on the stack a string with given length. Avoid using 's' when +** 'len' == 0 (as 's' can be NULL in that case), due to later use of +** 'memcmp' and 'memcpy'. +*/ +LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { + TString *ts; + lua_lock(L); + ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); + setsvalue2s(L, L->top, ts); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getstr(ts); +} + + +LUA_API const char *lua_pushstring (lua_State *L, const char *s) { + lua_lock(L); + if (s == NULL) + setnilvalue(s2v(L->top)); + else { + TString *ts; + ts = luaS_new(L, s); + setsvalue2s(L, L->top, ts); + s = getstr(ts); /* internal copy's address */ + } + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return s; +} + + +LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + ret = luaO_pushvfstring(L, fmt, argp); + luaC_checkGC(L); + lua_unlock(L); + return ret; +} + + +LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + va_start(argp, fmt); + ret = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + luaC_checkGC(L); + lua_unlock(L); + return ret; +} + + +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + lua_lock(L); + if (n == 0) { + setfvalue(s2v(L->top), fn); + api_incr_top(L); + } + else { + CClosure *cl; + api_checknelems(L, n); + api_check(L, n <= MAXUPVAL, "upvalue index too large"); + cl = luaF_newCclosure(L, n); + cl->f = fn; + L->top -= n; + while (n--) { + setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); + /* does not need barrier because closure is white */ + lua_assert(iswhite(cl)); + } + setclCvalue(L, s2v(L->top), cl); + api_incr_top(L); + luaC_checkGC(L); + } + lua_unlock(L); +} + + +LUA_API void lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + if (b) + setbtvalue(s2v(L->top)); + else + setbfvalue(s2v(L->top)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(s2v(L->top), p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, s2v(L->top), L); + api_incr_top(L); + lua_unlock(L); + return (G(L)->mainthread == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +static int auxgetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *slot; + TString *str = luaS_new(L, k); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + setobj2s(L, L->top, slot); + api_incr_top(L); + } + else { + setsvalue2s(L, L->top, str); + api_incr_top(L); + luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); + } + lua_unlock(L); + return ttype(s2v(L->top - 1)); +} + + +LUA_API int lua_getglobal (lua_State *L, const char *name) { + Table *reg; + lua_lock(L); + reg = hvalue(&G(L)->l_registry); + return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); +} + + +LUA_API int lua_gettable (lua_State *L, int idx) { + const TValue *slot; + TValue *t; + lua_lock(L); + t = index2value(L, idx); + if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) { + setobj2s(L, L->top - 1, slot); + } + else + luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); + lua_unlock(L); + return ttype(s2v(L->top - 1)); +} + + +LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { + lua_lock(L); + return auxgetstr(L, index2value(L, idx), k); +} + + +LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { + TValue *t; + const TValue *slot; + lua_lock(L); + t = index2value(L, idx); + if (luaV_fastgeti(L, t, n, slot)) { + setobj2s(L, L->top, slot); + } + else { + TValue aux; + setivalue(&aux, n); + luaV_finishget(L, t, &aux, L->top, slot); + } + api_incr_top(L); + lua_unlock(L); + return ttype(s2v(L->top - 1)); +} + + +static int finishrawget (lua_State *L, const TValue *val) { + if (isempty(val)) /* avoid copying empty items to the stack */ + setnilvalue(s2v(L->top)); + else + setobj2s(L, L->top, val); + api_incr_top(L); + lua_unlock(L); + return ttype(s2v(L->top - 1)); +} + + +static Table *gettable (lua_State *L, int idx) { + TValue *t = index2value(L, idx); + api_check(L, ttistable(t), "table expected"); + return hvalue(t); +} + + +LUA_API int lua_rawget (lua_State *L, int idx) { + Table *t; + const TValue *val; + lua_lock(L); + api_checknelems(L, 1); + t = gettable(L, idx); + val = luaH_get(t, s2v(L->top - 1)); + L->top--; /* remove key */ + return finishrawget(L, val); +} + + +LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { + Table *t; + lua_lock(L); + t = gettable(L, idx); + return finishrawget(L, luaH_getint(t, n)); +} + + +LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { + Table *t; + TValue k; + lua_lock(L); + t = gettable(L, idx); + setpvalue(&k, cast_voidp(p)); + return finishrawget(L, luaH_get(t, &k)); +} + + +LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { + Table *t; + lua_lock(L); + t = luaH_new(L); + sethvalue2s(L, L->top, t); + api_incr_top(L); + if (narray > 0 || nrec > 0) + luaH_resize(L, t, narray, nrec); + luaC_checkGC(L); + lua_unlock(L); +} + + +LUA_API int lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt; + int res = 0; + lua_lock(L); + obj = index2value(L, objindex); + switch (ttype(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + default: + mt = G(L)->mt[ttype(obj)]; + break; + } + if (mt != NULL) { + sethvalue2s(L, L->top, mt); + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { + TValue *o; + int t; + lua_lock(L); + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (n <= 0 || n > uvalue(o)->nuvalue) { + setnilvalue(s2v(L->top)); + t = LUA_TNONE; + } + else { + setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv); + t = ttype(s2v(L->top)); + } + api_incr_top(L); + lua_unlock(L); + return t; +} + + +/* +** set functions (stack -> Lua) +*/ + +/* +** t[k] = value at the top of the stack (where 'k' is a string) +*/ +static void auxsetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *slot; + TString *str = luaS_new(L, k); + api_checknelems(L, 1); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + luaV_finishfastset(L, t, slot, s2v(L->top - 1)); + L->top--; /* pop value */ + } + else { + setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ + api_incr_top(L); + luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot); + L->top -= 2; /* pop value and key */ + } + lua_unlock(L); /* lock done by caller */ +} + + +LUA_API void lua_setglobal (lua_State *L, const char *name) { + Table *reg; + lua_lock(L); /* unlock done in 'auxsetstr' */ + reg = hvalue(&G(L)->l_registry); + auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); +} + + +LUA_API void lua_settable (lua_State *L, int idx) { + TValue *t; + const TValue *slot; + lua_lock(L); + api_checknelems(L, 2); + t = index2value(L, idx); + if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) { + luaV_finishfastset(L, t, slot, s2v(L->top - 1)); + } + else + luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot); + L->top -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, index2value(L, idx), k); +} + + +LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { + TValue *t; + const TValue *slot; + lua_lock(L); + api_checknelems(L, 1); + t = index2value(L, idx); + if (luaV_fastgeti(L, t, n, slot)) { + luaV_finishfastset(L, t, slot, s2v(L->top - 1)); + } + else { + TValue aux; + setivalue(&aux, n); + luaV_finishset(L, t, &aux, s2v(L->top - 1), slot); + } + L->top--; /* pop value */ + lua_unlock(L); +} + + +static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { + Table *t; + TValue *slot; + lua_lock(L); + api_checknelems(L, n); + t = gettable(L, idx); + slot = luaH_set(L, t, key); + setobj2t(L, slot, s2v(L->top - 1)); + invalidateTMcache(t); + luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); + L->top -= n; + lua_unlock(L); +} + + +LUA_API void lua_rawset (lua_State *L, int idx) { + aux_rawset(L, idx, s2v(L->top - 2), 2); +} + + +LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { + TValue k; + setpvalue(&k, cast_voidp(p)); + aux_rawset(L, idx, &k, 1); +} + + +LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { + Table *t; + lua_lock(L); + api_checknelems(L, 1); + t = gettable(L, idx); + luaH_setint(L, t, n, s2v(L->top - 1)); + luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); + L->top--; + lua_unlock(L); +} + + +LUA_API int lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + lua_lock(L); + api_checknelems(L, 1); + obj = index2value(L, objindex); + if (ttisnil(s2v(L->top - 1))) + mt = NULL; + else { + api_check(L, ttistable(s2v(L->top - 1)), "table expected"); + mt = hvalue(s2v(L->top - 1)); + } + switch (ttype(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrier(L, gcvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrier(L, uvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + default: { + G(L)->mt[ttype(obj)] = mt; + break; + } + } + L->top--; + lua_unlock(L); + return 1; +} + + +LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { + TValue *o; + int res; + lua_lock(L); + api_checknelems(L, 1); + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) + res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ + else { + setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1)); + luaC_barrierback(L, gcvalue(o), s2v(L->top - 1)); + res = 1; + } + L->top--; + lua_unlock(L); + return res; +} + + +/* +** 'load' and 'call' functions (run Lua code) +*/ + + +#define checkresults(L,na,nr) \ + api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ + "results from function overflow current stack size") + + +LUA_API void lua_callk (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k) { + StkId func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checknelems(L, nargs+1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + func = L->top - (nargs+1); + if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ + L->ci->u.c.k = k; /* save continuation */ + L->ci->u.c.ctx = ctx; /* save context */ + luaD_call(L, func, nresults); /* do the call */ + } + else /* no continuation or no yieldable */ + luaD_callnoyield(L, func, nresults); /* just do the call */ + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to 'f_call' */ + StkId func; + int nresults; +}; + + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_callnoyield(L, c->func, c->nresults); +} + + + +LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k) { + struct CallS c; + int status; + ptrdiff_t func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checknelems(L, nargs+1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2stack(L, errfunc); + api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); + func = savestack(L, o); + } + c.func = L->top - (nargs+1); /* function to be called */ + if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ + c.nresults = nresults; /* do a 'conventional' protected call */ + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + } + else { /* prepare continuation (call is already protected by 'resume') */ + CallInfo *ci = L->ci; + ci->u.c.k = k; /* save continuation */ + ci->u.c.ctx = ctx; /* save context */ + /* save information for error recovery */ + ci->u2.funcidx = cast_int(savestack(L, c.func)); + ci->u.c.old_errfunc = L->errfunc; + L->errfunc = func; + setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ + ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ + luaD_call(L, c.func, nresults); /* do the call */ + ci->callstatus &= ~CIST_YPCALL; + L->errfunc = ci->u.c.old_errfunc; + status = LUA_OK; /* if it is here, there were no errors */ + } + adjustresults(L, nresults); + lua_unlock(L); + return status; +} + + +LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname, const char *mode) { + ZIO z; + int status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname, mode); + if (status == LUA_OK) { /* no errors? */ + LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */ + if (f->nupvalues >= 1) { /* does it have an upvalue? */ + /* get global table from registry */ + Table *reg = hvalue(&G(L)->l_registry); + const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS); + /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ + setobj(L, f->upvals[0]->v, gt); + luaC_barrier(L, f->upvals[0], gt); + } + } + lua_unlock(L); + return status; +} + + +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { + int status; + TValue *o; + lua_lock(L); + api_checknelems(L, 1); + o = s2v(L->top - 1); + if (isLfunction(o)) + status = luaU_dump(L, getproto(o), writer, data, strip); + else + status = 1; + lua_unlock(L); + return status; +} + + +LUA_API int lua_status (lua_State *L) { + return L->status; +} + + +/* +** Garbage-collection function +*/ +LUA_API int lua_gc (lua_State *L, int what, ...) { + va_list argp; + int res = 0; + global_State *g; + lua_lock(L); + g = G(L); + va_start(argp, what); + switch (what) { + case LUA_GCSTOP: { + g->gcrunning = 0; + break; + } + case LUA_GCRESTART: { + luaE_setdebt(g, 0); + g->gcrunning = 1; + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L, 0); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(gettotalbytes(g) >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(gettotalbytes(g) & 0x3ff); + break; + } + case LUA_GCSTEP: { + int data = va_arg(argp, int); + l_mem debt = 1; /* =1 to signal that it did an actual step */ + lu_byte oldrunning = g->gcrunning; + g->gcrunning = 1; /* allow GC to run */ + if (data == 0) { + luaE_setdebt(g, 0); /* do a basic step */ + luaC_step(L); + } + else { /* add 'data' to total debt */ + debt = cast(l_mem, data) * 1024 + g->GCdebt; + luaE_setdebt(g, debt); + luaC_checkGC(L); + } + g->gcrunning = oldrunning; /* restore previous state */ + if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ + break; + } + case LUA_GCSETPAUSE: { + int data = va_arg(argp, int); + res = getgcparam(g->gcpause); + setgcparam(g->gcpause, data); + break; + } + case LUA_GCSETSTEPMUL: { + int data = va_arg(argp, int); + res = getgcparam(g->gcstepmul); + setgcparam(g->gcstepmul, data); + break; + } + case LUA_GCISRUNNING: { + res = g->gcrunning; + break; + } + case LUA_GCGEN: { + int minormul = va_arg(argp, int); + int majormul = va_arg(argp, int); + res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; + if (minormul != 0) + g->genminormul = minormul; + if (majormul != 0) + setgcparam(g->genmajormul, majormul); + luaC_changemode(L, KGC_GEN); + break; + } + case LUA_GCINC: { + int pause = va_arg(argp, int); + int stepmul = va_arg(argp, int); + int stepsize = va_arg(argp, int); + res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; + if (pause != 0) + setgcparam(g->gcpause, pause); + if (stepmul != 0) + setgcparam(g->gcstepmul, stepmul); + if (stepsize != 0) + g->gcstepsize = stepsize; + luaC_changemode(L, KGC_INC); + break; + } + default: res = -1; /* invalid option */ + } + va_end(argp); + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int lua_error (lua_State *L) { + TValue *errobj; + lua_lock(L); + errobj = s2v(L->top - 1); + api_checknelems(L, 1); + /* error object is the memory error message? */ + if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) + luaM_error(L); /* raise a memory error */ + else + luaG_errormsg(L); /* raise a regular error */ + /* code unreachable; will unlock when control actually leaves the kernel */ + return 0; /* to avoid warnings */ +} + + +LUA_API int lua_next (lua_State *L, int idx) { + Table *t; + int more; + lua_lock(L); + api_checknelems(L, 1); + t = gettable(L, idx); + more = luaH_next(L, t, L->top - 1); + if (more) { + api_incr_top(L); + } + else /* no more elements */ + L->top -= 1; /* remove key */ + lua_unlock(L); + return more; +} + + +LUA_API void lua_toclose (lua_State *L, int idx) { + int nresults; + StkId o; + lua_lock(L); + o = index2stack(L, idx); + nresults = L->ci->nresults; + api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o, + "marked index below or equal new one"); + luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ + if (!hastocloseCfunc(nresults)) /* function not marked yet? */ + L->ci->nresults = codeNresults(nresults); /* mark it */ + lua_assert(hastocloseCfunc(L->ci->nresults)); + lua_unlock(L); +} + + +LUA_API void lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n > 0) + luaV_concat(L, n); + else { /* nothing to concatenate */ + setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */ + api_incr_top(L); + } + luaC_checkGC(L); + lua_unlock(L); +} + + +LUA_API void lua_len (lua_State *L, int idx) { + TValue *t; + lua_lock(L); + t = index2value(L, idx); + luaV_objlen(L, L->top, t); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) { + lua_lock(L); + G(L)->ud_warn = ud; + G(L)->warnf = f; + lua_unlock(L); +} + + +void lua_warning (lua_State *L, const char *msg, int tocont) { + lua_lock(L); + luaE_warning(L, msg, tocont); + lua_unlock(L); +} + + + +LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { + Udata *u; + lua_lock(L); + api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); + u = luaS_newudata(L, size, nuvalue); + setuvalue(L, s2v(L->top), u); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getudatamem(u); +} + + + +static const char *aux_upvalue (TValue *fi, int n, TValue **val, + GCObject **owner) { + switch (ttypetag(fi)) { + case LUA_VCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues))) + return NULL; /* 'n' not in [1, f->nupvalues] */ + *val = &f->upvalue[n-1]; + if (owner) *owner = obj2gco(f); + return ""; + } + case LUA_VLCL: { /* Lua closure */ + LClosure *f = clLvalue(fi); + TString *name; + Proto *p = f->p; + if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) + return NULL; /* 'n' not in [1, p->sizeupvalues] */ + *val = f->upvals[n-1]->v; + if (owner) *owner = obj2gco(f->upvals[n - 1]); + name = p->upvalues[n-1].name; + return (name == NULL) ? "(no name)" : getstr(name); + } + default: return NULL; /* not a closure */ + } +} + + +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + lua_lock(L); + name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); + if (name) { + setobj2s(L, L->top, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + GCObject *owner = NULL; /* to avoid warnings */ + TValue *fi; + lua_lock(L); + fi = index2value(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val, &owner); + if (name) { + L->top--; + setobj(L, val, s2v(L->top)); + luaC_barrier(L, owner, val); + } + lua_unlock(L); + return name; +} + + +static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { + LClosure *f; + TValue *fi = index2value(L, fidx); + api_check(L, ttisLclosure(fi), "Lua function expected"); + f = clLvalue(fi); + api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); + if (pf) *pf = f; + return &f->upvals[n - 1]; /* get its upvalue pointer */ +} + + +LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { + TValue *fi = index2value(L, fidx); + switch (ttypetag(fi)) { + case LUA_VLCL: { /* lua closure */ + return *getupvalref(L, fidx, n, NULL); + } + case LUA_VCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); + return &f->upvalue[n - 1]; + } + default: { + api_check(L, 0, "closure expected"); + return NULL; + } + } +} + + +LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, + int fidx2, int n2) { + LClosure *f1; + UpVal **up1 = getupvalref(L, fidx1, n1, &f1); + UpVal **up2 = getupvalref(L, fidx2, n2, NULL); + *up1 = *up2; + luaC_objbarrier(L, f1, *up1); +} + + diff --git a/Lua/lapi.h b/Lua/lapi.h new file mode 100644 index 00000000..41216b27 --- /dev/null +++ b/Lua/lapi.h @@ -0,0 +1,47 @@ +/* +** $Id: lapi.h $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +#include "llimits.h" +#include "lstate.h" + + +/* Increments 'L->top', checking for stack overflows */ +#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ + "stack overflow");} + + +/* +** If a call returns too many multiple returns, the callee may not have +** stack space to accommodate all results. In this case, this macro +** increases its stack space ('L->ci->top'). +*/ +#define adjustresults(L,nres) \ + { if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } + + +/* Ensure the stack has at least 'n' elements */ +#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ + "not enough elements in the stack") + + +/* +** To reduce the overhead of returning from C functions, the presence of +** to-be-closed variables in these functions is coded in the CallInfo's +** field 'nresults', in a way that functions with no to-be-closed variables +** with zero, one, or "all" wanted results have no overhead. Functions +** with other number of wanted results, as well as functions with +** variables to be closed, have an extra check. +*/ + +#define hastocloseCfunc(n) ((n) < LUA_MULTRET) + +#define codeNresults(n) (-(n) - 3) + +#endif diff --git a/Lua/lauxlib.c b/Lua/lauxlib.c new file mode 100644 index 00000000..cbe9ed31 --- /dev/null +++ b/Lua/lauxlib.c @@ -0,0 +1,1059 @@ +/* +** $Id: lauxlib.c $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + +#define lauxlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include +#include + + +/* +** This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#include "lua.h" + +#include "lauxlib.h" + + +#if !defined(MAX_SIZET) +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) +#endif + + +/* +** {====================================================== +** Traceback +** ======================================================= +*/ + + +#define LEVELS1 10 /* size of the first part of the stack */ +#define LEVELS2 11 /* size of the second part of the stack */ + + + +/* +** Search for 'objidx' in table at index -1. ('objidx' must be an +** absolute index.) Return 1 + string at top if it found a good name. +*/ +static int findfield (lua_State *L, int objidx, int level) { + if (level == 0 || !lua_istable(L, -1)) + return 0; /* not found */ + lua_pushnil(L); /* start 'next' loop */ + while (lua_next(L, -2)) { /* for each pair in table */ + if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ + if (lua_rawequal(L, objidx, -1)) { /* found object? */ + lua_pop(L, 1); /* remove value (but keep name) */ + return 1; + } + else if (findfield(L, objidx, level - 1)) { /* try recursively */ + /* stack: lib_name, lib_table, field_name (top) */ + lua_pushliteral(L, "."); /* place '.' between the two names */ + lua_replace(L, -3); /* (in the slot occupied by table) */ + lua_concat(L, 3); /* lib_name.field_name */ + return 1; + } + } + lua_pop(L, 1); /* remove value */ + } + return 0; /* not found */ +} + + +/* +** Search for a name for a function in all loaded modules +*/ +static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { + int top = lua_gettop(L); + lua_getinfo(L, "f", ar); /* push function */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + if (findfield(L, top + 1, 2)) { + const char *name = lua_tostring(L, -1); + if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ + lua_pushstring(L, name + 3); /* push name without prefix */ + lua_remove(L, -2); /* remove original name */ + } + lua_copy(L, -1, top + 1); /* copy name to proper place */ + lua_settop(L, top + 1); /* remove table "loaded" and name copy */ + return 1; + } + else { + lua_settop(L, top); /* remove function and global table */ + return 0; + } +} + + +static void pushfuncname (lua_State *L, lua_Debug *ar) { + if (pushglobalfuncname(L, ar)) { /* try first a global name */ + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } + else if (*ar->namewhat != '\0') /* is there a name from code? */ + lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ + else if (*ar->what == 'm') /* main? */ + lua_pushliteral(L, "main chunk"); + else if (*ar->what != 'C') /* for Lua functions, use */ + lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); + else /* nothing left... */ + lua_pushliteral(L, "?"); +} + + +static int lastlevel (lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; + /* find an upper bound */ + while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } + /* do a binary search */ + while (li < le) { + int m = (li + le)/2; + if (lua_getstack(L, m, &ar)) li = m + 1; + else le = m; + } + return le - 1; +} + + +LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, + const char *msg, int level) { + luaL_Buffer b; + lua_Debug ar; + int last = lastlevel(L1); + int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; + luaL_buffinit(L, &b); + if (msg) { + luaL_addstring(&b, msg); + luaL_addchar(&b, '\n'); + } + luaL_addstring(&b, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (limit2show-- == 0) { /* too many levels? */ + int n = last - level - LEVELS2 + 1; /* number of levels to skip */ + lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n); + luaL_addvalue(&b); /* add warning about skip */ + level += n; /* and skip to last levels */ + } + else { + lua_getinfo(L1, "Slnt", &ar); + if (ar.currentline <= 0) + lua_pushfstring(L, "\n\t%s: in ", ar.short_src); + else + lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline); + luaL_addvalue(&b); + pushfuncname(L, &ar); + luaL_addvalue(&b); + if (ar.istailcall) + luaL_addstring(&b, "\n\t(...tail calls...)"); + } + } + luaL_pushresult(&b); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + +LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + arg--; /* do not count 'self' */ + if (arg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling '%s' on bad self (%s)", + ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; + return luaL_error(L, "bad argument #%d to '%s' (%s)", + arg, ar.name, extramsg); +} + + +int luaL_typeerror (lua_State *L, int arg, const char *tname) { + const char *msg; + const char *typearg; /* name for the type of the actual argument */ + if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) + typearg = lua_tostring(L, -1); /* use the given type name */ + else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) + typearg = "light userdata"; /* special name for messages */ + else + typearg = luaL_typename(L, arg); /* standard name */ + msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); + return luaL_argerror(L, arg, msg); +} + + +static void tag_error (lua_State *L, int arg, int tag) { + luaL_typeerror(L, arg, lua_typename(L, tag)); +} + + +/* +** The use of 'lua_pushfstring' ensures this function does not +** need reserved stack space when called. +*/ +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushfstring(L, ""); /* else, no information available... */ +} + + +/* +** Again, the use of 'lua_pushvfstring' ensures this function does +** not need reserved stack space when called. (At worst, it generates +** an error with "stack overflow" instead of the given message.) +*/ +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + + +LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { + int en = errno; /* calls to Lua API may change this value */ + if (stat) { + lua_pushboolean(L, 1); + return 1; + } + else { + luaL_pushfail(L); + if (fname) + lua_pushfstring(L, "%s: %s", fname, strerror(en)); + else + lua_pushstring(L, strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +#if !defined(l_inspectstat) /* { */ + +#if defined(LUA_USE_POSIX) + +#include + +/* +** use appropriate macros to interpret 'pclose' return status +*/ +#define l_inspectstat(stat,what) \ + if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ + else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } + +#else + +#define l_inspectstat(stat,what) /* no op */ + +#endif + +#endif /* } */ + + +LUALIB_API int luaL_execresult (lua_State *L, int stat) { + const char *what = "exit"; /* type of termination */ + if (stat != 0 && errno != 0) /* error with an 'errno'? */ + return luaL_fileresult(L, 0, NULL); + else { + l_inspectstat(stat, what); /* interpret result */ + if (*what == 'e' && stat == 0) /* successful termination? */ + lua_pushboolean(L, 1); + else + luaL_pushfail(L); + lua_pushstring(L, what); + lua_pushinteger(L, stat); + return 3; /* return true/fail,what,code */ + } +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Userdata's metatable manipulation +** ======================================================= +*/ + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_createtable(L, 0, 2); /* create metatable */ + lua_pushstring(L, tname); + lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + + +LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) { + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + + +LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = luaL_testudata(L, ud, tname); + luaL_argexpected(L, p != NULL, ud, tname); + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Argument check functions +** ======================================================= +*/ + +LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, arg, def) : + luaL_checkstring(L, arg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, arg, + lua_pushfstring(L, "invalid option '%s'", name)); +} + + +/* +** Ensures the stack has at least 'space' extra slots, raising an error +** if it cannot fulfill the request. (The error handling needs a few +** extra slots to format the error message. In case of an error without +** this extra space, Lua will generate the same 'stack overflow' error, +** but without 'msg'.) +*/ +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { + if (!lua_checkstack(L, space)) { + if (msg) + luaL_error(L, "stack overflow (%s)", msg); + else + luaL_error(L, "stack overflow"); + } +} + + +LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) { + if (lua_type(L, arg) != t) + tag_error(L, arg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int arg) { + if (lua_type(L, arg) == LUA_TNONE) + luaL_argerror(L, arg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) { + const char *s = lua_tolstring(L, arg, len); + if (!s) tag_error(L, arg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int arg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, arg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, arg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) { + int isnum; + lua_Number d = lua_tonumberx(L, arg, &isnum); + if (!isnum) + tag_error(L, arg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, arg, def); +} + + +static void interror (lua_State *L, int arg) { + if (lua_isnumber(L, arg)) + luaL_argerror(L, arg, "number has no integer representation"); + else + tag_error(L, arg, LUA_TNUMBER); +} + + +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) { + int isnum; + lua_Integer d = lua_tointegerx(L, arg, &isnum); + if (!isnum) { + interror(L, arg); + } + return d; +} + + +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, arg, def); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +/* userdata to box arbitrary data */ +typedef struct UBox { + void *box; + size_t bsize; +} UBox; + + +static void *resizebox (lua_State *L, int idx, size_t newsize) { + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + UBox *box = (UBox *)lua_touserdata(L, idx); + void *temp = allocf(ud, box->box, box->bsize, newsize); + if (temp == NULL && newsize > 0) { /* allocation error? */ + lua_pushliteral(L, "not enough memory"); + lua_error(L); /* raise a memory error */ + } + box->box = temp; + box->bsize = newsize; + return temp; +} + + +static int boxgc (lua_State *L) { + resizebox(L, 1, 0); + return 0; +} + + +static const luaL_Reg boxmt[] = { /* box metamethods */ + {"__gc", boxgc}, + {"__close", boxgc}, + {NULL, NULL} +}; + + +static void newbox (lua_State *L) { + UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); + box->box = NULL; + box->bsize = 0; + if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ + luaL_setfuncs(L, boxmt, 0); /* set its metamethods */ + lua_setmetatable(L, -2); +} + + +/* +** check whether buffer is using a userdata on the stack as a temporary +** buffer +*/ +#define buffonstack(B) ((B)->b != (B)->init.b) + + +/* +** Compute new size for buffer 'B', enough to accommodate extra 'sz' +** bytes. +*/ +static size_t newbuffsize (luaL_Buffer *B, size_t sz) { + size_t newsize = B->size * 2; /* double buffer size */ + if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */ + return luaL_error(B->L, "buffer too large"); + if (newsize < B->n + sz) /* double is not big enough? */ + newsize = B->n + sz; + return newsize; +} + + +/* +** Returns a pointer to a free area with at least 'sz' bytes in buffer +** 'B'. 'boxidx' is the relative position in the stack where the +** buffer's box is or should be. +*/ +static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { + if (B->size - B->n >= sz) /* enough space? */ + return B->b + B->n; + else { + lua_State *L = B->L; + char *newbuff; + size_t newsize = newbuffsize(B, sz); + /* create larger buffer */ + if (buffonstack(B)) /* buffer already has a box? */ + newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ + else { /* no box yet */ + lua_pushnil(L); /* reserve slot for final result */ + newbox(L); /* create a new box */ + /* move box (and slot) to its intended position */ + lua_rotate(L, boxidx - 1, 2); + lua_toclose(L, boxidx); + newbuff = (char *)resizebox(L, boxidx, newsize); + memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ + } + B->b = newbuff; + B->size = newsize; + return newbuff + B->n; + } +} + +/* +** returns a pointer to a free area with at least 'sz' bytes +*/ +LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + return prepbuffsize(B, sz, -1); +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ + char *b = prepbuffsize(B, l, -1); + memcpy(b, s, l * sizeof(char)); + luaL_addsize(B, l); + } +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + lua_State *L = B->L; + lua_pushlstring(L, B->b, B->n); + if (buffonstack(B)) { + lua_copy(L, -1, -3); /* move string to reserved slot */ + lua_pop(L, 2); /* pop string and box (closing the box) */ + } +} + + +LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { + luaL_addsize(B, sz); + luaL_pushresult(B); +} + + +/* +** 'luaL_addvalue' is the only function in the Buffer system where the +** box (if existent) is not on the top of the stack. So, instead of +** calling 'luaL_addlstring', it replicates the code using -2 as the +** last argument to 'prepbuffsize', signaling that the box is (or will +** be) bellow the string being added to the buffer. (Box creation can +** trigger an emergency GC, so we should not remove the string from the +** stack before we have the space guaranteed.) +*/ +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t len; + const char *s = lua_tolstring(L, -1, &len); + char *b = prepbuffsize(B, len, -2); + memcpy(b, s, len * sizeof(char)); + luaL_addsize(B, len); + lua_pop(L, 1); /* pop string */ +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->b = B->init.b; + B->n = 0; + B->size = LUAL_BUFFERSIZE; +} + + +LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { + luaL_buffinit(L, B); + return prepbuffsize(B, sz, -1); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Reference system +** ======================================================= +*/ + +/* index of free-list header */ +#define freelist 0 + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* 'nil' has a unique fixed reference */ + } + t = lua_absindex(L, t); + lua_rawgeti(L, t, freelist); /* get first free element */ + ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ + } + else /* no free elements */ + ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = lua_absindex(L, t); + lua_rawgeti(L, t, freelist); + lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, freelist); /* t[freelist] = ref */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + int n; /* number of pre-read characters */ + FILE *f; /* file being read */ + char buff[BUFSIZ]; /* area for reading file */ +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; /* not used */ + if (lf->n > 0) { /* are there pre-read characters to be read? */ + *size = lf->n; /* return them (chars already in buffer) */ + lf->n = 0; /* no more pre-read characters */ + } + else { /* read a block from file */ + /* 'fread' can return > 0 *and* set the EOF flag. If next call to + 'getF' called 'fread', it might still wait for user input. + The next check avoids this problem. */ + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ + } + return lf->buff; +} + + +static int errfile (lua_State *L, const char *what, int fnameindex) { + const char *serr = strerror(errno); + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +static int skipBOM (LoadF *lf) { + const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ + int c; + lf->n = 0; + do { + c = getc(lf->f); + if (c == EOF || c != *(const unsigned char *)p++) return c; + lf->buff[lf->n++] = c; /* to be read by the parser */ + } while (*p != '\0'); + lf->n = 0; /* prefix matched; discard it */ + return getc(lf->f); /* return next character */ +} + + +/* +** reads the first character of file 'f' and skips an optional BOM mark +** in its beginning plus its first line if it starts with '#'. Returns +** true if it skipped the first line. In any case, '*cp' has the +** first "valid" character of the file (after the optional BOM and +** a first-line comment). +*/ +static int skipcomment (LoadF *lf, int *cp) { + int c = *cp = skipBOM(lf); + if (c == '#') { /* first line is a comment (Unix exec. file)? */ + do { /* skip first line */ + c = getc(lf->f); + } while (c != EOF && c != '\n'); + *cp = getc(lf->f); /* skip end-of-line, if present */ + return 1; /* there was a comment */ + } + else return 0; /* no comment */ +} + + +LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, + const char *mode) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + if (skipcomment(&lf, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + skipcomment(&lf, &c); /* re-read initial portion */ + } + if (c != EOF) + lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ + status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; /* not used */ + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size, + const char *name, const char *mode) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name, mode); +} + + +LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, strlen(s), s); +} + +/* }====================================================== */ + + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return LUA_TNIL; + else { + int tt; + lua_pushstring(L, event); + tt = lua_rawget(L, -2); + if (tt == LUA_TNIL) /* is metafield nil? */ + lua_pop(L, 2); /* remove metatable and metafield */ + else + lua_remove(L, -2); /* remove only metatable */ + return tt; /* return metafield type */ + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = lua_absindex(L, obj); + if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { + lua_Integer l; + int isnum; + lua_len(L, idx); + l = lua_tointegerx(L, -1, &isnum); + if (!isnum) + luaL_error(L, "object length is not an integer"); + lua_pop(L, 1); /* remove object */ + return l; +} + + +LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { + if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */ + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } + else { + switch (lua_type(L, idx)) { + case LUA_TNUMBER: { + if (lua_isinteger(L, idx)) + lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); + else + lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx)); + break; + } + case LUA_TSTRING: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: { + int tt = luaL_getmetafield(L, idx, "__name"); /* try name */ + const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : + luaL_typename(L, idx); + lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_remove(L, -2); /* remove '__name' */ + break; + } + } + } + return lua_tolstring(L, -1, len); +} + + +/* +** set functions from list 'l' into table at top - 'nup'; each +** function gets the 'nup' elements at the top as upvalues. +** Returns with only the table at the stack. +*/ +LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + if (l->func == NULL) /* place holder? */ + lua_pushboolean(L, 0); + else { + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + } + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + +/* +** ensure that stack[idx][fname] has a table and push that table +** into the stack +*/ +LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { + if (lua_getfield(L, idx, fname) == LUA_TTABLE) + return 1; /* table already there */ + else { + lua_pop(L, 1); /* remove previous result */ + idx = lua_absindex(L, idx); + lua_newtable(L); + lua_pushvalue(L, -1); /* copy to be left at top */ + lua_setfield(L, idx, fname); /* assign new table to field */ + return 0; /* false, because did not find table there */ + } +} + + +/* +** Stripped-down 'require': After checking "loaded" table, calls 'openf' +** to open a module, registers the result in 'package.loaded' table and, +** if 'glb' is true, also registers the result in the global table. +** Leaves resulting module on the top. +*/ +LUALIB_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb) { + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, -1, modname); /* LOADED[modname] */ + if (!lua_toboolean(L, -1)) { /* package not already loaded? */ + lua_pop(L, 1); /* remove field */ + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); /* argument to open function */ + lua_call(L, 1, 1); /* call 'openf' to open module */ + lua_pushvalue(L, -1); /* make copy of module (call result) */ + lua_setfield(L, -3, modname); /* LOADED[modname] = module */ + } + lua_remove(L, -2); /* remove LOADED table */ + if (glb) { + lua_pushvalue(L, -1); /* copy of module */ + lua_setglobal(L, modname); /* _G[modname] = module */ + } +} + + +LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s, + const char *p, const char *r) { + const char *wild; + size_t l = strlen(p); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(b, s, wild - s); /* push prefix */ + luaL_addstring(b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after 'p' */ + } + luaL_addstring(b, s); /* push last suffix */ +} + + +LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, + const char *p, const char *r) { + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addgsub(&b, s, p, r); + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + (void)ud; (void)osize; /* not used */ + if (nsize == 0) { + free(ptr); + return NULL; + } + else + return realloc(ptr, nsize); +} + + +static int panic (lua_State *L) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "error object is not a string"; + lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", + msg); + return 0; /* return to Lua to abort */ +} + + +/* +** Emit a warning. '*warnstate' means: +** 0 - warning system is off; +** 1 - ready to start a new message; +** 2 - previous message is to be continued. +*/ +static void warnf (void *ud, const char *message, int tocont) { + int *warnstate = (int *)ud; + if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */ + if (strcmp(message, "@off") == 0) + *warnstate = 0; + else if (strcmp(message, "@on") == 0) + *warnstate = 1; + return; + } + else if (*warnstate == 0) /* warnings off? */ + return; + if (*warnstate == 1) /* previous message was the last? */ + lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ + lua_writestringerror("%s", message); /* write message */ + if (tocont) /* not the last part? */ + *warnstate = 2; /* to be continued */ + else { /* last part */ + lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ + *warnstate = 1; /* ready to start a new message */ + } +} + + +LUALIB_API lua_State *luaL_newstate (void) { + lua_State *L = lua_newstate(l_alloc, NULL); + if (L) { + int *warnstate; /* space for warning state */ + lua_atpanic(L, &panic); + warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0); + luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */ + *warnstate = 0; /* default is warnings off */ + lua_setwarnf(L, warnf, warnstate); + } + return L; +} + + +LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { + lua_Number v = lua_version(L); + if (sz != LUAL_NUMSIZES) /* check numeric types */ + luaL_error(L, "core and library have incompatible numeric types"); + else if (v != ver) + luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", + (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v); +} + diff --git a/Lua/lauxlib.h b/Lua/lauxlib.h new file mode 100644 index 00000000..59fef6af --- /dev/null +++ b/Lua/lauxlib.h @@ -0,0 +1,276 @@ +/* +** $Id: lauxlib.h $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "lua.h" + + +/* global table */ +#define LUA_GNAME "_G" + + +typedef struct luaL_Buffer luaL_Buffer; + + +/* extra error code for 'luaL_loadfilex' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +/* key, in the registry, for table of loaded modules */ +#define LUA_LOADED_TABLE "_LOADED" + + +/* key, in the registry, for table of preloaded loaders */ +#define LUA_PRELOAD_TABLE "_PRELOAD" + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + +#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) + +LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); +#define luaL_checkversion(L) \ + luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) + +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); +LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); +LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int arg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); +LUALIB_API int (luaL_execresult) (lua_State *L, int stat); + + +/* predefined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, + const char *mode); + +#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) + +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + +LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); + +LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s, + const char *p, const char *r); +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, + const char *p, const char *r); + +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); + +LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); + +LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, + const char *msg, int level); + +LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, + lua_CFunction openf, int glb); + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + + +#define luaL_newlibtable(L,l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) + +#define luaL_newlib(L,l) \ + (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) + +#define luaL_argcheck(L, cond,arg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (arg), (extramsg)))) + +#define luaL_argexpected(L,cond,arg,tname) \ + ((void)((cond) || luaL_typeerror(L, (arg), (tname)))) + +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) + + +/* push the value used to represent failure/error */ +#define luaL_pushfail(L) lua_pushnil(L) + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +struct luaL_Buffer { + char *b; /* buffer address */ + size_t size; /* buffer size */ + size_t n; /* number of characters in buffer */ + lua_State *L; + union { + LUAI_MAXALIGN; /* ensure maximum alignment for buffer */ + char b[LUAL_BUFFERSIZE]; /* initial buffer */ + } init; +}; + + +#define luaL_bufflen(bf) ((bf)->n) +#define luaL_buffaddr(bf) ((bf)->b) + + +#define luaL_addchar(B,c) \ + ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ + ((B)->b[(B)->n++] = (c))) + +#define luaL_addsize(B,s) ((B)->n += (s)) + +#define luaL_buffsub(B,s) ((B)->n -= (s)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); +LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); + +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) + +/* }====================================================== */ + + + +/* +** {====================================================== +** File handles for IO library +** ======================================================= +*/ + +/* +** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and +** initial structure 'luaL_Stream' (it may contain other fields +** after that initial structure). +*/ + +#define LUA_FILEHANDLE "FILE*" + + +typedef struct luaL_Stream { + FILE *f; /* stream (NULL for incompletely created streams) */ + lua_CFunction closef; /* to close stream (NULL for closed streams) */ +} luaL_Stream; + +/* }====================================================== */ + +/* +** {================================================================== +** "Abstraction Layer" for basic report of messages and errors +** =================================================================== +*/ + +/* print a string */ +#if !defined(lua_writestring) +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif + +/* print a newline and flush the output */ +#if !defined(lua_writeline) +#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) +#endif + +/* print an error message */ +#if !defined(lua_writestringerror) +#define lua_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) +#endif + +/* }================================================================== */ + + +/* +** {============================================================ +** Compatibility with deprecated conversions +** ============================================================= +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) +#define luaL_optunsigned(L,a,d) \ + ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) + +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) + +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#endif +/* }============================================================ */ + + + +#endif + + diff --git a/Lua/lbaselib.c b/Lua/lbaselib.c new file mode 100644 index 00000000..747fd45a --- /dev/null +++ b/Lua/lbaselib.c @@ -0,0 +1,527 @@ +/* +** $Id: lbaselib.c $ +** Basic library +** See Copyright Notice in lua.h +*/ + +#define lbaselib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + for (i = 1; i <= n; i++) { /* for each argument */ + size_t l; + const char *s = luaL_tolstring(L, i, &l); /* convert it to string */ + if (i > 1) /* not the first element? */ + lua_writestring("\t", 1); /* add a tab before it */ + lua_writestring(s, l); /* print it */ + lua_pop(L, 1); /* pop result */ + } + lua_writeline(); + return 0; +} + + +/* +** Creates a warning with all given arguments. +** Check first for errors; otherwise an error may interrupt +** the composition of a warning, leaving it unfinished. +*/ +static int luaB_warn (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_checkstring(L, 1); /* at least one argument */ + for (i = 2; i <= n; i++) + luaL_checkstring(L, i); /* make sure all arguments are strings */ + for (i = 1; i < n; i++) /* compose warning */ + lua_warning(L, lua_tostring(L, i), 1); + lua_warning(L, lua_tostring(L, n), 0); /* close warning */ + return 0; +} + + +#define SPACECHARS " \f\n\r\t\v" + +static const char *b_str2int (const char *s, int base, lua_Integer *pn) { + lua_Unsigned n = 0; + int neg = 0; + s += strspn(s, SPACECHARS); /* skip initial spaces */ + if (*s == '-') { s++; neg = 1; } /* handle sign */ + else if (*s == '+') s++; + if (!isalnum((unsigned char)*s)) /* no digit? */ + return NULL; + do { + int digit = (isdigit((unsigned char)*s)) ? *s - '0' + : (toupper((unsigned char)*s) - 'A') + 10; + if (digit >= base) return NULL; /* invalid numeral */ + n = n * base + digit; + s++; + } while (isalnum((unsigned char)*s)); + s += strspn(s, SPACECHARS); /* skip trailing spaces */ + *pn = (lua_Integer)((neg) ? (0u - n) : n); + return s; +} + + +static int luaB_tonumber (lua_State *L) { + if (lua_isnoneornil(L, 2)) { /* standard conversion? */ + if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */ + lua_settop(L, 1); /* yes; return it */ + return 1; + } + else { + size_t l; + const char *s = lua_tolstring(L, 1, &l); + if (s != NULL && lua_stringtonumber(L, s) == l + 1) + return 1; /* successful conversion to number */ + /* else not a number */ + luaL_checkany(L, 1); /* (but there must be some parameter) */ + } + } + else { + size_t l; + const char *s; + lua_Integer n = 0; /* to avoid warnings */ + lua_Integer base = luaL_checkinteger(L, 2); + luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ + s = lua_tolstring(L, 1, &l); + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + if (b_str2int(s, (int)base, &n) == s + l) { + lua_pushinteger(L, n); + return 1; + } /* else not a number */ + } /* else not a number */ + luaL_pushfail(L); /* not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = (int)luaL_optinteger(L, 2, 1); + lua_settop(L, 1); + if (lua_type(L, 1) == LUA_TSTRING && level > 0) { + luaL_where(L, level); /* add extra information */ + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); + if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL) + return luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawlen (lua_State *L) { + int t = lua_type(L, 1); + luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, + "table or string"); + lua_pushinteger(L, lua_rawlen(L, 1)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int pushmode (lua_State *L, int oldmode) { + lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational"); + return 1; +} + + +static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", + "isrunning", "generational", "incremental", NULL}; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, + LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC}; + int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; + switch (o) { + case LUA_GCCOUNT: { + int k = lua_gc(L, o); + int b = lua_gc(L, LUA_GCCOUNTB); + lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + int step = (int)luaL_optinteger(L, 2, 0); + int res = lua_gc(L, o, step); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCSETPAUSE: + case LUA_GCSETSTEPMUL: { + int p = (int)luaL_optinteger(L, 2, 0); + int previous = lua_gc(L, o, p); + lua_pushinteger(L, previous); + return 1; + } + case LUA_GCISRUNNING: { + int res = lua_gc(L, o); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCGEN: { + int minormul = (int)luaL_optinteger(L, 2, 0); + int majormul = (int)luaL_optinteger(L, 3, 0); + return pushmode(L, lua_gc(L, o, minormul, majormul)); + } + case LUA_GCINC: { + int pause = (int)luaL_optinteger(L, 2, 0); + int stepmul = (int)luaL_optinteger(L, 3, 0); + int stepsize = (int)luaL_optinteger(L, 4, 0); + return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize)); + } + default: { + int res = lua_gc(L, o); + lua_pushinteger(L, res); + return 1; + } + } +} + + +static int luaB_type (lua_State *L) { + int t = lua_type(L, 1); + luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); + lua_pushstring(L, lua_typename(L, t)); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + luaL_checkany(L, 1); + if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ + lua_pushcfunction(L, luaB_next); /* will return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + } + else { + lua_pushvalue(L, 1); /* argument 'self' to metamethod */ + lua_call(L, 1, 3); /* get 3 values from metamethod */ + } + return 3; +} + + +/* +** Traversal function for 'ipairs' +*/ +static int ipairsaux (lua_State *L) { + lua_Integer i = luaL_checkinteger(L, 2) + 1; + lua_pushinteger(L, i); + return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; +} + + +/* +** 'ipairs' function. Returns 'ipairsaux', given "table", 0. +** (The given "table" may not be a table.) +*/ +static int luaB_ipairs (lua_State *L) { + luaL_checkany(L, 1); + lua_pushcfunction(L, ipairsaux); /* iteration function */ + lua_pushvalue(L, 1); /* state */ + lua_pushinteger(L, 0); /* initial value */ + return 3; +} + + +static int load_aux (lua_State *L, int status, int envidx) { + if (status == LUA_OK) { + if (envidx != 0) { /* 'env' parameter? */ + lua_pushvalue(L, envidx); /* environment for loaded function */ + if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */ + lua_pop(L, 1); /* remove 'env' if not used by previous call */ + } + return 1; + } + else { /* error (message is on top of the stack) */ + luaL_pushfail(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return fail plus error message */ + } +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + const char *mode = luaL_optstring(L, 2, NULL); + int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ + int status = luaL_loadfilex(L, fname, mode); + return load_aux(L, status, env); +} + + +/* +** {====================================================== +** Generic Read function +** ======================================================= +*/ + + +/* +** reserved slot, above all arguments, to hold a copy of the returned +** string to avoid it being collected while parsed. 'load' has four +** optional arguments (chunk, source name, mode, and environment). +*/ +#define RESERVEDSLOT 5 + + +/* +** Reader for generic 'load' function: 'lua_load' uses the +** stack for internal stuff, so the reader cannot change the +** stack top. Instead, it keeps its resulting string in a +** reserved slot inside the stack. +*/ +static const char *generic_reader (lua_State *L, void *ud, size_t *size) { + (void)(ud); /* not used */ + luaL_checkstack(L, 2, "too many nested functions"); + lua_pushvalue(L, 1); /* get function */ + lua_call(L, 0, 1); /* call it */ + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* pop result */ + *size = 0; + return NULL; + } + else if (!lua_isstring(L, -1)) + luaL_error(L, "reader function must return a string"); + lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ + return lua_tolstring(L, RESERVEDSLOT, size); +} + + +static int luaB_load (lua_State *L) { + int status; + size_t l; + const char *s = lua_tolstring(L, 1, &l); + const char *mode = luaL_optstring(L, 3, "bt"); + int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ + if (s != NULL) { /* loading a string? */ + const char *chunkname = luaL_optstring(L, 2, s); + status = luaL_loadbufferx(L, s, l, chunkname, mode); + } + else { /* loading from a reader function */ + const char *chunkname = luaL_optstring(L, 2, "=(load)"); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, RESERVEDSLOT); /* create reserved slot */ + status = lua_load(L, generic_reader, NULL, chunkname, mode); + } + return load_aux(L, status, env); +} + +/* }====================================================== */ + + +static int dofilecont (lua_State *L, int d1, lua_KContext d2) { + (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */ + return lua_gettop(L) - 1; +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + lua_settop(L, 1); + if (luaL_loadfile(L, fname) != LUA_OK) + return lua_error(L); + lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); + return dofilecont(L, 0, 0); +} + + +static int luaB_assert (lua_State *L) { + if (lua_toboolean(L, 1)) /* condition is true? */ + return lua_gettop(L); /* return all arguments */ + else { /* error */ + luaL_checkany(L, 1); /* there must be a condition */ + lua_remove(L, 1); /* remove it */ + lua_pushliteral(L, "assertion failed!"); /* default message */ + lua_settop(L, 1); /* leave only message (default if no other one) */ + return luaB_error(L); /* call 'error' */ + } +} + + +static int luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + lua_Integer i = luaL_checkinteger(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - (int)i; + } +} + + +/* +** Continuation function for 'pcall' and 'xpcall'. Both functions +** already pushed a 'true' before doing the call, so in case of success +** 'finishpcall' only has to return everything in the stack minus +** 'extra' values (where 'extra' is exactly the number of items to be +** ignored). +*/ +static int finishpcall (lua_State *L, int status, lua_KContext extra) { + if (status != LUA_OK && status != LUA_YIELD) { /* error? */ + lua_pushboolean(L, 0); /* first result (false) */ + lua_pushvalue(L, -2); /* error message */ + return 2; /* return false, msg */ + } + else + return lua_gettop(L) - (int)extra; /* return all results */ +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + lua_pushboolean(L, 1); /* first result if no errors */ + lua_insert(L, 1); /* put it in place */ + status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall); + return finishpcall(L, status, 0); +} + + +/* +** Do a protected call with error handling. After 'lua_rotate', the +** stack will have ; so, the function passes +** 2 to 'finishpcall' to skip the 2 first values when returning results. +*/ +static int luaB_xpcall (lua_State *L) { + int status; + int n = lua_gettop(L); + luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */ + lua_pushboolean(L, 1); /* first result */ + lua_pushvalue(L, 1); /* function */ + lua_rotate(L, 3, 2); /* move them below function's arguments */ + status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall); + return finishpcall(L, status, 2); +} + + +static int luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + luaL_tolstring(L, 1, NULL); + return 1; +} + + +static const luaL_Reg base_funcs[] = { + {"assert", luaB_assert}, + {"collectgarbage", luaB_collectgarbage}, + {"dofile", luaB_dofile}, + {"error", luaB_error}, + {"getmetatable", luaB_getmetatable}, + {"ipairs", luaB_ipairs}, + {"loadfile", luaB_loadfile}, + {"load", luaB_load}, + {"next", luaB_next}, + {"pairs", luaB_pairs}, + {"pcall", luaB_pcall}, + {"print", luaB_print}, + {"warn", luaB_warn}, + {"rawequal", luaB_rawequal}, + {"rawlen", luaB_rawlen}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"select", luaB_select}, + {"setmetatable", luaB_setmetatable}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"xpcall", luaB_xpcall}, + /* placeholders */ + {LUA_GNAME, NULL}, + {"_VERSION", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_base (lua_State *L) { + /* open lib into global table */ + lua_pushglobaltable(L); + luaL_setfuncs(L, base_funcs, 0); + /* set global _G */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, LUA_GNAME); + /* set global _VERSION */ + lua_pushliteral(L, LUA_VERSION); + lua_setfield(L, -2, "_VERSION"); + return 1; +} + diff --git a/Lua/lcode.c b/Lua/lcode.c new file mode 100644 index 00000000..6f241c94 --- /dev/null +++ b/Lua/lcode.c @@ -0,0 +1,1814 @@ +/* +** $Id: lcode.c $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#define lcode_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstring.h" +#include "ltable.h" +#include "lvm.h" + + +/* Maximum number of registers in a Lua function (must fit in 8 bits) */ +#define MAXREGS 255 + + +#define hasjumps(e) ((e)->t != (e)->f) + + +static int codesJ (FuncState *fs, OpCode o, int sj, int k); + + + +/* semantic error */ +l_noret luaK_semerror (LexState *ls, const char *msg) { + ls->t.token = 0; /* remove "near " from final message */ + luaX_syntaxerror(ls, msg); +} + + +/* +** If expression is a numeric constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +static int tonumeral (const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a numeral */ + switch (e->k) { + case VKINT: + if (v) setivalue(v, e->u.ival); + return 1; + case VKFLT: + if (v) setfltvalue(v, e->u.nval); + return 1; + default: return 0; + } +} + + +/* +** Get the constant value from a constant expression +*/ +static TValue *const2val (FuncState *fs, const expdesc *e) { + lua_assert(e->k == VCONST); + return &fs->ls->dyd->actvar.arr[e->u.info].k; +} + + +/* +** If expression is a constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a constant */ + switch (e->k) { + case VFALSE: + setbfvalue(v); + return 1; + case VTRUE: + setbtvalue(v); + return 1; + case VNIL: + setnilvalue(v); + return 1; + case VKSTR: { + setsvalue(fs->ls->L, v, e->u.strval); + return 1; + } + case VCONST: { + setobj(fs->ls->L, v, const2val(fs, e)); + return 1; + } + default: return tonumeral(e, v); + } +} + + +/* +** Return the previous instruction of the current code. If there +** may be a jump target between the current instruction and the +** previous one, return an invalid instruction (to avoid wrong +** optimizations). +*/ +static Instruction *previousinstruction (FuncState *fs) { + static const Instruction invalidinstruction = ~(Instruction)0; + if (fs->pc > fs->lasttarget) + return &fs->f->code[fs->pc - 1]; /* previous instruction */ + else + return cast(Instruction*, &invalidinstruction); +} + + +/* +** Create a OP_LOADNIL instruction, but try to optimize: if the previous +** instruction is also OP_LOADNIL and ranges are compatible, adjust +** range of previous instruction instead of emitting a new one. (For +** instance, 'local a; local b' will generate a single opcode.) +*/ +void luaK_nil (FuncState *fs, int from, int n) { + int l = from + n - 1; /* last register to set nil */ + Instruction *previous = previousinstruction(fs); + if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ + int pfrom = GETARG_A(*previous); /* get previous range */ + int pl = pfrom + GETARG_B(*previous); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ + if (pl > l) l = pl; /* l = max(l, pl) */ + SETARG_A(*previous, from); + SETARG_B(*previous, l - from); + return; + } /* else go through */ + } + luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ +} + + +/* +** Gets the destination address of a jump instruction. Used to traverse +** a list of jumps. +*/ +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sJ(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +/* +** Fix jump instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua) +*/ +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + lua_assert(dest != NO_JUMP); + if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ)) + luaX_syntaxerror(fs->ls, "control structure too long"); + lua_assert(GET_OPCODE(*jmp) == OP_JMP); + SETARG_sJ(*jmp, offset); +} + + +/* +** Concatenate jump-list 'l2' into jump-list 'l1' +*/ +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; /* nothing to concatenate? */ + else if (*l1 == NO_JUMP) /* no original list? */ + *l1 = l2; /* 'l1' points to 'l2' */ + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); /* last element links to 'l2' */ + } +} + + +/* +** Create a jump instruction and return its position, so its destination +** can be fixed later (with 'fixjump'). +*/ +int luaK_jump (FuncState *fs) { + return codesJ(fs, OP_JMP, NO_JUMP, 0); +} + + +/* +** Code a 'return' instruction +*/ +void luaK_ret (FuncState *fs, int first, int nret) { + OpCode op; + switch (nret) { + case 0: op = OP_RETURN0; break; + case 1: op = OP_RETURN1; break; + default: op = OP_RETURN; break; + } + luaK_codeABC(fs, op, first, nret + 1, 0); +} + + +/* +** Code a "conditional jump", that is, a test or comparison opcode +** followed by a jump. Return jump position. +*/ +static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) { + luaK_codeABCk(fs, op, A, B, C, k); + return luaK_jump(fs); +} + + +/* +** returns current 'pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +/* +** Returns the position of the instruction "controlling" a given +** jump (that is, its condition), or the jump itself if it is +** unconditional. +*/ +static Instruction *getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** Patch destination register for a TESTSET instruction. +** If instruction in position 'node' is not a TESTSET, return 0 ("fails"). +** Otherwise, if 'reg' is not 'NO_REG', set it as the destination +** register. Otherwise, change instruction to a simple 'TEST' (produces +** no register value) +*/ +static int patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else { + /* no register to put value or register already has the value; + change instruction to simple test */ + *i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i)); + } + return 1; +} + + +/* +** Traverse a list of tests ensuring no one produces a value +*/ +static void removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +/* +** Traverse a list of tests, patching their destination address and +** registers: tests producing values jump to 'vtarget' (and put their +** values in 'reg'), other tests jump to 'dtarget'. +*/ +static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +/* +** Path all jumps in 'list' to jump to 'target'. +** (The assert means that we cannot fix a jump to a forward address +** because we only know addresses once code is generated.) +*/ +void luaK_patchlist (FuncState *fs, int list, int target) { + lua_assert(target <= fs->pc); + patchlistaux(fs, list, target, NO_REG, target); +} + + +void luaK_patchtohere (FuncState *fs, int list) { + int hr = luaK_getlabel(fs); /* mark "here" as a jump target */ + luaK_patchlist(fs, list, hr); +} + + +/* +** MAXimum number of successive Instructions WiTHout ABSolute line +** information. +*/ +#if !defined(MAXIWTHABS) +#define MAXIWTHABS 120 +#endif + + +/* limit for difference between lines in relative line info. */ +#define LIMLINEDIFF 0x80 + + +/* +** Save line info for a new instruction. If difference from last line +** does not fit in a byte, of after that many instructions, save a new +** absolute line info; (in that case, the special value 'ABSLINEINFO' +** in 'lineinfo' signals the existence of this absolute information.) +** Otherwise, store the difference from last line in 'lineinfo'. +*/ +static void savelineinfo (FuncState *fs, Proto *f, int line) { + int linedif = line - fs->previousline; + int pc = fs->pc - 1; /* last instruction coded */ + if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) { + luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo, + f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines"); + f->abslineinfo[fs->nabslineinfo].pc = pc; + f->abslineinfo[fs->nabslineinfo++].line = line; + linedif = ABSLINEINFO; /* signal that there is absolute information */ + fs->iwthabs = 0; /* restart counter */ + } + luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte, + MAX_INT, "opcodes"); + f->lineinfo[pc] = linedif; + fs->previousline = line; /* last line saved */ +} + + +/* +** Remove line information from the last instruction. +** If line information for that instruction is absolute, set 'iwthabs' +** above its max to force the new (replacing) instruction to have +** absolute line info, too. +*/ +static void removelastlineinfo (FuncState *fs) { + Proto *f = fs->f; + int pc = fs->pc - 1; /* last instruction coded */ + if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */ + fs->previousline -= f->lineinfo[pc]; /* correct last line saved */ + fs->iwthabs--; /* undo previous increment */ + } + else { /* absolute line information */ + lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc); + fs->nabslineinfo--; /* remove it */ + fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */ + } +} + + +/* +** Remove the last instruction created, correcting line information +** accordingly. +*/ +static void removelastinstruction (FuncState *fs) { + removelastlineinfo(fs); + fs->pc--; +} + + +/* +** Emit instruction 'i', checking for array sizes and saving also its +** line information. Return 'i' position. +*/ +int luaK_code (FuncState *fs, Instruction i) { + Proto *f = fs->f; + /* put new instruction in code array */ + luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, + MAX_INT, "opcodes"); + f->code[fs->pc++] = i; + savelineinfo(fs, f, fs->ls->lastline); + return fs->pc - 1; /* index of new instruction */ +} + + +/* +** Format and emit an 'iABC' instruction. (Assertions check consistency +** of parameters versus opcode.) +*/ +int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) { + lua_assert(getOpMode(o) == iABC); + lua_assert(a <= MAXARG_A && b <= MAXARG_B && + c <= MAXARG_C && (k & ~1) == 0); + return luaK_code(fs, CREATE_ABCk(o, a, b, c, k)); +} + + +/* +** Format and emit an 'iABx' instruction. +*/ +int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { + lua_assert(getOpMode(o) == iABx); + lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, a, bc)); +} + + +/* +** Format and emit an 'iAsBx' instruction. +*/ +int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) { + unsigned int b = bc + OFFSET_sBx; + lua_assert(getOpMode(o) == iAsBx); + lua_assert(a <= MAXARG_A && b <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, a, b)); +} + + +/* +** Format and emit an 'isJ' instruction. +*/ +static int codesJ (FuncState *fs, OpCode o, int sj, int k) { + unsigned int j = sj + OFFSET_sJ; + lua_assert(getOpMode(o) == isJ); + lua_assert(j <= MAXARG_sJ && (k & ~1) == 0); + return luaK_code(fs, CREATE_sJ(o, j, k)); +} + + +/* +** Emit an "extra argument" instruction (format 'iAx') +*/ +static int codeextraarg (FuncState *fs, int a) { + lua_assert(a <= MAXARG_Ax); + return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); +} + + +/* +** Emit a "load constant" instruction, using either 'OP_LOADK' +** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' +** instruction with "extra argument". +*/ +static int luaK_codek (FuncState *fs, int reg, int k) { + if (k <= MAXARG_Bx) + return luaK_codeABx(fs, OP_LOADK, reg, k); + else { + int p = luaK_codeABx(fs, OP_LOADKX, reg, 0); + codeextraarg(fs, k); + return p; + } +} + + +/* +** Check register-stack level, keeping track of its maximum size +** in field 'maxstacksize' +*/ +void luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + if (newstack >= MAXREGS) + luaX_syntaxerror(fs->ls, + "function or expression needs too many registers"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +/* +** Reserve 'n' registers in register stack +*/ +void luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg += n; +} + + +/* +** Free register 'reg', if it is neither a constant index nor +** a local variable. +) +*/ +static void freereg (FuncState *fs, int reg) { + if (reg >= luaY_nvarstack(fs)) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +/* +** Free two registers in proper order +*/ +static void freeregs (FuncState *fs, int r1, int r2) { + if (r1 > r2) { + freereg(fs, r1); + freereg(fs, r2); + } + else { + freereg(fs, r2); + freereg(fs, r1); + } +} + + +/* +** Free register used by expression 'e' (if any) +*/ +static void freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.info); +} + + +/* +** Free registers used by expressions 'e1' and 'e2' (if any) in proper +** order. +*/ +static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) { + int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1; + int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1; + freeregs(fs, r1, r2); +} + + +/* +** Add constant 'v' to prototype's list of constants (field 'k'). +** Use scanner's table to cache position of constants in constant list +** and try to reuse constants. Because some values should not be used +** as keys (nil cannot be a key, integer keys can collapse with float +** keys), the caller must provide a useful 'key' for indexing the cache. +*/ +static int addk (FuncState *fs, TValue *key, TValue *v) { + lua_State *L = fs->ls->L; + Proto *f = fs->f; + TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */ + int k, oldsize; + if (ttisinteger(idx)) { /* is there an index there? */ + k = cast_int(ivalue(idx)); + /* correct value? (warning: must distinguish floats from integers!) */ + if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) && + luaV_rawequalobj(&f->k[k], v)) + return k; /* reuse index */ + } + /* constant not found; create a new entry */ + oldsize = f->sizek; + k = fs->nk; + /* numerical value does not need GC barrier; + table has no metatable, so it does not need to invalidate cache */ + setivalue(idx, k); + luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); + while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[k], v); + fs->nk++; + luaC_barrier(L, f, v); + return k; +} + + +/* +** Add a string to list of constants and return its index. +*/ +static int stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->ls->L, &o, s); + return addk(fs, &o, &o); /* use string itself as key */ +} + + +/* +** Add an integer to list of constants and return its index. +** Integers use userdata as keys to avoid collision with floats with +** same value; conversion to 'void*' is used only for hashing, so there +** are no "precision" problems. +*/ +static int luaK_intK (FuncState *fs, lua_Integer n) { + TValue k, o; + setpvalue(&k, cast_voidp(cast_sizet(n))); + setivalue(&o, n); + return addk(fs, &k, &o); +} + +/* +** Add a float to list of constants and return its index. +*/ +static int luaK_numberK (FuncState *fs, lua_Number r) { + TValue o; + setfltvalue(&o, r); + return addk(fs, &o, &o); /* use number itself as key */ +} + + +/* +** Add a false to list of constants and return its index. +*/ +static int boolF (FuncState *fs) { + TValue o; + setbfvalue(&o); + return addk(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add a true to list of constants and return its index. +*/ +static int boolT (FuncState *fs) { + TValue o; + setbtvalue(&o); + return addk(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add nil to list of constants and return its index. +*/ +static int nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself to represent nil */ + sethvalue(fs->ls->L, &k, fs->ls->h); + return addk(fs, &k, &v); +} + + +/* +** Check whether 'i' can be stored in an 'sC' operand. Equivalent to +** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of +** overflows in the hidden addition inside 'int2sC'. +*/ +static int fitsC (lua_Integer i) { + return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C)); +} + + +/* +** Check whether 'i' can be stored in an 'sBx' operand. +*/ +static int fitsBx (lua_Integer i) { + return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx); +} + + +void luaK_int (FuncState *fs, int reg, lua_Integer i) { + if (fitsBx(i)) + luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i)); + else + luaK_codek(fs, reg, luaK_intK(fs, i)); +} + + +static void luaK_float (FuncState *fs, int reg, lua_Number f) { + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi)) + luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi)); + else + luaK_codek(fs, reg, luaK_numberK(fs, f)); +} + + +/* +** Convert a constant in 'v' into an expression description 'e' +*/ +static void const2exp (TValue *v, expdesc *e) { + switch (ttypetag(v)) { + case LUA_VNUMINT: + e->k = VKINT; e->u.ival = ivalue(v); + break; + case LUA_VNUMFLT: + e->k = VKFLT; e->u.nval = fltvalue(v); + break; + case LUA_VFALSE: + e->k = VFALSE; + break; + case LUA_VTRUE: + e->k = VTRUE; + break; + case LUA_VNIL: + e->k = VNIL; + break; + case LUA_VSHRSTR: case LUA_VLNGSTR: + e->k = VKSTR; e->u.strval = tsvalue(v); + break; + default: lua_assert(0); + } +} + + +/* +** Fix an expression to return the number of results 'nresults'. +** 'e' must be a multi-ret expression (function call or vararg). +*/ +void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + Instruction *pc = &getinstruction(fs, e); + if (e->k == VCALL) /* expression is an open function call? */ + SETARG_C(*pc, nresults + 1); + else { + lua_assert(e->k == VVARARG); + SETARG_C(*pc, nresults + 1); + SETARG_A(*pc, fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +/* +** Convert a VKSTR to a VK +*/ +static void str2K (FuncState *fs, expdesc *e) { + lua_assert(e->k == VKSTR); + e->u.info = stringK(fs, e->u.strval); + e->k = VK; +} + + +/* +** Fix an expression to return one result. +** If expression is not a multi-ret expression (function call or +** vararg), it already returns one result, so nothing needs to be done. +** Function calls become VNONRELOC expressions (as its result comes +** fixed in the base register of the call), while vararg expressions +** become VRELOC (as OP_VARARG puts its results where it wants). +** (Calls are created returning one result, so that does not need +** to be fixed.) +*/ +void luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + /* already returns 1 value */ + lua_assert(GETARG_C(getinstruction(fs, e)) == 2); + e->k = VNONRELOC; /* result has fixed position */ + e->u.info = GETARG_A(getinstruction(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_C(getinstruction(fs, e), 2); + e->k = VRELOC; /* can relocate its simple result */ + } +} + + +/* +** Ensure that expression 'e' is not a variable (nor a constant). +** (Expression still may have jump lists.) +*/ +void luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VCONST: { + const2exp(const2val(fs, e), e); + break; + } + case VLOCAL: { /* already in a register */ + e->u.info = e->u.var.sidx; + e->k = VNONRELOC; /* becomes a non-relocatable value */ + break; + } + case VUPVAL: { /* move value to some (pending) register */ + e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); + e->k = VRELOC; + break; + } + case VINDEXUP: { + e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXI: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXSTR: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXED: { + freeregs(fs, e->u.ind.t, e->u.ind.idx); + e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VVARARG: case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +/* +** Ensures expression value is in register 'reg' (and therefore +** 'e' will become a non-relocatable expression). +** (Expression still may have jump lists.) +*/ +static void discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: { + luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0); + break; + } + case VTRUE: { + luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0); + break; + } + case VKSTR: { + str2K(fs, e); + } /* FALLTHROUGH */ + case VK: { + luaK_codek(fs, reg, e->u.info); + break; + } + case VKFLT: { + luaK_float(fs, reg, e->u.nval); + break; + } + case VKINT: { + luaK_int(fs, reg, e->u.ival); + break; + } + case VRELOC: { + Instruction *pc = &getinstruction(fs, e); + SETARG_A(*pc, reg); /* instruction will put result in 'reg' */ + break; + } + case VNONRELOC: { + if (reg != e->u.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); + break; + } + default: { + lua_assert(e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.info = reg; + e->k = VNONRELOC; +} + + +/* +** Ensures expression value is in any register. +** (Expression still may have jump lists.) +*/ +static void discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { /* no fixed register yet? */ + luaK_reserveregs(fs, 1); /* get a register */ + discharge2reg(fs, e, fs->freereg-1); /* put value there */ + } +} + + +static int code_loadbool (FuncState *fs, int A, OpCode op) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, op, A, 0, 0); +} + + +/* +** check whether list has any jump that do not produce a value +** or produce an inverted value +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +/* +** Ensures final expression result (which includes results from its +** jump lists) is in register 'reg'. +** If expression has jumps, need to patch these jumps either to +** its final position or to "load" instructions (for those tests +** that do not produce values). +*/ +static void exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) /* expression itself is a test? */ + luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */ + p_t = code_loadbool(fs, reg, OP_LOADTRUE); + /* jump around these booleans if 'e' is not a test */ + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.info = reg; + e->k = VNONRELOC; +} + + +/* +** Ensures final expression result is in next available register. +*/ +void luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +/* +** Ensures final expression result is in some (any) register +** and return that register. +*/ +int luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { /* expression already has a register? */ + if (!hasjumps(e)) /* no jumps? */ + return e->u.info; /* result is already in a register */ + if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.info); /* put final result in it */ + return e->u.info; + } + } + luaK_exp2nextreg(fs, e); /* otherwise, use next available register */ + return e->u.info; +} + + +/* +** Ensures final expression result is either in a register +** or in an upvalue. +*/ +void luaK_exp2anyregup (FuncState *fs, expdesc *e) { + if (e->k != VUPVAL || hasjumps(e)) + luaK_exp2anyreg(fs, e); +} + + +/* +** Ensures final expression result is either in a register +** or it is a constant. +*/ +void luaK_exp2val (FuncState *fs, expdesc *e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +/* +** Try to make 'e' a K expression with an index in the range of R/K +** indices. Return true iff succeeded. +*/ +static int luaK_exp2K (FuncState *fs, expdesc *e) { + if (!hasjumps(e)) { + int info; + switch (e->k) { /* move constants to 'k' */ + case VTRUE: info = boolT(fs); break; + case VFALSE: info = boolF(fs); break; + case VNIL: info = nilK(fs); break; + case VKINT: info = luaK_intK(fs, e->u.ival); break; + case VKFLT: info = luaK_numberK(fs, e->u.nval); break; + case VKSTR: info = stringK(fs, e->u.strval); break; + case VK: info = e->u.info; break; + default: return 0; /* not a constant */ + } + if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */ + e->k = VK; /* make expression a 'K' expression */ + e->u.info = info; + return 1; + } + } + /* else, expression doesn't fit; leave it unchanged */ + return 0; +} + + +/* +** Ensures final expression result is in a valid R/K index +** (that is, it is either in a register or in 'k' with an index +** in the range of R/K indices). +** Returns 1 iff expression is K. +*/ +int luaK_exp2RK (FuncState *fs, expdesc *e) { + if (luaK_exp2K(fs, e)) + return 1; + else { /* not a constant in the right range: put it in a register */ + luaK_exp2anyreg(fs, e); + return 0; + } +} + + +static void codeABRK (FuncState *fs, OpCode o, int a, int b, + expdesc *ec) { + int k = luaK_exp2RK(fs, ec); + luaK_codeABCk(fs, o, a, b, ec->u.info, k); +} + + +/* +** Generate code to store result of expression 'ex' into variable 'var'. +*/ +void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */ + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); + break; + } + case VINDEXUP: { + codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXI: { + codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXSTR: { + codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXED: { + codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); + break; + } + default: lua_assert(0); /* invalid var kind to store */ + } + freeexp(fs, ex); +} + + +/* +** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). +*/ +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int ereg; + luaK_exp2anyreg(fs, e); + ereg = e->u.info; /* register where 'e' was placed */ + freeexp(fs, e); + e->u.info = fs->freereg; /* base register for op_self */ + e->k = VNONRELOC; /* self expression has a fixed register */ + luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ + codeABRK(fs, OP_SELF, e->u.info, ereg, key); + freeexp(fs, key); +} + + +/* +** Negate condition 'e' (where 'e' is a comparison). +*/ +static void negatecondition (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_k(*pc, (GETARG_k(*pc) ^ 1)); +} + + +/* +** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' +** is true, code will jump if 'e' is true.) Return jump position. +** Optimize when 'e' is 'not' something, inverting the condition +** and removing the 'not'. +*/ +static int jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOC) { + Instruction ie = getinstruction(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + removelastinstruction(fs); /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond); +} + + +/* +** Emit code to go through if 'e' is true, jump otherwise. +*/ +void luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { /* condition? */ + negatecondition(fs, e); /* jump when it is false */ + pc = e->u.info; /* save jump position */ + break; + } + case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 0); /* jump when false */ + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert new jump in false list */ + luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */ + e->t = NO_JUMP; +} + + +/* +** Emit code to go through if 'e' is false, jump otherwise. +*/ +void luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { + pc = e->u.info; /* already jump if true */ + break; + } + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 1); /* jump if true */ + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */ + luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */ + e->f = NO_JUMP; +} + + +/* +** Code 'not e', doing constant folding. +*/ +static void codenot (FuncState *fs, expdesc *e) { + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; /* true == not nil == not false */ + break; + } + case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { + e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */ + break; + } + case VJMP: { + negatecondition(fs, e); + break; + } + case VRELOC: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0); + e->k = VRELOC; + break; + } + default: lua_assert(0); /* cannot happen */ + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); /* values are useless when negated */ + removevalues(fs, e->t); +} + + +/* +** Check whether expression 'e' is a small literal string +*/ +static int isKstr (FuncState *fs, expdesc *e) { + return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && + ttisshrstring(&fs->f->k[e->u.info])); +} + +/* +** Check whether expression 'e' is a literal integer. +*/ +int luaK_isKint (expdesc *e) { + return (e->k == VKINT && !hasjumps(e)); +} + + +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register C +*/ +static int isCint (expdesc *e) { + return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C)); +} + + +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register sC +*/ +static int isSCint (expdesc *e) { + return luaK_isKint(e) && fitsC(e->u.ival); +} + + +/* +** Check whether expression 'e' is a literal integer or float in +** proper range to fit in a register (sB or sC). +*/ +static int isSCnumber (expdesc *e, int *pi, int *isfloat) { + lua_Integer i; + if (e->k == VKINT) + i = e->u.ival; + else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq)) + *isfloat = 1; + else + return 0; /* not a number */ + if (!hasjumps(e) && fitsC(i)) { + *pi = int2sC(cast_int(i)); + return 1; + } + else + return 0; +} + + +/* +** Create expression 't[k]'. 't' must have its final result already in a +** register or upvalue. Upvalues can only be indexed by literal strings. +** Keys can be literal strings in the constant table or arbitrary +** values in registers. +*/ +void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + if (k->k == VKSTR) + str2K(fs, k); + lua_assert(!hasjumps(t) && + (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); + if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ + luaK_exp2anyreg(fs, t); /* put it in a register */ + if (t->k == VUPVAL) { + t->u.ind.t = t->u.info; /* upvalue index */ + t->u.ind.idx = k->u.info; /* literal string */ + t->k = VINDEXUP; + } + else { + /* register index of the table */ + t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info; + if (isKstr(fs, k)) { + t->u.ind.idx = k->u.info; /* literal string */ + t->k = VINDEXSTR; + } + else if (isCint(k)) { + t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */ + t->k = VINDEXI; + } + else { + t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */ + t->k = VINDEXED; + } + } +} + + +/* +** Return false if folding can raise an error. +** Bitwise operations need operands convertible to integers; division +** operations cannot have 0 as divisor. +*/ +static int validop (int op, TValue *v1, TValue *v2) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */ + lua_Integer i; + return (tointegerns(v1, &i) && tointegerns(v2, &i)); + } + case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */ + return (nvalue(v2) != 0); + default: return 1; /* everything else is valid */ + } +} + + +/* +** Try to "constant-fold" an operation; return 1 iff successful. +** (In this case, 'e1' has the final result.) +*/ +static int constfolding (FuncState *fs, int op, expdesc *e1, + const expdesc *e2) { + TValue v1, v2, res; + if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) + return 0; /* non-numeric operands or not safe to fold */ + luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ + if (ttisinteger(&res)) { + e1->k = VKINT; + e1->u.ival = ivalue(&res); + } + else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ + lua_Number n = fltvalue(&res); + if (luai_numisnan(n) || n == 0) + return 0; + e1->k = VKFLT; + e1->u.nval = n; + } + return 1; +} + + +/* +** Emit code for unary expressions that "produce values" +** (everything but 'not'). +** Expression to produce final result will be encoded in 'e'. +*/ +static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) { + int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ + e->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" +** (everything but logical operators 'and'/'or' and comparison +** operators). +** Expression to produce final result will be encoded in 'e1'. +*/ +static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int v2, int flip, int line, + OpCode mmop, TMS event) { + int v1 = luaK_exp2anyreg(fs, e1); + int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0); + freeexps(fs, e1, e2); + e1->u.info = pc; + e1->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); + luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" over +** two registers. +*/ +static void codebinexpval (FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int line) { + int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */ + lua_assert(OP_ADD <= op && op <= OP_SHR); + finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, + cast(TMS, (op - OP_ADD) + TM_ADD)); +} + + +/* +** Code binary operators with immediate operands. +*/ +static void codebini (FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int flip, int line, + TMS event) { + int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */ + lua_assert(e2->k == VKINT); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event); +} + + +/* Try to code a binary operator negating its second operand. +** For the metamethod, 2nd operand must keep its original value. +*/ +static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int line, TMS event) { + if (!luaK_isKint(e2)) + return 0; /* not an integer constant */ + else { + lua_Integer i2 = e2->u.ival; + if (!(fitsC(i2) && fitsC(-i2))) + return 0; /* not in the proper range */ + else { /* operating a small integer constant */ + int v2 = cast_int(i2); + finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event); + /* correct metamethod argument */ + SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2)); + return 1; /* successfully coded */ + } + } +} + + +static void swapexps (expdesc *e1, expdesc *e2) { + expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */ +} + + +/* +** Code arithmetic operators ('+', '-', ...). If second operand is a +** constant in the proper range, use variant opcodes with K operands. +*/ +static void codearith (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + TMS event = cast(TMS, opr + TM_ADD); + if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ + int v2 = e2->u.info; /* K index */ + OpCode op = cast(OpCode, opr + OP_ADDK); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); + } + else { /* 'e2' is neither an immediate nor a K operand */ + OpCode op = cast(OpCode, opr + OP_ADD); + if (flip) + swapexps(e1, e2); /* back to original order */ + codebinexpval(fs, op, e1, e2, line); /* use standard operators */ + } +} + + +/* +** Code commutative operators ('+', '*'). If first operand is a +** numeric constant, change order of operands to try to use an +** immediate or K operator. +*/ +static void codecommutative (FuncState *fs, BinOpr op, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */ + swapexps(e1, e2); /* change order */ + flip = 1; + } + if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ + codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD); + else + codearith(fs, op, e1, e2, flip, line); +} + + +/* +** Code bitwise operations; they are all associative, so the function +** tries to put an integer constant as the 2nd operand (a K operand). +*/ +static void codebitwise (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + int v2; + OpCode op; + if (e1->k == VKINT && luaK_exp2RK(fs, e1)) { + swapexps(e1, e2); /* 'e2' will be the constant operand */ + flip = 1; + } + else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */ + op = cast(OpCode, opr + OP_ADD); + codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */ + return; + } + v2 = e2->u.info; /* index in K array */ + op = cast(OpCode, opr + OP_ADDK); + lua_assert(ttisinteger(&fs->f->k[v2])); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, + cast(TMS, opr + TM_ADD)); +} + + +/* +** Emit code for order comparisons. When using an immediate operand, +** 'isfloat' tells whether the original value was a float. +*/ +static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; + if (isSCnumber(e2, &im, &isfloat)) { + /* use immediate operand */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = im; + op = cast(OpCode, (op - OP_LT) + OP_LTI); + } + else if (isSCnumber(e1, &im, &isfloat)) { + /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ + r1 = luaK_exp2anyreg(fs, e2); + r2 = im; + op = (op == OP_LT) ? OP_GTI : OP_GEI; + } + else { /* regular case, compare two registers */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = luaK_exp2anyreg(fs, e2); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); + e1->k = VJMP; +} + + +/* +** Emit code for equality comparisons ('==', '~='). +** 'e1' was already put as RK by 'luaK_infix'. +*/ +static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; /* not needed here, but kept for symmetry */ + OpCode op; + if (e1->k != VNONRELOC) { + lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT); + swapexps(e1, e2); + } + r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */ + if (isSCnumber(e2, &im, &isfloat)) { + op = OP_EQI; + r2 = im; /* immediate operand */ + } + else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */ + op = OP_EQK; + r2 = e2->u.info; /* constant index */ + } + else { + op = OP_EQ; /* will compare two registers */ + r2 = luaK_exp2anyreg(fs, e2); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ)); + e1->k = VJMP; +} + + +/* +** Apply prefix operation 'op' to expression 'e'. +*/ +void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { + static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; + luaK_dischargevars(fs, e); + switch (op) { + case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ + if (constfolding(fs, op + LUA_OPUNM, e, &ef)) + break; + /* else */ /* FALLTHROUGH */ + case OPR_LEN: + codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); + break; + case OPR_NOT: codenot(fs, e); break; + default: lua_assert(0); + } +} + + +/* +** Process 1st operand 'v' of binary operation 'op' before reading +** 2nd operand. +*/ +void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + luaK_dischargevars(fs, v); + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the stack */ + break; + } + case OPR_ADD: case OPR_SUB: + case OPR_MUL: case OPR_DIV: case OPR_IDIV: + case OPR_MOD: case OPR_POW: + case OPR_BAND: case OPR_BOR: case OPR_BXOR: + case OPR_SHL: case OPR_SHR: { + if (!tonumeral(v, NULL)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be folded with 2nd operand */ + break; + } + case OPR_EQ: case OPR_NE: { + if (!tonumeral(v, NULL)) + luaK_exp2RK(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + case OPR_LT: case OPR_LE: + case OPR_GT: case OPR_GE: { + int dummy, dummy2; + if (!isSCnumber(v, &dummy, &dummy2)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + default: lua_assert(0); + } +} + +/* +** Create code for '(e1 .. e2)'. +** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))', +** because concatenation is right associative), merge both CONCATs. +*/ +static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) { + Instruction *ie2 = previousinstruction(fs); + if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */ + int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */ + lua_assert(e1->u.info + 1 == GETARG_A(*ie2)); + freeexp(fs, e2); + SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */ + SETARG_B(*ie2, n + 1); /* will concatenate one more element */ + } + else { /* 'e2' is not a concatenation */ + luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */ + freeexp(fs, e2); + luaK_fixline(fs, line); + } +} + + +/* +** Finalize code for binary operation, after reading 2nd operand. +*/ +void luaK_posfix (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + luaK_dischargevars(fs, e2); + if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2)) + return; /* done by folding */ + switch (opr) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */ + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */ + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { /* e1 .. e2 */ + luaK_exp2nextreg(fs, e2); + codeconcat(fs, e1, e2, line); + break; + } + case OPR_ADD: case OPR_MUL: { + codecommutative(fs, opr, e1, e2, line); + break; + } + case OPR_SUB: { + if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB)) + break; /* coded as (r1 + -I) */ + /* ELSE */ + } /* FALLTHROUGH */ + case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: { + codearith(fs, opr, e1, e2, 0, line); + break; + } + case OPR_BAND: case OPR_BOR: case OPR_BXOR: { + codebitwise(fs, opr, e1, e2, line); + break; + } + case OPR_SHL: { + if (isSCint(e1)) { + swapexps(e1, e2); + codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */ + } + else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) { + /* coded as (r1 >> -I) */; + } + else /* regular case (two registers) */ + codebinexpval(fs, OP_SHL, e1, e2, line); + break; + } + case OPR_SHR: { + if (isSCint(e2)) + codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ + else /* regular case (two registers) */ + codebinexpval(fs, OP_SHR, e1, e2, line); + break; + } + case OPR_EQ: case OPR_NE: { + codeeq(fs, opr, e1, e2); + break; + } + case OPR_LT: case OPR_LE: { + OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ); + codeorder(fs, op, e1, e2); + break; + } + case OPR_GT: case OPR_GE: { + /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ + OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ); + swapexps(e1, e2); + codeorder(fs, op, e1, e2); + break; + } + default: lua_assert(0); + } +} + + +/* +** Change line information associated with current position, by removing +** previous info and adding it again with new line. +*/ +void luaK_fixline (FuncState *fs, int line) { + removelastlineinfo(fs); + savelineinfo(fs, fs->f, line); +} + + +void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) { + Instruction *inst = &fs->f->code[pc]; + int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */ + int extra = asize / (MAXARG_C + 1); /* higher bits of array size */ + int rc = asize % (MAXARG_C + 1); /* lower bits of array size */ + int k = (extra > 0); /* true iff needs extra argument */ + *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k); + *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra); +} + + +/* +** Emit a SETLIST instruction. +** 'base' is register that keeps table; +** 'nelems' is #table plus those to be stored now; +** 'tostore' is number of values (in registers 'base + 1',...) to add to +** table (or LUA_MULTRET to add up to stack top). +*/ +void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); + if (tostore == LUA_MULTRET) + tostore = 0; + if (nelems <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems); + else { + int extra = nelems / (MAXARG_C + 1); + nelems %= (MAXARG_C + 1); + luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1); + codeextraarg(fs, extra); + } + fs->freereg = base + 1; /* free registers with list values */ +} + + +/* +** return the final target of a jump (skipping jumps to jumps) +*/ +static int finaltarget (Instruction *code, int i) { + int count; + for (count = 0; count < 100; count++) { /* avoid infinite loops */ + Instruction pc = code[i]; + if (GET_OPCODE(pc) != OP_JMP) + break; + else + i += GETARG_sJ(pc) + 1; + } + return i; +} + + +/* +** Do a final pass over the code of a function, doing small peephole +** optimizations and adjustments. +*/ +void luaK_finish (FuncState *fs) { + int i; + Proto *p = fs->f; + for (i = 0; i < fs->pc; i++) { + Instruction *pc = &p->code[i]; + lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc)); + switch (GET_OPCODE(*pc)) { + case OP_RETURN0: case OP_RETURN1: { + if (!(fs->needclose || p->is_vararg)) + break; /* no extra work */ + /* else use OP_RETURN to do the extra work */ + SET_OPCODE(*pc, OP_RETURN); + } /* FALLTHROUGH */ + case OP_RETURN: case OP_TAILCALL: { + if (fs->needclose) + SETARG_k(*pc, 1); /* signal that it needs to close */ + if (p->is_vararg) + SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ + break; + } + case OP_JMP: { + int target = finaltarget(p->code, i); + fixjump(fs, i, target); + break; + } + default: break; + } + } +} diff --git a/Lua/lcode.h b/Lua/lcode.h new file mode 100644 index 00000000..32658244 --- /dev/null +++ b/Lua/lcode.h @@ -0,0 +1,104 @@ +/* +** $Id: lcode.h $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums (ORDER OP) +*/ +typedef enum BinOpr { + /* arithmetic operators */ + OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, + OPR_DIV, OPR_IDIV, + /* bitwise operators */ + OPR_BAND, OPR_BOR, OPR_BXOR, + OPR_SHL, OPR_SHR, + /* string operator */ + OPR_CONCAT, + /* comparison operators */ + OPR_EQ, OPR_LT, OPR_LE, + OPR_NE, OPR_GT, OPR_GE, + /* logical operators */ + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +/* true if operation is foldable (that is, it is arithmetic or bitwise) */ +#define foldbinop(op) ((op) <= OPR_SHR) + + +#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0) + + +typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +/* get (pointer to) instruction of given 'expdesc' */ +#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) + + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) + +LUAI_FUNC int luaK_code (FuncState *fs, Instruction i); +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); +LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); +LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, + int B, int C, int k); +LUAI_FUNC int luaK_isKint (expdesc *e); +LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, + expdesc *v2, int line); +LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc, + int ra, int asize, int hsize); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); +LUAI_FUNC void luaK_finish (FuncState *fs); +LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg); + + +#endif diff --git a/Lua/lcorolib.c b/Lua/lcorolib.c new file mode 100644 index 00000000..c165031d --- /dev/null +++ b/Lua/lcorolib.c @@ -0,0 +1,207 @@ +/* +** $Id: lcorolib.c $ +** Coroutine Library +** See Copyright Notice in lua.h +*/ + +#define lcorolib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +static lua_State *getco (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argexpected(L, co, 1, "thread"); + return co; +} + + +/* +** Resumes a coroutine. Returns the number of results for non-error +** cases or -1 for errors. +*/ +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status, nres; + if (!lua_checkstack(co, narg)) { + lua_pushliteral(L, "too many arguments to resume"); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + status = lua_resume(co, L, narg, &nres); + if (status == LUA_OK || status == LUA_YIELD) { + if (!lua_checkstack(L, nres + 1)) { + lua_pop(co, nres); /* remove results anyway */ + lua_pushliteral(L, "too many results to resume"); + return -1; /* error flag */ + } + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = getco(L); + int r; + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + 'resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { /* error? */ + int stat = lua_status(co); + if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */ + lua_resetthread(co); /* close its tbc variables */ + if (stat != LUA_ERRMEM && /* not a memory error and ... */ + lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ + luaL_where(L, 1); /* add extra info, if available */ + lua_insert(L, -2); + lua_concat(L, 2); + } + return lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL; + luaL_checktype(L, 1, LUA_TFUNCTION); + NL = lua_newthread(L); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +#define COS_RUN 0 +#define COS_DEAD 1 +#define COS_YIELD 2 +#define COS_NORM 3 + + +static const char *const statname[] = + {"running", "dead", "suspended", "normal"}; + + +static int auxstatus (lua_State *L, lua_State *co) { + if (L == co) return COS_RUN; + else { + switch (lua_status(co)) { + case LUA_YIELD: + return COS_YIELD; + case LUA_OK: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar)) /* does it have frames? */ + return COS_NORM; /* it is running */ + else if (lua_gettop(co) == 0) + return COS_DEAD; + else + return COS_YIELD; /* initial state */ + } + default: /* some error occurred */ + return COS_DEAD; + } + } +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = getco(L); + lua_pushstring(L, statname[auxstatus(L, co)]); + return 1; +} + + +static int luaB_yieldable (lua_State *L) { + lua_State *co = lua_isnone(L, 1) ? L : getco(L); + lua_pushboolean(L, lua_isyieldable(co)); + return 1; +} + + +static int luaB_corunning (lua_State *L) { + int ismain = lua_pushthread(L); + lua_pushboolean(L, ismain); + return 2; +} + + +static int luaB_close (lua_State *L) { + lua_State *co = getco(L); + int status = auxstatus(L, co); + switch (status) { + case COS_DEAD: case COS_YIELD: { + status = lua_resetthread(co); + if (status == LUA_OK) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushboolean(L, 0); + lua_xmove(co, L, 1); /* copy error message */ + return 2; + } + } + default: /* normal or running coroutine */ + return luaL_error(L, "cannot close a %s coroutine", statname[status]); + } +} + + +static const luaL_Reg co_funcs[] = { + {"create", luaB_cocreate}, + {"resume", luaB_coresume}, + {"running", luaB_corunning}, + {"status", luaB_costatus}, + {"wrap", luaB_cowrap}, + {"yield", luaB_yield}, + {"isyieldable", luaB_yieldable}, + {"close", luaB_close}, + {NULL, NULL} +}; + + + +LUAMOD_API int luaopen_coroutine (lua_State *L) { + luaL_newlib(L, co_funcs); + return 1; +} + diff --git a/Lua/lctype.c b/Lua/lctype.c new file mode 100644 index 00000000..95422809 --- /dev/null +++ b/Lua/lctype.c @@ -0,0 +1,64 @@ +/* +** $Id: lctype.c $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#define lctype_c +#define LUA_CORE + +#include "lprefix.h" + + +#include "lctype.h" + +#if !LUA_USE_CTYPE /* { */ + +#include + + +#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ +/* consider all non-ascii codepoints to be alphabetic */ +#define NONA 0x01 +#else +#define NONA 0x00 /* default */ +#endif + + +LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { + 0x00, /* EOZ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ + 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#endif /* } */ diff --git a/Lua/lctype.h b/Lua/lctype.h new file mode 100644 index 00000000..864e1901 --- /dev/null +++ b/Lua/lctype.h @@ -0,0 +1,101 @@ +/* +** $Id: lctype.h $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lctype_h +#define lctype_h + +#include "lua.h" + + +/* +** WARNING: the functions defined here do not necessarily correspond +** to the similar functions in the standard C ctype.h. They are +** optimized for the specific needs of Lua. +*/ + +#if !defined(LUA_USE_CTYPE) + +#if 'A' == 65 && '0' == 48 +/* ASCII case: can use its own tables; faster and fixed */ +#define LUA_USE_CTYPE 0 +#else +/* must use standard C ctype */ +#define LUA_USE_CTYPE 1 +#endif + +#endif + + +#if !LUA_USE_CTYPE /* { */ + +#include + +#include "llimits.h" + + +#define ALPHABIT 0 +#define DIGITBIT 1 +#define PRINTBIT 2 +#define SPACEBIT 3 +#define XDIGITBIT 4 + + +#define MASK(B) (1 << (B)) + + +/* +** add 1 to char to allow index -1 (EOZ) +*/ +#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) + +/* +** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' +*/ +#define lislalpha(c) testprop(c, MASK(ALPHABIT)) +#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) +#define lisdigit(c) testprop(c, MASK(DIGITBIT)) +#define lisspace(c) testprop(c, MASK(SPACEBIT)) +#define lisprint(c) testprop(c, MASK(PRINTBIT)) +#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) + + +/* +** In ASCII, this 'ltolower' is correct for alphabetic characters and +** for '.'. That is enough for Lua needs. ('check_exp' ensures that +** the character either is an upper-case letter or is unchanged by +** the transformation, which holds for lower-case letters and '.'.) +*/ +#define ltolower(c) \ + check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ + (c) | ('A' ^ 'a')) + + +/* one entry for each character and for -1 (EOZ) */ +LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) + + +#else /* }{ */ + +/* +** use standard C ctypes +*/ + +#include + + +#define lislalpha(c) (isalpha(c) || (c) == '_') +#define lislalnum(c) (isalnum(c) || (c) == '_') +#define lisdigit(c) (isdigit(c)) +#define lisspace(c) (isspace(c)) +#define lisprint(c) (isprint(c)) +#define lisxdigit(c) (isxdigit(c)) + +#define ltolower(c) (tolower(c)) + +#endif /* } */ + +#endif + diff --git a/Lua/ldblib.c b/Lua/ldblib.c new file mode 100644 index 00000000..59eb8f0e --- /dev/null +++ b/Lua/ldblib.c @@ -0,0 +1,477 @@ +/* +** $Id: ldblib.c $ +** Interface from Lua to its debug API +** See Copyright Notice in lua.h +*/ + +#define ldblib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* +** The hook table at registry[HOOKKEY] maps threads to their current +** hook function. +*/ +static const char *const HOOKKEY = "_HOOKKEY"; + + +/* +** If L1 != L, L1 can be in any state, and therefore there are no +** guarantees about its stack space; any push in L1 must be +** checked. +*/ +static void checkstack (lua_State *L, lua_State *L1, int n) { + if (L != L1 && !lua_checkstack(L1, n)) + luaL_error(L, "stack overflow"); +} + + +static int db_getregistry (lua_State *L) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} + + +static int db_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); /* no metatable */ + } + return 1; +} + + +static int db_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; /* return 1st argument */ +} + + +static int db_getuservalue (lua_State *L) { + int n = (int)luaL_optinteger(L, 2, 1); + if (lua_type(L, 1) != LUA_TUSERDATA) + luaL_pushfail(L); + else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) { + lua_pushboolean(L, 1); + return 2; + } + return 1; +} + + +static int db_setuservalue (lua_State *L) { + int n = (int)luaL_optinteger(L, 3, 1); + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checkany(L, 2); + lua_settop(L, 2); + if (!lua_setiuservalue(L, 1, n)) + luaL_pushfail(L); + return 1; +} + + +/* +** Auxiliary function used by several library functions: check for +** an optional thread as function's first argument and set 'arg' with +** 1 if this argument is present (so that functions can skip it to +** access their other arguments) +*/ +static lua_State *getthread (lua_State *L, int *arg) { + if (lua_isthread(L, 1)) { + *arg = 1; + return lua_tothread(L, 1); + } + else { + *arg = 0; + return L; /* function will operate over current thread */ + } +} + + +/* +** Variations of 'lua_settable', used by 'db_getinfo' to put results +** from 'lua_getinfo' into result table. Key is always a string; +** value can be a string, an int, or a boolean. +*/ +static void settabss (lua_State *L, const char *k, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, k); +} + +static void settabsi (lua_State *L, const char *k, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, k); +} + +static void settabsb (lua_State *L, const char *k, int v) { + lua_pushboolean(L, v); + lua_setfield(L, -2, k); +} + + +/* +** In function 'db_getinfo', the call to 'lua_getinfo' may push +** results on the stack; later it creates the result table to put +** these objects. Function 'treatstackoption' puts the result from +** 'lua_getinfo' on top of the result table so that it can call +** 'lua_setfield'. +*/ +static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) + lua_rotate(L, -2, 1); /* exchange object and table */ + else + lua_xmove(L1, L, 1); /* move object to the "main" stack */ + lua_setfield(L, -2, fname); /* put object into table */ +} + + +/* +** Calls 'lua_getinfo' and collects all results in a new table. +** L1 needs stack space for an optional input (function) plus +** two optional outputs (function and line table) from function +** 'lua_getinfo'. +*/ +static int db_getinfo (lua_State *L) { + lua_Debug ar; + int arg; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSrtu"); + checkstack(L, L1, 3); + if (lua_isfunction(L, arg + 1)) { /* info about a function? */ + options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ + lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ + lua_xmove(L, L1, 1); + } + else { /* stack level */ + if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) { + luaL_pushfail(L); /* level out of range */ + return 1; + } + } + if (!lua_getinfo(L1, options, &ar)) + return luaL_argerror(L, arg+2, "invalid option"); + lua_newtable(L); /* table to collect results */ + if (strchr(options, 'S')) { + lua_pushlstring(L, ar.source, ar.srclen); + lua_setfield(L, -2, "source"); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + } + if (strchr(options, 'l')) + settabsi(L, "currentline", ar.currentline); + if (strchr(options, 'u')) { + settabsi(L, "nups", ar.nups); + settabsi(L, "nparams", ar.nparams); + settabsb(L, "isvararg", ar.isvararg); + } + if (strchr(options, 'n')) { + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + } + if (strchr(options, 'r')) { + settabsi(L, "ftransfer", ar.ftransfer); + settabsi(L, "ntransfer", ar.ntransfer); + } + if (strchr(options, 't')) + settabsb(L, "istailcall", ar.istailcall); + if (strchr(options, 'L')) + treatstackoption(L, L1, "activelines"); + if (strchr(options, 'f')) + treatstackoption(L, L1, "func"); + return 1; /* return table */ +} + + +static int db_getlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */ + if (lua_isfunction(L, arg + 1)) { /* function argument? */ + lua_pushvalue(L, arg + 1); /* push function */ + lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ + return 1; /* return only name (there is no value) */ + } + else { /* stack-level argument */ + lua_Debug ar; + const char *name; + int level = (int)luaL_checkinteger(L, arg + 1); + if (!lua_getstack(L1, level, &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + checkstack(L, L1, 1); + name = lua_getlocal(L1, &ar, nvar); + if (name) { + lua_xmove(L1, L, 1); /* move local value */ + lua_pushstring(L, name); /* push name */ + lua_rotate(L, -2, 1); /* re-order */ + return 2; + } + else { + luaL_pushfail(L); /* no name (nor value) */ + return 1; + } + } +} + + +static int db_setlocal (lua_State *L) { + int arg; + const char *name; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + int level = (int)luaL_checkinteger(L, arg + 1); + int nvar = (int)luaL_checkinteger(L, arg + 2); + if (!lua_getstack(L1, level, &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + luaL_checkany(L, arg+3); + lua_settop(L, arg+3); + checkstack(L, L1, 1); + lua_xmove(L, L1, 1); + name = lua_setlocal(L1, &ar, nvar); + if (name == NULL) + lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ + lua_pushstring(L, name); + return 1; +} + + +/* +** get (if 'get' is true) or set an upvalue from a closure +*/ +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = (int)luaL_checkinteger(L, 2); /* upvalue index */ + luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); /* no-op if get is false */ + return get + 1; +} + + +static int db_getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int db_setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + +/* +** Check whether a given upvalue from a given closure exists and +** returns its index +*/ +static int checkupval (lua_State *L, int argf, int argnup) { + int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ + luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ + luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup, + "invalid upvalue index"); + return nup; +} + + +static int db_upvalueid (lua_State *L) { + int n = checkupval(L, 1, 2); + lua_pushlightuserdata(L, lua_upvalueid(L, 1, n)); + return 1; +} + + +static int db_upvaluejoin (lua_State *L) { + int n1 = checkupval(L, 1, 2); + int n2 = checkupval(L, 3, 4); + luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); + luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); + lua_upvaluejoin(L, 1, n1, 3, n2); + return 0; +} + + +/* +** Call hook function registered at hook table for the current +** thread (if there is one) +*/ +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail call"}; + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); + lua_pushthread(L); + if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ + lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); /* push current line */ + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); /* call hook function */ + } +} + + +/* +** Convert a string mask (for 'sethook') into a bit mask +*/ +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +/* +** Convert a bit mask (for 'gethook') into a string mask +*/ +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static int db_sethook (lua_State *L) { + int arg, mask, count; + lua_Hook func; + lua_State *L1 = getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { /* no hook? */ + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checktype(L, arg+1, LUA_TFUNCTION); + count = (int)luaL_optinteger(L, arg + 3, 0); + func = hookf; mask = makemask(smask, count); + } + if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) { + /* table just created; initialize it */ + lua_pushstring(L, "k"); + lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */ + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */ + } + checkstack(L, L1, 1); + lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */ + lua_pushvalue(L, arg + 1); /* value (hook function) */ + lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ + lua_sethook(L1, func, mask, count); + return 0; +} + + +static int db_gethook (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + char buff[5]; + int mask = lua_gethookmask(L1); + lua_Hook hook = lua_gethook(L1); + if (hook == NULL) { /* no hook? */ + luaL_pushfail(L); + return 1; + } + else if (hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { /* hook table must exist */ + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); + checkstack(L, L1, 1); + lua_pushthread(L1); lua_xmove(L1, L, 1); + lua_rawget(L, -2); /* 1st result = hooktable[L1] */ + lua_remove(L, -2); /* remove hook table */ + } + lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ + lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ + return 3; +} + + +static int db_debug (lua_State *L) { + for (;;) { + char buffer[250]; + lua_writestringerror("%s", "lua_debug> "); + if (fgets(buffer, sizeof(buffer), stdin) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) + lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +static int db_traceback (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + const char *msg = lua_tostring(L, arg + 1); + if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */ + lua_pushvalue(L, arg + 1); /* return it untouched */ + else { + int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0); + luaL_traceback(L, L1, msg, level); + } + return 1; +} + + +static int db_setcstacklimit (lua_State *L) { + int limit = (int)luaL_checkinteger(L, 1); + int res = lua_setcstacklimit(L, limit); + if (res == 0) + lua_pushboolean(L, 0); + else + lua_pushinteger(L, res); + return 1; +} + + +static const luaL_Reg dblib[] = { + {"debug", db_debug}, + {"getuservalue", db_getuservalue}, + {"gethook", db_gethook}, + {"getinfo", db_getinfo}, + {"getlocal", db_getlocal}, + {"getregistry", db_getregistry}, + {"getmetatable", db_getmetatable}, + {"getupvalue", db_getupvalue}, + {"upvaluejoin", db_upvaluejoin}, + {"upvalueid", db_upvalueid}, + {"setuservalue", db_setuservalue}, + {"sethook", db_sethook}, + {"setlocal", db_setlocal}, + {"setmetatable", db_setmetatable}, + {"setupvalue", db_setupvalue}, + {"traceback", db_traceback}, + {"setcstacklimit", db_setcstacklimit}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_debug (lua_State *L) { + luaL_newlib(L, dblib); + return 1; +} + diff --git a/Lua/ldebug.c b/Lua/ldebug.c new file mode 100644 index 00000000..8cb00e51 --- /dev/null +++ b/Lua/ldebug.c @@ -0,0 +1,852 @@ +/* +** $Id: ldebug.c $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + +#define ldebug_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) + +/* inverse of 'pcRel' */ +#define invpcRel(pc, p) ((p)->code + (pc) + 1) + +static const char *funcnamefromcode (lua_State *L, CallInfo *ci, + const char **name); + + +static int currentpc (CallInfo *ci) { + lua_assert(isLua(ci)); + return pcRel(ci->u.l.savedpc, ci_func(ci)->p); +} + + +/* +** Get a "base line" to find the line corresponding to an instruction. +** For that, search the array of absolute line info for the largest saved +** instruction smaller or equal to the wanted instruction. A special +** case is when there is no absolute info or the instruction is before +** the first absolute one. +*/ +static int getbaseline (const Proto *f, int pc, int *basepc) { + if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) { + *basepc = -1; /* start from the beginning */ + return f->linedefined; + } + else { + unsigned int i; + if (pc >= f->abslineinfo[f->sizeabslineinfo - 1].pc) + i = f->sizeabslineinfo - 1; /* instruction is after last saved one */ + else { /* binary search */ + unsigned int j = f->sizeabslineinfo - 1; /* pc < anchorlines[j] */ + i = 0; /* abslineinfo[i] <= pc */ + while (i < j - 1) { + unsigned int m = (j + i) / 2; + if (pc >= f->abslineinfo[m].pc) + i = m; + else + j = m; + } + } + *basepc = f->abslineinfo[i].pc; + return f->abslineinfo[i].line; + } +} + + +/* +** Get the line corresponding to instruction 'pc' in function 'f'; +** first gets a base line and from there does the increments until +** the desired instruction. +*/ +int luaG_getfuncline (const Proto *f, int pc) { + if (f->lineinfo == NULL) /* no debug information? */ + return -1; + else { + int basepc; + int baseline = getbaseline(f, pc, &basepc); + while (basepc++ < pc) { /* walk until given instruction */ + lua_assert(f->lineinfo[basepc] != ABSLINEINFO); + baseline += f->lineinfo[basepc]; /* correct line */ + } + return baseline; + } +} + + +static int getcurrentline (CallInfo *ci) { + return luaG_getfuncline(ci_func(ci)->p, currentpc(ci)); +} + + +/* +** Set 'trap' for all active Lua frames. +** This function can be called during a signal, under "reasonable" +** assumptions. A new 'ci' is completely linked in the list before it +** becomes part of the "active" list, and we assume that pointers are +** atomic; see comment in next function. +** (A compiler doing interprocedural optimizations could, theoretically, +** reorder memory writes in such a way that the list could be +** temporarily broken while inserting a new element. We simply assume it +** has no good reasons to do that.) +*/ +static void settraps (CallInfo *ci) { + for (; ci != NULL; ci = ci->previous) + if (isLua(ci)) + ci->u.l.trap = 1; +} + + +/* +** This function can be called during a signal, under "reasonable" +** assumptions. +** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount') +** are for debug only, and it is no problem if they get arbitrary +** values (causes at most one wrong hook call). 'hookmask' is an atomic +** value. We assume that pointers are atomic too (e.g., gcc ensures that +** for all platforms where it runs). Moreover, 'hook' is always checked +** before being called (see 'luaD_hook'). +*/ +LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + if (mask) + settraps(L->ci); /* to trace inside 'luaV_execute' */ +} + + +LUA_API lua_Hook lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + if (level < 0) return 0; /* invalid (negative) level */ + lua_lock(L); + for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) + level--; + if (level == 0 && ci != &L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = ci; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static const char *upvalname (const Proto *p, int uv) { + TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); + if (s == NULL) return "?"; + else return getstr(s); +} + + +static const char *findvararg (CallInfo *ci, int n, StkId *pos) { + if (clLvalue(s2v(ci->func))->p->is_vararg) { + int nextra = ci->u.l.nextraargs; + if (n >= -nextra) { /* 'n' is negative */ + *pos = ci->func - nextra - (n + 1); + return "(vararg)"; /* generic name for any vararg */ + } + } + return NULL; /* no such vararg */ +} + + +const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { + StkId base = ci->func + 1; + const char *name = NULL; + if (isLua(ci)) { + if (n < 0) /* access to vararg values? */ + return findvararg(ci, n, pos); + else + name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); + } + if (name == NULL) { /* no 'standard' name? */ + StkId limit = (ci == L->ci) ? L->top : ci->next->func; + if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ + /* generic name for any valid slot */ + name = isLua(ci) ? "(temporary)" : "(C temporary)"; + } + else + return NULL; /* no name */ + } + if (pos) + *pos = base + (n - 1); + return name; +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + const char *name; + lua_lock(L); + if (ar == NULL) { /* information about non-active function? */ + if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */ + name = NULL; + else /* consider live variables at function start (parameters) */ + name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0); + } + else { /* active function; get information through 'ar' */ + StkId pos = NULL; /* to avoid warnings */ + name = luaG_findlocal(L, ar->i_ci, n, &pos); + if (name) { + setobjs2s(L, L->top, pos); + api_incr_top(L); + } + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + StkId pos = NULL; /* to avoid warnings */ + const char *name; + lua_lock(L); + name = luaG_findlocal(L, ar->i_ci, n, &pos); + if (name) { + setobjs2s(L, pos, L->top - 1); + L->top--; /* pop value */ + } + lua_unlock(L); + return name; +} + + +static void funcinfo (lua_Debug *ar, Closure *cl) { + if (noLuaClosure(cl)) { + ar->source = "=[C]"; + ar->srclen = LL("=[C]"); + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + const Proto *p = cl->l.p; + if (p->source) { + ar->source = getstr(p->source); + ar->srclen = tsslen(p->source); + } + else { + ar->source = "=?"; + ar->srclen = LL("=?"); + } + ar->linedefined = p->linedefined; + ar->lastlinedefined = p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, ar->srclen); +} + + +static int nextline (const Proto *p, int currentline, int pc) { + if (p->lineinfo[pc] != ABSLINEINFO) + return currentline + p->lineinfo[pc]; + else + return luaG_getfuncline(p, pc); +} + + +static void collectvalidlines (lua_State *L, Closure *f) { + if (noLuaClosure(f)) { + setnilvalue(s2v(L->top)); + api_incr_top(L); + } + else { + int i; + TValue v; + const Proto *p = f->l.p; + int currentline = p->linedefined; + Table *t = luaH_new(L); /* new table to store active lines */ + sethvalue2s(L, L->top, t); /* push it on stack */ + api_incr_top(L); + setbtvalue(&v); /* boolean 'true' to be the value of all indices */ + for (i = 0; i < p->sizelineinfo; i++) { /* for all lines with code */ + currentline = nextline(p, currentline, i); + luaH_setint(L, t, currentline, &v); /* table[line] = true */ + } + } +} + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + if (ci == NULL) /* no 'ci'? */ + return NULL; /* no info */ + else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */ + *name = "__gc"; + return "metamethod"; /* report it as such */ + } + /* calling function is a known Lua function? */ + else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous)) + return funcnamefromcode(L, ci->previous, name); + else return NULL; /* no way to find a name */ +} + + +static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, CallInfo *ci) { + int status = 1; + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f); + break; + } + case 'l': { + ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1; + break; + } + case 'u': { + ar->nups = (f == NULL) ? 0 : f->c.nupvalues; + if (noLuaClosure(f)) { + ar->isvararg = 1; + ar->nparams = 0; + } + else { + ar->isvararg = f->l.p->is_vararg; + ar->nparams = f->l.p->numparams; + } + break; + } + case 't': { + ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; + break; + } + case 'n': { + ar->namewhat = getfuncname(L, ci, &ar->name); + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'r': { + if (ci == NULL || !(ci->callstatus & CIST_TRAN)) + ar->ftransfer = ar->ntransfer = 0; + else { + ar->ftransfer = ci->u2.transferinfo.ftransfer; + ar->ntransfer = ci->u2.transferinfo.ntransfer; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *cl; + CallInfo *ci; + TValue *func; + lua_lock(L); + if (*what == '>') { + ci = NULL; + func = s2v(L->top - 1); + api_check(L, ttisfunction(func), "function expected"); + what++; /* skip the '>' */ + L->top--; /* pop function */ + } + else { + ci = ar->i_ci; + func = s2v(ci->func); + lua_assert(ttisfunction(func)); + } + cl = ttisclosure(func) ? clvalue(func) : NULL; + status = auxgetinfo(L, what, ar, cl, ci); + if (strchr(what, 'f')) { + setobj2s(L, L->top, func); + api_incr_top(L); + } + if (strchr(what, 'L')) + collectvalidlines(L, cl); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution +** ======================================================= +*/ + +static const char *getobjname (const Proto *p, int lastpc, int reg, + const char **name); + + +/* +** Find a "name" for the constant 'c'. +*/ +static void kname (const Proto *p, int c, const char **name) { + TValue *kvalue = &p->k[c]; + *name = (ttisstring(kvalue)) ? svalue(kvalue) : "?"; +} + + +/* +** Find a "name" for the register 'c'. +*/ +static void rname (const Proto *p, int pc, int c, const char **name) { + const char *what = getobjname(p, pc, c, name); /* search for 'c' */ + if (!(what && *what == 'c')) /* did not find a constant name? */ + *name = "?"; +} + + +/* +** Find a "name" for a 'C' value in an RK instruction. +*/ +static void rkname (const Proto *p, int pc, Instruction i, const char **name) { + int c = GETARG_C(i); /* key index */ + if (GETARG_k(i)) /* is 'c' a constant? */ + kname(p, c, name); + else /* 'c' is a register */ + rname(p, pc, c, name); +} + + +static int filterpc (int pc, int jmptarget) { + if (pc < jmptarget) /* is code conditional (inside a jump)? */ + return -1; /* cannot know who sets that register */ + else return pc; /* current position sets that register */ +} + + +/* +** Try to find last instruction before 'lastpc' that modified register 'reg'. +*/ +static int findsetreg (const Proto *p, int lastpc, int reg) { + int pc; + int setreg = -1; /* keep last instruction that changed 'reg' */ + int jmptarget = 0; /* any code before this address is conditional */ + if (testMMMode(GET_OPCODE(p->code[lastpc]))) + lastpc--; /* previous instruction was not actually executed */ + for (pc = 0; pc < lastpc; pc++) { + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + int change; /* true if current instruction changed 'reg' */ + switch (op) { + case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */ + int b = GETARG_B(i); + change = (a <= reg && reg <= a + b); + break; + } + case OP_TFORCALL: { /* affect all regs above its base */ + change = (reg >= a + 2); + break; + } + case OP_CALL: + case OP_TAILCALL: { /* affect all registers above base */ + change = (reg >= a); + break; + } + case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */ + int b = GETARG_sJ(i); + int dest = pc + 1 + b; + /* jump does not skip 'lastpc' and is larger than current one? */ + if (dest <= lastpc && dest > jmptarget) + jmptarget = dest; /* update 'jmptarget' */ + change = 0; + break; + } + default: /* any instruction that sets A */ + change = (testAMode(op) && reg == a); + break; + } + if (change) + setreg = filterpc(pc, jmptarget); + } + return setreg; +} + + +/* +** Check whether table being indexed by instruction 'i' is the +** environment '_ENV' +*/ +static const char *gxf (const Proto *p, int pc, Instruction i, int isup) { + int t = GETARG_B(i); /* table index */ + const char *name; /* name of indexed variable */ + if (isup) /* is an upvalue? */ + name = upvalname(p, t); + else + getobjname(p, pc, t, &name); + return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; +} + + +static const char *getobjname (const Proto *p, int lastpc, int reg, + const char **name) { + int pc; + *name = luaF_getlocalname(p, reg + 1, lastpc); + if (*name) /* is a local? */ + return "local"; + /* else try symbolic execution */ + pc = findsetreg(p, lastpc, reg); + if (pc != -1) { /* could find instruction? */ + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_MOVE: { + int b = GETARG_B(i); /* move from 'b' to 'a' */ + if (b < GETARG_A(i)) + return getobjname(p, pc, b, name); /* get name for 'b' */ + break; + } + case OP_GETTABUP: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return gxf(p, pc, i, 1); + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + rname(p, pc, k, name); + return gxf(p, pc, i, 0); + } + case OP_GETI: { + *name = "integer index"; + return "field"; + } + case OP_GETFIELD: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return gxf(p, pc, i, 0); + } + case OP_GETUPVAL: { + *name = upvalname(p, GETARG_B(i)); + return "upvalue"; + } + case OP_LOADK: + case OP_LOADKX: { + int b = (op == OP_LOADK) ? GETARG_Bx(i) + : GETARG_Ax(p->code[pc + 1]); + if (ttisstring(&p->k[b])) { + *name = svalue(&p->k[b]); + return "constant"; + } + break; + } + case OP_SELF: { + rkname(p, pc, i, name); + return "method"; + } + default: break; /* go through to return NULL */ + } + } + return NULL; /* could not find reasonable name */ +} + + +/* +** Try to find a name for a function based on the code that called it. +** (Only works when function was called by a Lua function.) +** Returns what the name is (e.g., "for iterator", "method", +** "metamethod") and sets '*name' to point to the name. +*/ +static const char *funcnamefromcode (lua_State *L, CallInfo *ci, + const char **name) { + TMS tm = (TMS)0; /* (initial value avoids warnings) */ + const Proto *p = ci_func(ci)->p; /* calling function */ + int pc = currentpc(ci); /* calling instruction index */ + Instruction i = p->code[pc]; /* calling instruction */ + if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ + *name = "?"; + return "hook"; + } + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + return getobjname(p, pc, GETARG_A(i), name); /* get function name */ + case OP_TFORCALL: { /* for iterator */ + *name = "for iterator"; + return "for iterator"; + } + /* other instructions can do calls through metamethods */ + case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: + case OP_GETI: case OP_GETFIELD: + tm = TM_INDEX; + break; + case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD: + tm = TM_NEWINDEX; + break; + case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { + tm = cast(TMS, GETARG_C(i)); + break; + } + case OP_UNM: tm = TM_UNM; break; + case OP_BNOT: tm = TM_BNOT; break; + case OP_LEN: tm = TM_LEN; break; + case OP_CONCAT: tm = TM_CONCAT; break; + case OP_EQ: tm = TM_EQ; break; + case OP_LT: case OP_LE: case OP_LTI: case OP_LEI: + *name = "order"; /* '<=' can call '__lt', etc. */ + return "metamethod"; + case OP_CLOSE: case OP_RETURN: + *name = "close"; + return "metamethod"; + default: + return NULL; /* cannot find a reasonable name */ + } + *name = getstr(G(L)->tmname[tm]) + 2; + return "metamethod"; +} + +/* }====================================================== */ + + + +/* +** The subtraction of two potentially unrelated pointers is +** not ISO C, but it should not crash a program; the subsequent +** checks are ISO C and ensure a correct result. +*/ +static int isinstack (CallInfo *ci, const TValue *o) { + StkId base = ci->func + 1; + ptrdiff_t i = cast(StkId, o) - base; + return (0 <= i && i < (ci->top - base) && s2v(base + i) == o); +} + + +/* +** Checks whether value 'o' came from an upvalue. (That can only happen +** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on +** upvalues.) +*/ +static const char *getupvalname (CallInfo *ci, const TValue *o, + const char **name) { + LClosure *c = ci_func(ci); + int i; + for (i = 0; i < c->nupvalues; i++) { + if (c->upvals[i]->v == o) { + *name = upvalname(c->p, i); + return "upvalue"; + } + } + return NULL; +} + + +static const char *varinfo (lua_State *L, const TValue *o) { + const char *name = NULL; /* to avoid warnings */ + CallInfo *ci = L->ci; + const char *kind = NULL; + if (isLua(ci)) { + kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ + if (!kind && isinstack(ci, o)) /* no? try a register */ + kind = getobjname(ci_func(ci)->p, currentpc(ci), + cast_int(cast(StkId, o) - (ci->func + 1)), &name); + } + return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : ""; +} + + +l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + const char *t = luaT_objtypename(L, o); + luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o)); +} + + +l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) { + luaG_runerror(L, "bad 'for' %s (number expected, got %s)", + what, luaT_objtypename(L, o)); +} + + +l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { + if (ttisstring(p1) || cvt2str(p1)) p1 = p2; + luaG_typeerror(L, p1, "concatenate"); +} + + +l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, const char *msg) { + if (!ttisnumber(p1)) /* first operand is wrong? */ + p2 = p1; /* now second is wrong */ + luaG_typeerror(L, p2, msg); +} + + +/* +** Error when both values are convertible to numbers, but not to integers +*/ +l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { + lua_Integer temp; + if (!tointegerns(p1, &temp)) + p2 = p1; + luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); +} + + +l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = luaT_objtypename(L, p1); + const char *t2 = luaT_objtypename(L, p2); + if (strcmp(t1, t2) == 0) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); +} + + +/* add src:line information to 'msg' */ +const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, + int line) { + char buff[LUA_IDSIZE]; + if (src) + luaO_chunkid(buff, getstr(src), tsslen(src)); + else { /* no source available; use "?" instead */ + buff[0] = '?'; buff[1] = '\0'; + } + return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); +} + + +l_noret luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + lua_assert(ttisfunction(s2v(errfunc))); + setobjs2s(L, L->top, L->top - 1); /* move argument */ + setobjs2s(L, L->top - 1, errfunc); /* push function */ + L->top++; /* assume EXTRA_STACK */ + luaD_callnoyield(L, L->top - 2, 1); /* call it */ + } + luaD_throw(L, LUA_ERRRUN); +} + + +l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { + CallInfo *ci = L->ci; + const char *msg; + va_list argp; + luaC_checkGC(L); /* error message uses memory */ + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); /* format message */ + va_end(argp); + if (isLua(ci)) /* if Lua function, add source:line information */ + luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); + luaG_errormsg(L); +} + + +/* +** Check whether new instruction 'newpc' is in a different line from +** previous instruction 'oldpc'. +*/ +static int changedline (const Proto *p, int oldpc, int newpc) { + if (p->lineinfo == NULL) /* no debug information? */ + return 0; + while (oldpc++ < newpc) { + if (p->lineinfo[oldpc] != 0) + return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc)); + } + return 0; /* no line changes between positions */ +} + + +/* +** Traces the execution of a Lua function. Called before the execution +** of each opcode, when debug is on. 'L->oldpc' stores the last +** instruction traced, to detect line changes. When entering a new +** function, 'npci' will be zero and will test as a new line without +** the need for 'oldpc'; so, 'oldpc' does not need to be initialized +** before. Some exceptional conditions may return to a function without +** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is +** reset to zero. (A wrong but valid 'oldpc' at most causes an extra +** call to a line hook.) +*/ +int luaG_traceexec (lua_State *L, const Instruction *pc) { + CallInfo *ci = L->ci; + lu_byte mask = L->hookmask; + const Proto *p = ci_func(ci)->p; + int counthook; + /* 'L->oldpc' may be invalid; reset it in this case */ + int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0; + if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ + ci->u.l.trap = 0; /* don't need to stop again */ + return 0; /* turn off 'trap' */ + } + pc++; /* reference is always next instruction */ + ci->u.l.savedpc = pc; /* save 'pc' */ + counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); + if (counthook) + resethookcount(L); /* reset count */ + else if (!(mask & LUA_MASKLINE)) + return 1; /* no line hook and count != 0; nothing to be done now */ + if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ + ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ + return 1; /* do not call hook again (VM yielded, so it did not move) */ + } + if (!isIT(*(ci->u.l.savedpc - 1))) + L->top = ci->top; /* prepare top */ + if (counthook) + luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ + if (mask & LUA_MASKLINE) { + int npci = pcRel(pc, p); + if (npci == 0 || /* call linehook when enter a new function, */ + pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */ + changedline(p, oldpc, npci)) { /* enter new line */ + int newline = luaG_getfuncline(p, npci); + luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ + } + L->oldpc = npci; /* 'pc' of last call to line hook */ + } + if (L->status == LUA_YIELD) { /* did hook yield? */ + if (counthook) + L->hookcount = 1; /* undo decrement to zero */ + ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ + ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ + luaD_throw(L, LUA_YIELD); + } + return 1; /* keep 'trap' on */ +} + diff --git a/Lua/ldebug.h b/Lua/ldebug.h new file mode 100644 index 00000000..a0a58486 --- /dev/null +++ b/Lua/ldebug.h @@ -0,0 +1,52 @@ +/* +** $Id: ldebug.h $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" + + +#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) + + +/* Active Lua function (given call info) */ +#define ci_func(ci) (clLvalue(s2v((ci)->func))) + + +#define resethookcount(L) (L->hookcount = L->basehookcount) + +/* +** mark for entries in 'lineinfo' array that has absolute information in +** 'abslineinfo' array +*/ +#define ABSLINEINFO (-0x80) + +LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc); +LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, + StkId *pos); +LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o, + const char *what); +LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, + const char *msg); +LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, + TString *src, int line); +LUAI_FUNC l_noret luaG_errormsg (lua_State *L); +LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc); + + +#endif diff --git a/Lua/ldo.c b/Lua/ldo.c new file mode 100644 index 00000000..5473815a --- /dev/null +++ b/Lua/ldo.c @@ -0,0 +1,822 @@ +/* +** $Id: ldo.c $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#define ldo_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lzio.h" + + + +#define errorstatus(s) ((s) > LUA_YIELD) + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + +/* +** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By +** default, Lua handles errors with exceptions when compiling as +** C++ code, with _longjmp/_setjmp when asked to use them, and with +** longjmp/setjmp otherwise. +*/ +#if !defined(LUAI_THROW) /* { */ + +#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */ + +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) \ + try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_POSIX) /* }{ */ + +/* in POSIX, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else /* }{ */ + +/* ISO C handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif /* } */ + +#endif /* } */ + + + +/* chain list of long jump buffers */ +struct lua_longjmp { + struct lua_longjmp *previous; + luai_jmpbuf b; + volatile int status; /* error code */ +}; + + +void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { + switch (errcode) { + case LUA_ERRMEM: { /* memory error? */ + setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ + break; + } + case LUA_ERRERR: { + setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); + break; + } + case CLOSEPROTECT: { + setnilvalue(s2v(oldtop)); /* no error message */ + break; + } + default: { + setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + break; + } + } + L->top = oldtop + 1; +} + + +l_noret luaD_throw (lua_State *L, int errcode) { + if (L->errorJmp) { /* thread has an error handler? */ + L->errorJmp->status = errcode; /* set status */ + LUAI_THROW(L, L->errorJmp); /* jump to it */ + } + else { /* thread has no error handler */ + global_State *g = G(L); + errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */ + L->status = cast_byte(errcode); /* mark it as dead */ + if (g->mainthread->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ + luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ + } + else { /* no handler at all; abort */ + if (g->panic) { /* panic function? */ + luaD_seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */ + if (L->ci->top < L->top) + L->ci->top = L->top; /* pushing msg. can break this invariant */ + lua_unlock(L); + g->panic(L); /* call panic function (last chance to jump out) */ + } + abort(); + } + } +} + + +int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + global_State *g = G(L); + l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci); + struct lua_longjmp lj; + lj.status = LUA_OK; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, + (*f)(L, ud); + ); + L->errorJmp = lj.previous; /* restore old error handler */ + L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci; + return lj.status; +} + +/* }====================================================== */ + + +/* +** {================================================================== +** Stack reallocation +** =================================================================== +*/ +static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { + CallInfo *ci; + UpVal *up; + if (oldstack == newstack) + return; /* stack address did not change */ + L->top = (L->top - oldstack) + newstack; + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v = s2v((uplevel(up) - oldstack) + newstack); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top = (ci->top - oldstack) + newstack; + ci->func = (ci->func - oldstack) + newstack; + if (isLua(ci)) + ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ + } +} + + +/* some space for error handling */ +#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) + + +int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { + int lim = L->stacksize; + StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue); + lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); + if (unlikely(newstack == NULL)) { /* reallocation failed? */ + if (raiseerror) + luaM_error(L); + else return 0; /* do not raise an error */ + } + for (; lim < newsize; lim++) + setnilvalue(s2v(newstack + lim)); /* erase new segment */ + correctstack(L, L->stack, newstack); + L->stack = newstack; + L->stacksize = newsize; + L->stack_last = L->stack + newsize - EXTRA_STACK; + return 1; +} + + +/* +** Try to grow the stack by at least 'n' elements. when 'raiseerror' +** is true, raises any error; otherwise, return 0 in case of errors. +*/ +int luaD_growstack (lua_State *L, int n, int raiseerror) { + int size = L->stacksize; + int newsize = 2 * size; /* tentative new size */ + if (unlikely(size > LUAI_MAXSTACK)) { /* need more space after extra size? */ + if (raiseerror) + luaD_throw(L, LUA_ERRERR); /* error inside message handler */ + else return 0; + } + else { + int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; + if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ + newsize = LUAI_MAXSTACK; + if (newsize < needed) /* but must respect what was asked for */ + newsize = needed; + if (unlikely(newsize > LUAI_MAXSTACK)) { /* stack overflow? */ + /* add extra size to be able to handle the error message */ + luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); + if (raiseerror) + luaG_runerror(L, "stack overflow"); + else return 0; + } + } /* else no errors */ + return luaD_reallocstack(L, newsize, raiseerror); +} + + +static int stackinuse (lua_State *L) { + CallInfo *ci; + StkId lim = L->top; + for (ci = L->ci; ci != NULL; ci = ci->previous) { + if (lim < ci->top) lim = ci->top; + } + lua_assert(lim <= L->stack_last); + return cast_int(lim - L->stack) + 1; /* part of stack in use */ +} + + +void luaD_shrinkstack (lua_State *L) { + int inuse = stackinuse(L); + int goodsize = inuse + BASIC_STACK_SIZE; + if (goodsize > LUAI_MAXSTACK) + goodsize = LUAI_MAXSTACK; /* respect stack limit */ + /* if thread is currently not handling a stack overflow and its + good size is smaller than current size, shrink its stack */ + if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize) + luaD_reallocstack(L, goodsize, 0); /* ok if that fails */ + else /* don't change stack */ + condmovestack(L,{},{}); /* (change only for debugging) */ + luaE_shrinkCI(L); /* shrink CI list */ +} + + +void luaD_inctop (lua_State *L) { + luaD_checkstack(L, 1); + L->top++; +} + +/* }================================================================== */ + + +/* +** Call a hook for the given event. Make sure there is a hook to be +** called. (Both 'L->hook' and 'L->hookmask', which trigger this +** function, can be changed asynchronously by signals.) +*/ +void luaD_hook (lua_State *L, int event, int line, + int ftransfer, int ntransfer) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { /* make sure there is a hook */ + int mask = CIST_HOOKED; + CallInfo *ci = L->ci; + ptrdiff_t top = savestack(L, L->top); + ptrdiff_t ci_top = savestack(L, ci->top); + lua_Debug ar; + ar.event = event; + ar.currentline = line; + ar.i_ci = ci; + if (ntransfer != 0) { + mask |= CIST_TRAN; /* 'ci' has transfer information */ + ci->u2.transferinfo.ftransfer = ftransfer; + ci->u2.transferinfo.ntransfer = ntransfer; + } + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + if (L->top + LUA_MINSTACK > ci->top) + ci->top = L->top + LUA_MINSTACK; + L->allowhook = 0; /* cannot call hooks inside a hook */ + ci->callstatus |= mask; + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + ci->top = restorestack(L, ci_top); + L->top = restorestack(L, top); + ci->callstatus &= ~mask; + } +} + + +/* +** Executes a call hook for Lua functions. This function is called +** whenever 'hookmask' is not zero, so it checks whether call hooks are +** active. +*/ +void luaD_hookcall (lua_State *L, CallInfo *ci) { + int hook = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL; + Proto *p; + if (!(L->hookmask & LUA_MASKCALL)) /* some other hook? */ + return; /* don't call hook */ + p = clLvalue(s2v(ci->func))->p; + L->top = ci->top; /* prepare top */ + ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_hook(L, hook, -1, 1, p->numparams); + ci->u.l.savedpc--; /* correct 'pc' */ +} + + +static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) { + ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ + int delta = 0; + if (isLuacode(ci)) { + Proto *p = ci_func(ci)->p; + if (p->is_vararg) + delta = ci->u.l.nextraargs + p->numparams + 1; + if (L->top < ci->top) + L->top = ci->top; /* correct top to run hook */ + } + if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ + int ftransfer; + ci->func += delta; /* if vararg, back to virtual 'func' */ + ftransfer = cast(unsigned short, firstres - ci->func); + luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ + ci->func -= delta; + } + if (isLua(ci = ci->previous)) + L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */ + return restorestack(L, oldtop); +} + + +/* +** Check whether 'func' has a '__call' metafield. If so, put it in the +** stack, below original 'func', so that 'luaD_call' can call it. Raise +** an error if there is no '__call' metafield. +*/ +void luaD_tryfuncTM (lua_State *L, StkId func) { + const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); + StkId p; + if (unlikely(ttisnil(tm))) + luaG_typeerror(L, s2v(func), "call"); /* nothing to call */ + for (p = L->top; p > func; p--) /* open space for metamethod */ + setobjs2s(L, p, p-1); + L->top++; /* stack space pre-allocated by the caller */ + setobj2s(L, func, tm); /* metamethod is the new function to be called */ +} + + +/* +** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. +** Handle most typical cases (zero results for commands, one result for +** expressions, multiple results for tail calls/single parameters) +** separated. +*/ +static void moveresults (lua_State *L, StkId res, int nres, int wanted) { + StkId firstresult; + int i; + switch (wanted) { /* handle typical cases separately */ + case 0: /* no values needed */ + L->top = res; + return; + case 1: /* one value needed */ + if (nres == 0) /* no results? */ + setnilvalue(s2v(res)); /* adjust with nil */ + else + setobjs2s(L, res, L->top - nres); /* move it to proper place */ + L->top = res + 1; + return; + case LUA_MULTRET: + wanted = nres; /* we want all results */ + break; + default: /* multiple results (or to-be-closed variables) */ + if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ + ptrdiff_t savedres = savestack(L, res); + luaF_close(L, res, LUA_OK); /* may change the stack */ + res = restorestack(L, savedres); + wanted = codeNresults(wanted); /* correct value */ + if (wanted == LUA_MULTRET) + wanted = nres; + } + break; + } + firstresult = L->top - nres; /* index of first result */ + /* move all results to correct place */ + for (i = 0; i < nres && i < wanted; i++) + setobjs2s(L, res + i, firstresult + i); + for (; i < wanted; i++) /* complete wanted number of results */ + setnilvalue(s2v(res + i)); + L->top = res + wanted; /* top points after the last result */ +} + + +/* +** Finishes a function call: calls hook if necessary, removes CallInfo, +** moves current number of results to proper place. +*/ +void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { + if (L->hookmask) + L->top = rethook(L, ci, L->top - nres, nres); + L->ci = ci->previous; /* back to caller */ + /* move results to proper place */ + moveresults(L, ci->func, nres, ci->nresults); +} + + + +#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) + + +/* +** Prepare a function for a tail call, building its call info on top +** of the current call info. 'narg1' is the number of arguments plus 1 +** (so that it includes the function itself). +*/ +void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { + Proto *p = clLvalue(s2v(func))->p; + int fsize = p->maxstacksize; /* frame size */ + int nfixparams = p->numparams; + int i; + for (i = 0; i < narg1; i++) /* move down function and arguments */ + setobjs2s(L, ci->func + i, func + i); + checkstackGC(L, fsize); + func = ci->func; /* moved-down function */ + for (; narg1 <= nfixparams; narg1++) + setnilvalue(s2v(func + narg1)); /* complete missing arguments */ + ci->top = func + 1 + fsize; /* top for new function */ + lua_assert(ci->top <= L->stack_last); + ci->u.l.savedpc = p->code; /* starting point */ + ci->callstatus |= CIST_TAIL; + L->top = func + narg1; /* set top */ +} + + +/* +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, all the results are on the stack, starting at the original +** function position. +*/ +void luaD_call (lua_State *L, StkId func, int nresults) { + lua_CFunction f; + retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + f = clCvalue(s2v(func))->f; + goto Cfunc; + case LUA_VLCF: /* light C function */ + f = fvalue(s2v(func)); + Cfunc: { + int n; /* number of returns */ + CallInfo *ci; + checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ + L->ci = ci = next_ci(L); + ci->nresults = nresults; + ci->callstatus = CIST_C; + ci->top = L->top + LUA_MINSTACK; + ci->func = func; + lua_assert(ci->top <= L->stack_last); + if (L->hookmask & LUA_MASKCALL) { + int narg = cast_int(L->top - func) - 1; + luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); + } + lua_unlock(L); + n = (*f)(L); /* do the actual call */ + lua_lock(L); + api_checknelems(L, n); + luaD_poscall(L, ci, n); + break; + } + case LUA_VLCL: { /* Lua function */ + CallInfo *ci; + Proto *p = clLvalue(s2v(func))->p; + int narg = cast_int(L->top - func) - 1; /* number of real arguments */ + int nfixparams = p->numparams; + int fsize = p->maxstacksize; /* frame size */ + checkstackGCp(L, fsize, func); + L->ci = ci = next_ci(L); + ci->nresults = nresults; + ci->u.l.savedpc = p->code; /* starting point */ + ci->callstatus = 0; + ci->top = func + 1 + fsize; + ci->func = func; + L->ci = ci; + for (; narg < nfixparams; narg++) + setnilvalue(s2v(L->top++)); /* complete missing arguments */ + lua_assert(ci->top <= L->stack_last); + luaV_execute(L, ci); /* run the function */ + break; + } + default: { /* not a function */ + checkstackGCp(L, 1, func); /* space for metamethod */ + luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ + goto retry; /* try again with metamethod */ + } + } +} + + +/* +** Similar to 'luaD_call', but does not allow yields during the call. +*/ +void luaD_callnoyield (lua_State *L, StkId func, int nResults) { + incXCcalls(L); + if (getCcalls(L) <= CSTACKERR) { /* possible C stack overflow? */ + luaE_exitCcall(L); /* to compensate decrement in next call */ + luaE_enterCcall(L); /* check properly */ + } + luaD_call(L, func, nResults); + decXCcalls(L); +} + + +/* +** Completes the execution of an interrupted C function, calling its +** continuation function. +*/ +static void finishCcall (lua_State *L, int status) { + CallInfo *ci = L->ci; + int n; + /* must have a continuation and must be able to call it */ + lua_assert(ci->u.c.k != NULL && yieldable(L)); + /* error status can only happen in a protected call */ + lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD); + if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */ + ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */ + L->errfunc = ci->u.c.old_errfunc; /* with the same error function */ + } + /* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already + handled */ + adjustresults(L, ci->nresults); + lua_unlock(L); + n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */ + lua_lock(L); + api_checknelems(L, n); + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ +} + + +/* +** Executes "full continuation" (everything in the stack) of a +** previously interrupted coroutine until the stack is empty (or another +** interruption long-jumps out of the loop). If the coroutine is +** recovering from an error, 'ud' points to the error status, which must +** be passed to the first continuation function (otherwise the default +** status is LUA_YIELD). +*/ +static void unroll (lua_State *L, void *ud) { + CallInfo *ci; + if (ud != NULL) /* error status? */ + finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */ + while ((ci = L->ci) != &L->base_ci) { /* something in the stack */ + if (!isLua(ci)) /* C function? */ + finishCcall(L, LUA_YIELD); /* complete its execution */ + else { /* Lua function */ + luaV_finishOp(L); /* finish interrupted instruction */ + luaV_execute(L, ci); /* execute down to higher C 'boundary' */ + } + } +} + + +/* +** Try to find a suspended protected call (a "recover point") for the +** given thread. +*/ +static CallInfo *findpcall (lua_State *L) { + CallInfo *ci; + for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */ + if (ci->callstatus & CIST_YPCALL) + return ci; + } + return NULL; /* no pending pcall */ +} + + +/* +** Recovers from an error in a coroutine. Finds a recover point (if +** there is one) and completes the execution of the interrupted +** 'luaD_pcall'. If there is no recover point, returns zero. +*/ +static int recover (lua_State *L, int status) { + StkId oldtop; + CallInfo *ci = findpcall(L); + if (ci == NULL) return 0; /* no recovery point */ + /* "finish" luaD_pcall */ + oldtop = restorestack(L, ci->u2.funcidx); + luaF_close(L, oldtop, status); /* may change the stack */ + oldtop = restorestack(L, ci->u2.funcidx); + luaD_seterrorobj(L, status, oldtop); + L->ci = ci; + L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ + luaD_shrinkstack(L); + L->errfunc = ci->u.c.old_errfunc; + return 1; /* continue running the coroutine */ +} + + +/* +** Signal an error in the call to 'lua_resume', not in the execution +** of the coroutine itself. (Such errors should not be handled by any +** coroutine error handler and should not kill the coroutine.) +*/ +static int resume_error (lua_State *L, const char *msg, int narg) { + L->top -= narg; /* remove args from the stack */ + setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ + api_incr_top(L); + lua_unlock(L); + return LUA_ERRRUN; +} + + +/* +** Do the work for 'lua_resume' in protected mode. Most of the work +** depends on the status of the coroutine: initial state, suspended +** inside a hook, or regularly suspended (optionally with a continuation +** function), plus erroneous cases: non-suspended coroutine or dead +** coroutine. +*/ +static void resume (lua_State *L, void *ud) { + int n = *(cast(int*, ud)); /* number of arguments */ + StkId firstArg = L->top - n; /* first argument */ + CallInfo *ci = L->ci; + if (L->status == LUA_OK) { /* starting a coroutine? */ + luaD_call(L, firstArg - 1, LUA_MULTRET); + } + else { /* resuming from previous yield */ + lua_assert(L->status == LUA_YIELD); + L->status = LUA_OK; /* mark that it is running (again) */ + if (isLua(ci)) /* yielded inside a hook? */ + luaV_execute(L, ci); /* just continue running Lua code */ + else { /* 'common' yield */ + if (ci->u.c.k != NULL) { /* does it have a continuation function? */ + lua_unlock(L); + n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */ + lua_lock(L); + api_checknelems(L, n); + } + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ + } + unroll(L, NULL); /* run continuation */ + } +} + +LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, + int *nresults) { + int status; + lua_lock(L); + if (L->status == LUA_OK) { /* may be starting a coroutine */ + if (L->ci != &L->base_ci) /* not in base level? */ + return resume_error(L, "cannot resume non-suspended coroutine", nargs); + else if (L->top - (L->ci->func + 1) == nargs) /* no function? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + } + else if (L->status != LUA_YIELD) /* ended with errors? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + if (from == NULL) + L->nCcalls = CSTACKTHREAD; + else /* correct 'nCcalls' for this thread */ + L->nCcalls = getCcalls(from) - L->nci - CSTACKCF; + if (L->nCcalls <= CSTACKERR) + return resume_error(L, "C stack overflow", nargs); + luai_userstateresume(L, nargs); + api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); + status = luaD_rawrunprotected(L, resume, &nargs); + /* continue running after recoverable errors */ + while (errorstatus(status) && recover(L, status)) { + /* unroll continuation */ + status = luaD_rawrunprotected(L, unroll, &status); + } + if (likely(!errorstatus(status))) + lua_assert(status == L->status); /* normal end or yield */ + else { /* unrecoverable error */ + L->status = cast_byte(status); /* mark thread as 'dead' */ + luaD_seterrorobj(L, status, L->top); /* push error message */ + L->ci->top = L->top; + } + *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield + : cast_int(L->top - (L->ci->func + 1)); + lua_unlock(L); + return status; +} + + +LUA_API int lua_isyieldable (lua_State *L) { + return yieldable(L); +} + + +LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k) { + CallInfo *ci; + luai_userstateyield(L, nresults); + lua_lock(L); + ci = L->ci; + api_checknelems(L, nresults); + if (unlikely(!yieldable(L))) { + if (L != G(L)->mainthread) + luaG_runerror(L, "attempt to yield across a C-call boundary"); + else + luaG_runerror(L, "attempt to yield from outside a coroutine"); + } + L->status = LUA_YIELD; + if (isLua(ci)) { /* inside a hook? */ + lua_assert(!isLuacode(ci)); + api_check(L, k == NULL, "hooks cannot continue after yielding"); + ci->u2.nyield = 0; /* no results */ + } + else { + if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ + ci->u.c.ctx = ctx; /* save context */ + ci->u2.nyield = nresults; /* save number of results */ + luaD_throw(L, LUA_YIELD); + } + lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ + lua_unlock(L); + return 0; /* return to 'luaD_hook' */ +} + + +/* +** Call the C function 'func' in protected mode, restoring basic +** thread information ('allowhook', etc.) and in particular +** its stack level in case of errors. +*/ +int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t old_top, ptrdiff_t ef) { + int status; + CallInfo *old_ci = L->ci; + lu_byte old_allowhooks = L->allowhook; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (unlikely(status != LUA_OK)) { /* an error occurred? */ + StkId oldtop = restorestack(L, old_top); + L->ci = old_ci; + L->allowhook = old_allowhooks; + status = luaF_close(L, oldtop, status); + oldtop = restorestack(L, old_top); /* previous call may change stack */ + luaD_seterrorobj(L, status, oldtop); + luaD_shrinkstack(L); + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to 'f_parser' */ + ZIO *z; + Mbuffer buff; /* dynamic structure used by the scanner */ + Dyndata dyd; /* dynamic structures used by the parser */ + const char *mode; + const char *name; +}; + + +static void checkmode (lua_State *L, const char *mode, const char *x) { + if (mode && strchr(mode, x[0]) == NULL) { + luaO_pushfstring(L, + "attempt to load a %s chunk (mode is '%s')", x, mode); + luaD_throw(L, LUA_ERRSYNTAX); + } +} + + +static void f_parser (lua_State *L, void *ud) { + LClosure *cl; + struct SParser *p = cast(struct SParser *, ud); + int c = zgetc(p->z); /* read first character */ + if (c == LUA_SIGNATURE[0]) { + checkmode(L, p->mode, "binary"); + cl = luaU_undump(L, p->z, p->name); + } + else { + checkmode(L, p->mode, "text"); + cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); + } + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luaF_initupvals(L, cl); +} + + +int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode) { + struct SParser p; + int status; + incnny(L); /* cannot yield during parsing */ + p.z = z; p.name = name; p.mode = mode; + p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; + p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; + p.dyd.label.arr = NULL; p.dyd.label.size = 0; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + luaZ_freebuffer(L, &p.buff); + luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); + luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); + luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); + decnny(L); + return status; +} + + diff --git a/Lua/ldo.h b/Lua/ldo.h new file mode 100644 index 00000000..6c6cb285 --- /dev/null +++ b/Lua/ldo.h @@ -0,0 +1,77 @@ +/* +** $Id: ldo.h $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +#include "lobject.h" +#include "lstate.h" +#include "lzio.h" + + +/* +** Macro to check stack size and grow stack if needed. Parameters +** 'pre'/'pos' allow the macro to preserve a pointer into the +** stack across reallocations, doing the work only when needed. +** It also allows the running of one GC step when the stack is +** reallocated. +** 'condmovestack' is used in heavy tests to force a stack reallocation +** at every check. +*/ +#define luaD_checkstackaux(L,n,pre,pos) \ + if (L->stack_last - L->top <= (n)) \ + { pre; luaD_growstack(L, n, 1); pos; } \ + else { condmovestack(L,pre,pos); } + +/* In general, 'pre'/'pos' are empty (nothing to save) */ +#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) + + + +#define savestack(L,p) ((char *)(p) - (char *)L->stack) +#define restorestack(L,n) ((StkId)((char *)L->stack + (n))) + + +/* macro to check stack size, preserving 'p' */ +#define checkstackGCp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ + luaC_checkGC(L), /* stack grow uses memory */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* macro to check stack size and GC */ +#define checkstackGC(L,fsize) \ + luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0) + + +/* type of protected functions, to be ran by 'runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); +LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode); +LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, + int fTransfer, int nTransfer); +LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); +LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); +LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); +LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); +LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); +LUAI_FUNC void luaD_shrinkstack (lua_State *L); +LUAI_FUNC void luaD_inctop (lua_State *L); + +LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); +LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +#endif + diff --git a/Lua/ldump.c b/Lua/ldump.c new file mode 100644 index 00000000..f848b669 --- /dev/null +++ b/Lua/ldump.c @@ -0,0 +1,226 @@ +/* +** $Id: ldump.c $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#define ldump_c +#define LUA_CORE + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lundump.h" + + +typedef struct { + lua_State *L; + lua_Writer writer; + void *data; + int strip; + int status; +} DumpState; + + +/* +** All high-level dumps go through dumpVector; you can change it to +** change the endianness of the result +*/ +#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0])) + +#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char)) + + +static void dumpBlock (DumpState *D, const void *b, size_t size) { + if (D->status == 0 && size > 0) { + lua_unlock(D->L); + D->status = (*D->writer)(D->L, b, size, D->data); + lua_lock(D->L); + } +} + + +#define dumpVar(D,x) dumpVector(D,&x,1) + + +static void dumpByte (DumpState *D, int y) { + lu_byte x = (lu_byte)y; + dumpVar(D, x); +} + + +/* dumpInt Buff Size */ +#define DIBS ((sizeof(size_t) * 8 / 7) + 1) + +static void dumpSize (DumpState *D, size_t x) { + lu_byte buff[DIBS]; + int n = 0; + do { + buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */ + x >>= 7; + } while (x != 0); + buff[DIBS - 1] |= 0x80; /* mark last byte */ + dumpVector(D, buff + DIBS - n, n); +} + + +static void dumpInt (DumpState *D, int x) { + dumpSize(D, x); +} + + +static void dumpNumber (DumpState *D, lua_Number x) { + dumpVar(D, x); +} + + +static void dumpInteger (DumpState *D, lua_Integer x) { + dumpVar(D, x); +} + + +static void dumpString (DumpState *D, const TString *s) { + if (s == NULL) + dumpSize(D, 0); + else { + size_t size = tsslen(s); + const char *str = getstr(s); + dumpSize(D, size + 1); + dumpVector(D, str, size); + } +} + + +static void dumpCode (DumpState *D, const Proto *f) { + dumpInt(D, f->sizecode); + dumpVector(D, f->code, f->sizecode); +} + + +static void dumpFunction(DumpState *D, const Proto *f, TString *psource); + +static void dumpConstants (DumpState *D, const Proto *f) { + int i; + int n = f->sizek; + dumpInt(D, n); + for (i = 0; i < n; i++) { + const TValue *o = &f->k[i]; + int tt = ttypetag(o); + dumpByte(D, tt); + switch (tt) { + case LUA_VNUMFLT: + dumpNumber(D, fltvalue(o)); + break; + case LUA_VNUMINT: + dumpInteger(D, ivalue(o)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + dumpString(D, tsvalue(o)); + break; + default: + lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE); + } + } +} + + +static void dumpProtos (DumpState *D, const Proto *f) { + int i; + int n = f->sizep; + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpFunction(D, f->p[i], f->source); +} + + +static void dumpUpvalues (DumpState *D, const Proto *f) { + int i, n = f->sizeupvalues; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpByte(D, f->upvalues[i].instack); + dumpByte(D, f->upvalues[i].idx); + dumpByte(D, f->upvalues[i].kind); + } +} + + +static void dumpDebug (DumpState *D, const Proto *f) { + int i, n; + n = (D->strip) ? 0 : f->sizelineinfo; + dumpInt(D, n); + dumpVector(D, f->lineinfo, n); + n = (D->strip) ? 0 : f->sizeabslineinfo; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpInt(D, f->abslineinfo[i].pc); + dumpInt(D, f->abslineinfo[i].line); + } + n = (D->strip) ? 0 : f->sizelocvars; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpString(D, f->locvars[i].varname); + dumpInt(D, f->locvars[i].startpc); + dumpInt(D, f->locvars[i].endpc); + } + n = (D->strip) ? 0 : f->sizeupvalues; + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpString(D, f->upvalues[i].name); +} + + +static void dumpFunction (DumpState *D, const Proto *f, TString *psource) { + if (D->strip || f->source == psource) + dumpString(D, NULL); /* no debug info or same source as its parent */ + else + dumpString(D, f->source); + dumpInt(D, f->linedefined); + dumpInt(D, f->lastlinedefined); + dumpByte(D, f->numparams); + dumpByte(D, f->is_vararg); + dumpByte(D, f->maxstacksize); + dumpCode(D, f); + dumpConstants(D, f); + dumpUpvalues(D, f); + dumpProtos(D, f); + dumpDebug(D, f); +} + + +static void dumpHeader (DumpState *D) { + dumpLiteral(D, LUA_SIGNATURE); + dumpByte(D, LUAC_VERSION); + dumpByte(D, LUAC_FORMAT); + dumpLiteral(D, LUAC_DATA); + dumpByte(D, sizeof(Instruction)); + dumpByte(D, sizeof(lua_Integer)); + dumpByte(D, sizeof(lua_Number)); + dumpInteger(D, LUAC_INT); + dumpNumber(D, LUAC_NUM); +} + + +/* +** dump Lua function as precompiled chunk +*/ +int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, + int strip) { + DumpState D; + D.L = L; + D.writer = w; + D.data = data; + D.strip = strip; + D.status = 0; + dumpHeader(&D); + dumpByte(&D, f->sizeupvalues); + dumpFunction(&D, f, NULL); + return D.status; +} + diff --git a/Lua/lfunc.c b/Lua/lfunc.c new file mode 100644 index 00000000..88d45328 --- /dev/null +++ b/Lua/lfunc.c @@ -0,0 +1,300 @@ +/* +** $Id: lfunc.c $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#define lfunc_c +#define LUA_CORE + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +CClosure *luaF_newCclosure (lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals)); + CClosure *c = gco2ccl(o); + c->nupvalues = cast_byte(nupvals); + return c; +} + + +LClosure *luaF_newLclosure (lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals)); + LClosure *c = gco2lcl(o); + c->p = NULL; + c->nupvalues = cast_byte(nupvals); + while (nupvals--) c->upvals[nupvals] = NULL; + return c; +} + + +/* +** fill a closure with new closed upvalues +*/ +void luaF_initupvals (lua_State *L, LClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + uv->v = &uv->u.value; /* make it closed */ + setnilvalue(uv->v); + cl->upvals[i] = uv; + luaC_objbarrier(L, cl, o); + } +} + + +/* +** Create a new upvalue at the given level, and link it to the list of +** open upvalues of 'L' after entry 'prev'. +**/ +static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + UpVal *next = *prev; + uv->v = s2v(level); /* current value lives in the stack */ + uv->tbc = tbc; + uv->u.open.next = next; /* link it to list of open upvalues */ + uv->u.open.previous = prev; + if (next) + next->u.open.previous = &uv->u.open.next; + *prev = uv; + if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ + L->twups = G(L)->twups; /* link it to the list */ + G(L)->twups = L; + } + return uv; +} + + +/* +** Find and reuse, or create if it does not exist, an upvalue +** at the given level. +*/ +UpVal *luaF_findupval (lua_State *L, StkId level) { + UpVal **pp = &L->openupval; + UpVal *p; + lua_assert(isintwups(L) || L->openupval == NULL); + while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */ + lua_assert(!isdead(G(L), p)); + if (uplevel(p) == level) /* corresponding upvalue? */ + return p; /* return it */ + pp = &p->u.open.next; + } + /* not found: create a new upvalue after 'pp' */ + return newupval(L, 0, level, pp); +} + + +static void callclose (lua_State *L, void *ud) { + UNUSED(ud); + luaD_callnoyield(L, L->top - 3, 0); +} + + +/* +** Prepare closing method plus its arguments for object 'obj' with +** error message 'err'. (This function assumes EXTRA_STACK.) +*/ +static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { + StkId top = L->top; + const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); + if (ttisnil(tm)) /* no metamethod? */ + return 0; /* nothing to call */ + setobj2s(L, top, tm); /* will call metamethod... */ + setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ + setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ + L->top = top + 3; /* add function and arguments */ + return 1; +} + + +/* +** Raise an error with message 'msg', inserting the name of the +** local variable at position 'level' in the stack. +*/ +static void varerror (lua_State *L, StkId level, const char *msg) { + int idx = cast_int(level - L->ci->func); + const char *vname = luaG_findlocal(L, L->ci, idx, NULL); + if (vname == NULL) vname = "?"; + luaG_runerror(L, msg, vname); +} + + +/* +** Prepare and call a closing method. If status is OK, code is still +** inside the original protected call, and so any error will be handled +** there. Otherwise, a previous error already activated the original +** protected call, and so the call to the closing method must be +** protected here. (A status == CLOSEPROTECT behaves like a previous +** error, to also run the closing method in protected mode). +** If status is OK, the call to the closing method will be pushed +** at the top of the stack. Otherwise, values are pushed after +** the 'level' of the upvalue being closed, as everything after +** that won't be used again. +*/ +static int callclosemth (lua_State *L, StkId level, int status) { + TValue *uv = s2v(level); /* value being closed */ + if (likely(status == LUA_OK)) { + if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ + callclose(L, NULL); /* call closing method */ + else if (!l_isfalse(uv)) /* non-closable non-false value? */ + varerror(L, level, "attempt to close non-closable variable '%s'"); + } + else { /* must close the object in protected mode */ + ptrdiff_t oldtop; + level++; /* space for error message */ + oldtop = savestack(L, level + 1); /* top will be after that */ + luaD_seterrorobj(L, status, level); /* set error message */ + if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */ + int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0); + if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */ + status = newstatus; /* this will be the new error */ + else { + if (newstatus != LUA_OK) /* suppressed error? */ + luaE_warnerror(L, "__close metamethod"); + /* leave original error (or nil) on top */ + L->top = restorestack(L, oldtop); + } + } + /* else no metamethod; ignore this case and keep original error */ + } + return status; +} + + +/* +** Try to create a to-be-closed upvalue +** (can raise a memory-allocation error) +*/ +static void trynewtbcupval (lua_State *L, void *ud) { + newupval(L, 1, cast(StkId, ud), &L->openupval); +} + + +/* +** Create a to-be-closed upvalue. If there is a memory error +** when creating the upvalue, the closing method must be called here, +** as there is no upvalue to call it later. +*/ +void luaF_newtbcupval (lua_State *L, StkId level) { + TValue *obj = s2v(level); + lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); + if (!l_isfalse(obj)) { /* false doesn't need to be closed */ + int status; + const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); + if (ttisnil(tm)) /* no metamethod? */ + varerror(L, level, "variable '%s' got a non-closable value"); + status = luaD_rawrunprotected(L, trynewtbcupval, level); + if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ + lua_assert(status == LUA_ERRMEM); + luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ + /* next call must succeed, as object is closable */ + prepclosingmethod(L, s2v(level), s2v(level + 1)); + callclose(L, NULL); /* call closing method */ + luaD_throw(L, LUA_ERRMEM); /* throw memory error */ + } + } +} + + +void luaF_unlinkupval (UpVal *uv) { + lua_assert(upisopen(uv)); + *uv->u.open.previous = uv->u.open.next; + if (uv->u.open.next) + uv->u.open.next->u.open.previous = uv->u.open.previous; +} + + +int luaF_close (lua_State *L, StkId level, int status) { + UpVal *uv; + while ((uv = L->openupval) != NULL && uplevel(uv) >= level) { + TValue *slot = &uv->u.value; /* new position for value */ + lua_assert(uplevel(uv) < L->top); + if (uv->tbc && status != NOCLOSINGMETH) { + /* must run closing method, which may change the stack */ + ptrdiff_t levelrel = savestack(L, level); + status = callclosemth(L, uplevel(uv), status); + level = restorestack(L, levelrel); + } + luaF_unlinkupval(uv); + setobj(L, slot, uv->v); /* move value to upvalue slot */ + uv->v = slot; /* now current value lives here */ + if (!iswhite(uv)) { /* neither white nor dead? */ + nw2black(uv); /* closed upvalues cannot be gray */ + luaC_barrier(L, uv, slot); + } + } + return status; +} + + +Proto *luaF_newproto (lua_State *L) { + GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto)); + Proto *f = gco2p(o); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->lineinfo = NULL; + f->sizelineinfo = 0; + f->abslineinfo = NULL; + f->sizeabslineinfo = 0; + f->upvalues = NULL; + f->sizeupvalues = 0; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->locvars = NULL; + f->sizelocvars = 0; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +void luaF_freeproto (lua_State *L, Proto *f) { + luaM_freearray(L, f->code, f->sizecode); + luaM_freearray(L, f->p, f->sizep); + luaM_freearray(L, f->k, f->sizek); + luaM_freearray(L, f->lineinfo, f->sizelineinfo); + luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo); + luaM_freearray(L, f->locvars, f->sizelocvars); + luaM_freearray(L, f->upvalues, f->sizeupvalues); + luaM_free(L, f); +} + + +/* +** Look for n-th local variable at line 'line' in function 'func'. +** Returns NULL if not found. +*/ +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + diff --git a/Lua/lfunc.h b/Lua/lfunc.h new file mode 100644 index 00000000..8d6f965c --- /dev/null +++ b/Lua/lfunc.h @@ -0,0 +1,69 @@ +/* +** $Id: lfunc.h $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +#include "lobject.h" + + +#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \ + cast_int(sizeof(TValue)) * (n)) + +#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \ + cast_int(sizeof(TValue *)) * (n)) + + +/* test whether thread is in 'twups' list */ +#define isintwups(L) (L->twups != L) + + +/* +** maximum number of upvalues in a closure (both C and Lua). (Value +** must fit in a VM register.) +*/ +#define MAXUPVAL 255 + + +#define upisopen(up) ((up)->v != &(up)->u.value) + + +#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v)) + + +/* +** maximum number of misses before giving up the cache of closures +** in prototypes +*/ +#define MAXMISS 10 + + +/* +** Special "status" for 'luaF_close' +*/ + +/* close upvalues without running their closing methods */ +#define NOCLOSINGMETH (-1) + +/* close upvalues running all closing methods in protected mode */ +#define CLOSEPROTECT (-2) + + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals); +LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); +LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); +LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status); +LUAI_FUNC void luaF_unlinkupval (UpVal *uv); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif diff --git a/Lua/lgc.c b/Lua/lgc.c new file mode 100644 index 00000000..4a7bcaed --- /dev/null +++ b/Lua/lgc.c @@ -0,0 +1,1710 @@ +/* +** $Id: lgc.c $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#define lgc_c +#define LUA_CORE + +#include "lprefix.h" + +#include +#include + + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +/* +** Maximum number of elements to sweep in each single step. +** (Large enough to dissipate fixed overheads but small enough +** to allow small steps for the collector.) +*/ +#define GCSWEEPMAX 100 + +/* +** Maximum number of finalizers to call in each single step. +*/ +#define GCFINMAX 10 + + +/* +** Cost of calling one finalizer. +*/ +#define GCFINALIZECOST 50 + + +/* +** The equivalent, in bytes, of one unit of "work" (visiting a slot, +** sweeping an object, etc.) +*/ +#define WORK2MEM sizeof(TValue) + + +/* +** macro to adjust 'pause': 'pause' is actually used like +** 'pause / PAUSEADJ' (value chosen by tests) +*/ +#define PAUSEADJ 100 + + +/* mask with all color bits */ +#define maskcolors (bitmask(BLACKBIT) | WHITEBITS) + +/* mask with all GC bits */ +#define maskgcbits (maskcolors | AGEBITS) + + +/* macro to erase all color bits then set only the current white bit */ +#define makewhite(g,x) \ + (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g))) + +/* make an object gray (neither white nor black) */ +#define set2gray(x) resetbits(x->marked, maskcolors) + + +/* make an object black (coming from any color) */ +#define set2black(x) \ + (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT))) + + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) + + +/* +** Protected access to objects in values +*/ +#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) + + +#define markvalue(g,o) { checkliveness(g->mainthread,o); \ + if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } + +#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } + +#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } + +/* +** mark an object that can be NULL (either because it is really optional, +** or it was stripped as debug info, or inside an uncompleted structure) +*/ +#define markobjectN(g,t) { if (t) markobject(g,t); } + +static void reallymarkobject (global_State *g, GCObject *o); +static lu_mem atomic (lua_State *L); +static void entersweep (lua_State *L); + + +/* +** {====================================================== +** Generic functions +** ======================================================= +*/ + + +/* +** one after last element in a hash array +*/ +#define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) + + +static GCObject **getgclist (GCObject *o) { + switch (o->tt) { + case LUA_VTABLE: return &gco2t(o)->gclist; + case LUA_VLCL: return &gco2lcl(o)->gclist; + case LUA_VCCL: return &gco2ccl(o)->gclist; + case LUA_VTHREAD: return &gco2th(o)->gclist; + case LUA_VPROTO: return &gco2p(o)->gclist; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + lua_assert(u->nuvalue > 0); + return &u->gclist; + } + default: lua_assert(0); return 0; + } +} + + +/* +** Link a collectable object 'o' with a known type into the list 'p'. +** (Must be a macro to access the 'gclist' field in different types.) +*/ +#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p)) + +static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) { + lua_assert(!isgray(o)); /* cannot be in a gray list */ + *pnext = *list; + *list = o; + set2gray(o); /* now it is */ +} + + +/* +** Link a generic collectable object 'o' into the list 'p'. +*/ +#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p)) + + + +/* +** Clear keys for empty entries in tables. If entry is empty +** and its key is not marked, mark its entry as dead. This allows the +** collection of the key, but keeps its entry in the table (its removal +** could break a chain). The main feature of a dead key is that it must +** be different from any other value, to do not disturb searches. +** Other places never manipulate dead keys, because its associated empty +** value is enough to signal that the entry is logically empty. +*/ +static void clearkey (Node *n) { + lua_assert(isempty(gval(n))); + if (keyiswhite(n)) + setdeadkey(n); /* unused and unmarked key; remove it */ +} + + +/* +** tells whether a key or value can be cleared from a weak +** table. Non-collectable objects are never removed from weak +** tables. Strings behave as 'values', so are never removed too. for +** other objects: if really collected, cannot keep them; for objects +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (global_State *g, const GCObject *o) { + if (o == NULL) return 0; /* non-collectable value */ + else if (novariant(o->tt) == LUA_TSTRING) { + markobject(g, o); /* strings are 'values', so are never weak */ + return 0; + } + else return iswhite(o); +} + + +/* +** Barrier that moves collector forward, that is, marks the white object +** 'v' being pointed by the black object 'o'. In the generational +** mode, 'v' must also become old, if 'o' is old; however, it cannot +** be changed directly to OLD, because it may still point to non-old +** objects. So, it is marked as OLD0. In the next cycle it will become +** OLD1, and in the next it will finally become OLD (regular old). By +** then, any object it points to will also be old. If called in the +** incremental sweep phase, it clears the black object to white (sweep +** it) to avoid other barrier calls for this same object. (That cannot +** be done is generational mode, as its sweep does not distinguish +** whites from deads.) +*/ +void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + if (keepinvariant(g)) { /* must keep invariant? */ + reallymarkobject(g, v); /* restore invariant */ + if (isold(o)) { + lua_assert(!isold(v)); /* white object could not be old */ + setage(v, G_OLD0); /* restore generational invariant */ + } + } + else { /* sweep phase */ + lua_assert(issweepphase(g)); + if (g->gckind == KGC_INC) /* incremental mode? */ + makewhite(g, o); /* mark 'o' as white to avoid other barriers */ + } +} + + +/* +** barrier that moves collector backward, that is, mark the black object +** pointing to a white object as gray again. +*/ +void luaC_barrierback_ (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); + if (getage(o) == G_TOUCHED2) /* already in gray list? */ + set2gray(o); /* make it gray to become touched1 */ + else /* link it in 'grayagain' and paint it gray */ + linkobjgclist(o, g->grayagain); + if (isold(o)) /* generational mode? */ + setage(o, G_TOUCHED1); /* touched in current cycle */ +} + + +void luaC_fix (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ + set2gray(o); /* they will be gray forever */ + setage(o, G_OLD); /* and old forever */ + g->allgc = o->next; /* remove object from 'allgc' list */ + o->next = g->fixedgc; /* link it to 'fixedgc' list */ + g->fixedgc = o; +} + + +/* +** create a new collectable object (with given type and size) and link +** it to 'allgc' list. +*/ +GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { + global_State *g = G(L); + GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); + o->marked = luaC_white(g); + o->tt = tt; + o->next = g->allgc; + g->allgc = o; + return o; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Mark functions +** ======================================================= +*/ + + +/* +** Mark an object. Userdata with no user values, strings, and closed +** upvalues are visited and turned black here. Open upvalues are +** already indirectly linked through their respective threads in the +** 'twups' list, so they don't go to the gray list; nevertheless, they +** are kept gray to avoid barriers, as their values will be revisited +** by the thread or by 'remarkupvals'. Other objects are added to the +** gray list to be visited (and turned black) later. Both userdata and +** upvalues can call this function recursively, but this recursion goes +** for at most two levels: An upvalue cannot refer to another upvalue +** (only closures can), and a userdata's metatable must be a table. +*/ +static void reallymarkobject (global_State *g, GCObject *o) { + switch (o->tt) { + case LUA_VSHRSTR: + case LUA_VLNGSTR: { + set2black(o); /* nothing to visit */ + break; + } + case LUA_VUPVAL: { + UpVal *uv = gco2upv(o); + if (upisopen(uv)) + set2gray(uv); /* open upvalues are kept gray */ + else + set2black(o); /* closed upvalues are visited here */ + markvalue(g, uv->v); /* mark its content */ + break; + } + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + if (u->nuvalue == 0) { /* no user values? */ + markobjectN(g, u->metatable); /* mark its metatable */ + set2black(o); /* nothing else to mark */ + break; + } + /* else... */ + } /* FALLTHROUGH */ + case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: + case LUA_VTHREAD: case LUA_VPROTO: { + linkobjgclist(o, g->gray); /* to be visited later */ + break; + } + default: lua_assert(0); break; + } +} + + +/* +** mark metamethods for basic types +*/ +static void markmt (global_State *g) { + int i; + for (i=0; i < LUA_NUMTAGS; i++) + markobjectN(g, g->mt[i]); +} + + +/* +** mark all objects in list of being-finalized +*/ +static lu_mem markbeingfnz (global_State *g) { + GCObject *o; + lu_mem count = 0; + for (o = g->tobefnz; o != NULL; o = o->next) { + count++; + markobject(g, o); + } + return count; +} + + +/* +** For each non-marked thread, simulates a barrier between each open +** upvalue and its value. (If the thread is collected, the value will be +** assigned to the upvalue, but then it can be too late for the barrier +** to act. The "barrier" does not need to check colors: A non-marked +** thread must be young; upvalues cannot be older than their threads; so +** any visited upvalue must be young too.) Also removes the thread from +** the list, as it was already visited. Removes also threads with no +** upvalues, as they have nothing to be checked. (If the thread gets an +** upvalue later, it will be linked in the list again.) +*/ +static int remarkupvals (global_State *g) { + lua_State *thread; + lua_State **p = &g->twups; + int work = 0; /* estimate of how much work was done here */ + while ((thread = *p) != NULL) { + work++; + if (!iswhite(thread) && thread->openupval != NULL) + p = &thread->twups; /* keep marked thread with upvalues in the list */ + else { /* thread is not marked or without upvalues */ + UpVal *uv; + lua_assert(!isold(thread) || thread->openupval == NULL); + *p = thread->twups; /* remove thread from the list */ + thread->twups = thread; /* mark that it is out of list */ + for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { + lua_assert(getage(uv) <= getage(thread)); + work++; + if (!iswhite(uv)) { /* upvalue already visited? */ + lua_assert(upisopen(uv) && isgray(uv)); + markvalue(g, uv->v); /* mark its value */ + } + } + } + } + return work; +} + + +static void cleargraylists (global_State *g) { + g->gray = g->grayagain = NULL; + g->weak = g->allweak = g->ephemeron = NULL; +} + + +/* +** mark root set and reset all gray lists, to start a new collection +*/ +static void restartcollection (global_State *g) { + cleargraylists(g); + markobject(g, g->mainthread); + markvalue(g, &g->l_registry); + markmt(g); + markbeingfnz(g); /* mark any finalizing object left from previous cycle */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Traverse functions +** ======================================================= +*/ + + +/* +** Check whether object 'o' should be kept in the 'grayagain' list for +** post-processing by 'correctgraylist'. (It could put all old objects +** in the list and leave all the work to 'correctgraylist', but it is +** more efficient to avoid adding elements that will be removed.) Only +** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go +** back to a gray list, but then it must become OLD. (That is what +** 'correctgraylist' does when it finds a TOUCHED2 object.) +*/ +static void genlink (global_State *g, GCObject *o) { + lua_assert(isblack(o)); + if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ + linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ + } /* everything else do not need to be linked back */ + else if (getage(o) == G_TOUCHED2) + changeage(o, G_TOUCHED2, G_OLD); /* advance age */ +} + + +/* +** Traverse a table with weak values and link it to proper list. During +** propagate phase, keep it in 'grayagain' list, to be revisited in the +** atomic phase. In the atomic phase, if table has any white value, +** put it in 'weak' list, to be cleared. +*/ +static void traverseweakvalue (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + /* if there is array part, assume it may have white values (it is not + worth traversing it now just to check) */ + int hasclears = (h->alimit > 0); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else { + lua_assert(!keyisnil(n)); + markkey(g, n); + if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */ + hasclears = 1; /* table will have to be cleared */ + } + } + if (g->gcstate == GCSatomic && hasclears) + linkgclist(h, g->weak); /* has to be cleared later */ + else + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ +} + + +/* +** Traverse an ephemeron table and link it to proper list. Returns true +** iff any object was marked during this traversal (which implies that +** convergence has to continue). During propagation phase, keep table +** in 'grayagain' list, to be visited again in the atomic phase. In +** the atomic phase, if table has any white->white entry, it has to +** be revisited during ephemeron convergence (as that key may turn +** black). Otherwise, if it has any white key, table has to be cleared +** (in the atomic phase). In generational mode, some tables +** must be kept in some gray list for post-processing; this is done +** by 'genlink'. +*/ +static int traverseephemeron (global_State *g, Table *h, int inv) { + int marked = 0; /* true if an object is marked in this traversal */ + int hasclears = 0; /* true if table has white keys */ + int hasww = 0; /* true if table has entry "white-key -> white-value" */ + unsigned int i; + unsigned int asize = luaH_realasize(h); + unsigned int nsize = sizenode(h); + /* traverse array part */ + for (i = 0; i < asize; i++) { + if (valiswhite(&h->array[i])) { + marked = 1; + reallymarkobject(g, gcvalue(&h->array[i])); + } + } + /* traverse hash part; if 'inv', traverse descending + (see 'convergeephemerons') */ + for (i = 0; i < nsize; i++) { + Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i); + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ + hasclears = 1; /* table must be cleared */ + if (valiswhite(gval(n))) /* value not marked yet? */ + hasww = 1; /* white-white entry */ + } + else if (valiswhite(gval(n))) { /* value not marked yet? */ + marked = 1; + reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ + } + } + /* link table into proper list */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasww) /* table has white->white entries? */ + linkgclist(h, g->ephemeron); /* have to propagate again */ + else if (hasclears) /* table has white keys? */ + linkgclist(h, g->allweak); /* may have to clean white keys */ + else + genlink(g, obj2gco(h)); /* check whether collector still needs to see it */ + return marked; +} + + +static void traversestrongtable (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + unsigned int i; + unsigned int asize = luaH_realasize(h); + for (i = 0; i < asize; i++) /* traverse array part */ + markvalue(g, &h->array[i]); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else { + lua_assert(!keyisnil(n)); + markkey(g, n); + markvalue(g, gval(n)); + } + } + genlink(g, obj2gco(h)); +} + + +static lu_mem traversetable (global_State *g, Table *h) { + const char *weakkey, *weakvalue; + const TValue *mode = gfasttm(g, h->metatable, TM_MODE); + markobjectN(g, h->metatable); + if (mode && ttisstring(mode) && /* is there a weak mode? */ + (cast_void(weakkey = strchr(svalue(mode), 'k')), + cast_void(weakvalue = strchr(svalue(mode), 'v')), + (weakkey || weakvalue))) { /* is really weak? */ + if (!weakkey) /* strong keys? */ + traverseweakvalue(g, h); + else if (!weakvalue) /* strong values? */ + traverseephemeron(g, h, 0); + else /* all weak */ + linkgclist(h, g->allweak); /* nothing to traverse now */ + } + else /* not weak */ + traversestrongtable(g, h); + return 1 + h->alimit + 2 * allocsizenode(h); +} + + +static int traverseudata (global_State *g, Udata *u) { + int i; + markobjectN(g, u->metatable); /* mark its metatable */ + for (i = 0; i < u->nuvalue; i++) + markvalue(g, &u->uv[i].uv); + genlink(g, obj2gco(u)); + return 1 + u->nuvalue; +} + + +/* +** Traverse a prototype. (While a prototype is being build, its +** arrays can be larger than needed; the extra slots are filled with +** NULL, so the use of 'markobjectN') +*/ +static int traverseproto (global_State *g, Proto *f) { + int i; + markobjectN(g, f->source); + for (i = 0; i < f->sizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ + markobjectN(g, f->upvalues[i].name); + for (i = 0; i < f->sizep; i++) /* mark nested protos */ + markobjectN(g, f->p[i]); + for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ + markobjectN(g, f->locvars[i].varname); + return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; +} + + +static int traverseCclosure (global_State *g, CClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->upvalue[i]); + return 1 + cl->nupvalues; +} + +/* +** Traverse a Lua closure, marking its prototype and its upvalues. +** (Both can be NULL while closure is being created.) +*/ +static int traverseLclosure (global_State *g, LClosure *cl) { + int i; + markobjectN(g, cl->p); /* mark its prototype */ + for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ + UpVal *uv = cl->upvals[i]; + markobjectN(g, uv); /* mark upvalue */ + } + return 1 + cl->nupvalues; +} + + +/* +** Traverse a thread, marking the elements in the stack up to its top +** and cleaning the rest of the stack in the final traversal. That +** ensures that the entire stack have valid (non-dead) objects. +** Threads have no barriers. In gen. mode, old threads must be visited +** at every cycle, because they might point to young objects. In inc. +** mode, the thread can still be modified before the end of the cycle, +** and therefore it must be visited again in the atomic phase. To ensure +** these visits, threads must return to a gray list if they are not new +** (which can only happen in generational mode) or if the traverse is in +** the propagate phase (which can only happen in incremental mode). +*/ +static int traversethread (global_State *g, lua_State *th) { + UpVal *uv; + StkId o = th->stack; + if (isold(th) || g->gcstate == GCSpropagate) + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ + if (o == NULL) + return 1; /* stack not completely built yet */ + lua_assert(g->gcstate == GCSatomic || + th->openupval == NULL || isintwups(th)); + for (; o < th->top; o++) /* mark live elements in the stack */ + markvalue(g, s2v(o)); + for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) + markobject(g, uv); /* open upvalues cannot be collected */ + if (g->gcstate == GCSatomic) { /* final traversal? */ + StkId lim = th->stack + th->stacksize; /* real end of stack */ + for (; o < lim; o++) /* clear not-marked stack slice */ + setnilvalue(s2v(o)); + /* 'remarkupvals' may have removed thread from 'twups' list */ + if (!isintwups(th) && th->openupval != NULL) { + th->twups = g->twups; /* link it back to the list */ + g->twups = th; + } + } + else if (!g->gcemergency) + luaD_shrinkstack(th); /* do not change stack in emergency cycle */ + return 1 + th->stacksize; +} + + +/* +** traverse one gray object, turning it to black. +*/ +static lu_mem propagatemark (global_State *g) { + GCObject *o = g->gray; + nw2black(o); + g->gray = *getgclist(o); /* remove from 'gray' list */ + switch (o->tt) { + case LUA_VTABLE: return traversetable(g, gco2t(o)); + case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); + case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); + case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); + case LUA_VPROTO: return traverseproto(g, gco2p(o)); + case LUA_VTHREAD: return traversethread(g, gco2th(o)); + default: lua_assert(0); return 0; + } +} + + +static lu_mem propagateall (global_State *g) { + lu_mem tot = 0; + while (g->gray) + tot += propagatemark(g); + return tot; +} + + +/* +** Traverse all ephemeron tables propagating marks from keys to values. +** Repeat until it converges, that is, nothing new is marked. 'dir' +** inverts the direction of the traversals, trying to speed up +** convergence on chains in the same table. +** +*/ +static void convergeephemerons (global_State *g) { + int changed; + int dir = 0; + do { + GCObject *w; + GCObject *next = g->ephemeron; /* get ephemeron list */ + g->ephemeron = NULL; /* tables may return to this list when traversed */ + changed = 0; + while ((w = next) != NULL) { /* for each ephemeron table */ + Table *h = gco2t(w); + next = h->gclist; /* list is rebuilt during loop */ + nw2black(h); /* out of the list (for now) */ + if (traverseephemeron(g, h, dir)) { /* marked some value? */ + propagateall(g); /* propagate changes */ + changed = 1; /* will have to revisit all ephemeron tables */ + } + } + dir = !dir; /* invert direction next time */ + } while (changed); /* repeat until no more changes */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Sweep Functions +** ======================================================= +*/ + + +/* +** clear entries with unmarked keys from all weaktables in list 'l' +*/ +static void clearbykeys (global_State *g, GCObject *l) { + for (; l; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *limit = gnodelast(h); + Node *n; + for (n = gnode(h, 0); n < limit; n++) { + if (iscleared(g, gckeyN(n))) /* unmarked key? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ + } + } +} + + +/* +** clear entries with unmarked values from all weaktables in list 'l' up +** to element 'f' +*/ +static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { + for (; l != f; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *n, *limit = gnodelast(h); + unsigned int i; + unsigned int asize = luaH_realasize(h); + for (i = 0; i < asize; i++) { + TValue *o = &h->array[i]; + if (iscleared(g, gcvalueN(o))) /* value was collected? */ + setempty(o); /* remove entry */ + } + for (n = gnode(h, 0); n < limit; n++) { + if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ + } + } +} + + +static void freeupval (lua_State *L, UpVal *uv) { + if (upisopen(uv)) + luaF_unlinkupval(uv); + luaM_free(L, uv); +} + + +static void freeobj (lua_State *L, GCObject *o) { + switch (o->tt) { + case LUA_VPROTO: + luaF_freeproto(L, gco2p(o)); + break; + case LUA_VUPVAL: + freeupval(L, gco2upv(o)); + break; + case LUA_VLCL: + luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); + break; + case LUA_VCCL: + luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); + break; + case LUA_VTABLE: + luaH_free(L, gco2t(o)); + break; + case LUA_VTHREAD: + luaE_freethread(L, gco2th(o)); + break; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); + break; + } + case LUA_VSHRSTR: + luaS_remove(L, gco2ts(o)); /* remove it from hash table */ + luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); + break; + case LUA_VLNGSTR: + luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); + break; + default: lua_assert(0); + } +} + + +/* +** sweep at most 'countin' elements from a list of GCObjects erasing dead +** objects, where a dead object is one marked with the old (non current) +** white; change all non-dead objects back to white, preparing for next +** collection cycle. Return where to continue the traversal or NULL if +** list is finished. ('*countout' gets the number of elements traversed.) +*/ +static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, + int *countout) { + global_State *g = G(L); + int ow = otherwhite(g); + int i; + int white = luaC_white(g); /* current white */ + for (i = 0; *p != NULL && i < countin; i++) { + GCObject *curr = *p; + int marked = curr->marked; + if (isdeadm(ow, marked)) { /* is 'curr' dead? */ + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* change mark to 'white' */ + curr->marked = cast_byte((marked & ~maskgcbits) | white); + p = &curr->next; /* go to next element */ + } + } + if (countout) + *countout = i; /* number of elements traversed */ + return (*p == NULL) ? NULL : p; +} + + +/* +** sweep a list until a live object (or end of list) +*/ +static GCObject **sweeptolive (lua_State *L, GCObject **p) { + GCObject **old = p; + do { + p = sweeplist(L, p, 1, NULL); + } while (p == old); + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Finalization +** ======================================================= +*/ + +/* +** If possible, shrink string table. +*/ +static void checkSizes (lua_State *L, global_State *g) { + if (!g->gcemergency) { + if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ + l_mem olddebt = g->GCdebt; + luaS_resize(L, g->strt.size / 2); + g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ + } + } +} + + +/* +** Get the next udata to be finalized from the 'tobefnz' list, and +** link it back into the 'allgc' list. +*/ +static GCObject *udata2finalize (global_State *g) { + GCObject *o = g->tobefnz; /* get first element */ + lua_assert(tofinalize(o)); + g->tobefnz = o->next; /* remove it from 'tobefnz' list */ + o->next = g->allgc; /* return it to 'allgc' list */ + g->allgc = o; + resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ + if (issweepphase(g)) + makewhite(g, o); /* "sweep" object */ + else if (getage(o) == G_OLD1) + g->firstold1 = o; /* it is the first OLD1 object in the list */ + return o; +} + + +static void dothecall (lua_State *L, void *ud) { + UNUSED(ud); + luaD_callnoyield(L, L->top - 2, 0); +} + + +static void GCTM (lua_State *L) { + global_State *g = G(L); + const TValue *tm; + TValue v; + lua_assert(!g->gcemergency); + setgcovalue(L, &v, udata2finalize(g)); + tm = luaT_gettmbyobj(L, &v, TM_GC); + if (!notm(tm)) { /* is there a finalizer? */ + int status; + lu_byte oldah = L->allowhook; + int running = g->gcrunning; + L->allowhook = 0; /* stop debug hooks during GC metamethod */ + g->gcrunning = 0; /* avoid GC steps */ + setobj2s(L, L->top++, tm); /* push finalizer... */ + setobj2s(L, L->top++, &v); /* ... and its argument */ + L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); + L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ + L->allowhook = oldah; /* restore hooks */ + g->gcrunning = running; /* restore state */ + if (unlikely(status != LUA_OK)) { /* error while running __gc? */ + luaE_warnerror(L, "__gc metamethod"); + L->top--; /* pops error object */ + } + } +} + + +/* +** Call a few finalizers +*/ +static int runafewfinalizers (lua_State *L, int n) { + global_State *g = G(L); + int i; + for (i = 0; i < n && g->tobefnz; i++) + GCTM(L); /* call one finalizer */ + return i; +} + + +/* +** call all pending finalizers +*/ +static void callallpendingfinalizers (lua_State *L) { + global_State *g = G(L); + while (g->tobefnz) + GCTM(L); +} + + +/* +** find last 'next' field in list 'p' list (to add elements in its end) +*/ +static GCObject **findlast (GCObject **p) { + while (*p != NULL) + p = &(*p)->next; + return p; +} + + +/* +** Move all unreachable objects (or 'all' objects) that need +** finalization from list 'finobj' to list 'tobefnz' (to be finalized). +** (Note that objects after 'finobjold1' cannot be white, so they +** don't need to be traversed. In incremental mode, 'finobjold1' is NULL, +** so the whole list is traversed.) +*/ +static void separatetobefnz (global_State *g, int all) { + GCObject *curr; + GCObject **p = &g->finobj; + GCObject **lastnext = findlast(&g->tobefnz); + while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */ + lua_assert(tofinalize(curr)); + if (!(iswhite(curr) || all)) /* not being collected? */ + p = &curr->next; /* don't bother with it */ + else { + if (curr == g->finobjsur) /* removing 'finobjsur'? */ + g->finobjsur = curr->next; /* correct it */ + *p = curr->next; /* remove 'curr' from 'finobj' list */ + curr->next = *lastnext; /* link at the end of 'tobefnz' list */ + *lastnext = curr; + lastnext = &curr->next; + } + } +} + + +/* +** If pointer 'p' points to 'o', move it to the next element. +*/ +static void checkpointer (GCObject **p, GCObject *o) { + if (o == *p) + *p = o->next; +} + + +/* +** Correct pointers to objects inside 'allgc' list when +** object 'o' is being removed from the list. +*/ +static void correctpointers (global_State *g, GCObject *o) { + checkpointer(&g->survival, o); + checkpointer(&g->old1, o); + checkpointer(&g->reallyold, o); + checkpointer(&g->firstold1, o); +} + + +/* +** if object 'o' has a finalizer, remove it from 'allgc' list (must +** search the list to find it) and link it in 'finobj' list. +*/ +void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { + global_State *g = G(L); + if (tofinalize(o) || /* obj. is already marked... */ + gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ + return; /* nothing to be done */ + else { /* move 'o' to 'finobj' list */ + GCObject **p; + if (issweepphase(g)) { + makewhite(g, o); /* "sweep" object 'o' */ + if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ + g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ + } + else + correctpointers(g, o); + /* search for pointer pointing to 'o' */ + for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } + *p = o->next; /* remove 'o' from 'allgc' list */ + o->next = g->finobj; /* link it in 'finobj' list */ + g->finobj = o; + l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Generational Collector +** ======================================================= +*/ + +static void setpause (global_State *g); + + +/* +** Sweep a list of objects to enter generational mode. Deletes dead +** objects and turns the non dead to old. All non-dead threads---which +** are now old---must be in a gray list. Everything else is not in a +** gray list. Open upvalues are also kept gray. +*/ +static void sweep2old (lua_State *L, GCObject **p) { + GCObject *curr; + global_State *g = G(L); + while ((curr = *p) != NULL) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* all surviving objects become old */ + setage(curr, G_OLD); + if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ + lua_State *th = gco2th(curr); + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ + } + else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) + set2gray(curr); /* open upvalues are always gray */ + else /* everything else is black */ + nw2black(curr); + p = &curr->next; /* go to next element */ + } + } +} + + +/* +** Sweep for generational mode. Delete dead objects. (Because the +** collection is not incremental, there are no "new white" objects +** during the sweep. So, any white object must be dead.) For +** non-dead objects, advance their ages and clear the color of +** new objects. (Old objects keep their colors.) +** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced +** here, because these old-generation objects are usually not swept +** here. They will all be advanced in 'correctgraylist'. That function +** will also remove objects turned white here from any gray list. +*/ +static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, + GCObject *limit, GCObject **pfirstold1) { + static const lu_byte nextage[] = { + G_SURVIVAL, /* from G_NEW */ + G_OLD1, /* from G_SURVIVAL */ + G_OLD1, /* from G_OLD0 */ + G_OLD, /* from G_OLD1 */ + G_OLD, /* from G_OLD (do not change) */ + G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ + G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ + }; + int white = luaC_white(g); + GCObject *curr; + while ((curr = *p) != limit) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(!isold(curr) && isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* correct mark and age */ + if (getage(curr) == G_NEW) { /* new objects go back to white */ + int marked = curr->marked & ~maskgcbits; /* erase GC bits */ + curr->marked = cast_byte(marked | G_SURVIVAL | white); + } + else { /* all other objects will be old, and so keep their color */ + setage(curr, nextage[getage(curr)]); + if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) + *pfirstold1 = curr; /* first OLD1 object in the list */ + } + p = &curr->next; /* go to next element */ + } + } + return p; +} + + +/* +** Traverse a list making all its elements white and clearing their +** age. In incremental mode, all objects are 'new' all the time, +** except for fixed strings (which are always old). +*/ +static void whitelist (global_State *g, GCObject *p) { + int white = luaC_white(g); + for (; p != NULL; p = p->next) + p->marked = cast_byte((p->marked & ~maskgcbits) | white); +} + + +/* +** Correct a list of gray objects. Return pointer to where rest of the +** list should be linked. +** Because this correction is done after sweeping, young objects might +** be turned white and still be in the list. They are only removed. +** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; +** Non-white threads also remain on the list; 'TOUCHED2' objects become +** regular old; they and anything else are removed from the list. +*/ +static GCObject **correctgraylist (GCObject **p) { + GCObject *curr; + while ((curr = *p) != NULL) { + GCObject **next = getgclist(curr); + if (iswhite(curr)) + goto remove; /* remove all white objects */ + else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ + lua_assert(isgray(curr)); + nw2black(curr); /* make it black, for next barrier */ + changeage(curr, G_TOUCHED1, G_TOUCHED2); + goto remain; /* keep it in the list and go to next element */ + } + else if (curr->tt == LUA_VTHREAD) { + lua_assert(isgray(curr)); + goto remain; /* keep non-white threads on the list */ + } + else { /* everything else is removed */ + lua_assert(isold(curr)); /* young objects should be white here */ + if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */ + changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */ + nw2black(curr); /* make object black (to be removed) */ + goto remove; + } + remove: *p = *next; continue; + remain: p = next; continue; + } + return p; +} + + +/* +** Correct all gray lists, coalescing them into 'grayagain'. +*/ +static void correctgraylists (global_State *g) { + GCObject **list = correctgraylist(&g->grayagain); + *list = g->weak; g->weak = NULL; + list = correctgraylist(list); + *list = g->allweak; g->allweak = NULL; + list = correctgraylist(list); + *list = g->ephemeron; g->ephemeron = NULL; + correctgraylist(list); +} + + +/* +** Mark black 'OLD1' objects when starting a new young collection. +** Gray objects are already in some gray list, and so will be visited +** in the atomic step. +*/ +static void markold (global_State *g, GCObject *from, GCObject *to) { + GCObject *p; + for (p = from; p != to; p = p->next) { + if (getage(p) == G_OLD1) { + lua_assert(!iswhite(p)); + changeage(p, G_OLD1, G_OLD); /* now they are old */ + if (isblack(p)) + reallymarkobject(g, p); + } + } +} + + +/* +** Finish a young-generation collection. +*/ +static void finishgencycle (lua_State *L, global_State *g) { + correctgraylists(g); + checkSizes(L, g); + g->gcstate = GCSpropagate; /* skip restart */ + if (!g->gcemergency) + callallpendingfinalizers(L); +} + + +/* +** Does a young collection. First, mark 'OLD1' objects. Then does the +** atomic step. Then, sweep all lists and advance pointers. Finally, +** finish the collection. +*/ +static void youngcollection (lua_State *L, global_State *g) { + GCObject **psurvival; /* to point to first non-dead survival object */ + GCObject *dummy; /* dummy out parameter to 'sweepgen' */ + lua_assert(g->gcstate == GCSpropagate); + if (g->firstold1) { /* are there regular OLD1 objects? */ + markold(g, g->firstold1, g->reallyold); /* mark them */ + g->firstold1 = NULL; /* no more OLD1 objects (for now) */ + } + markold(g, g->finobj, g->finobjrold); + markold(g, g->tobefnz, NULL); + atomic(L); + + /* sweep nursery and get a pointer to its last live element */ + g->gcstate = GCSswpallgc; + psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->old1, &g->firstold1); + g->reallyold = g->old1; + g->old1 = *psurvival; /* 'survival' survivals are old now */ + g->survival = g->allgc; /* all news are survivals */ + + /* repeat for 'finobj' lists */ + dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ + psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->finobjold1, &dummy); + g->finobjrold = g->finobjold1; + g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ + g->finobjsur = g->finobj; /* all news are survivals */ + + sweepgen(L, g, &g->tobefnz, NULL, &dummy); + finishgencycle(L, g); +} + + +/* +** Clears all gray lists, sweeps objects, and prepare sublists to enter +** generational mode. The sweeps remove dead objects and turn all +** surviving objects to old. Threads go back to 'grayagain'; everything +** else is turned black (not in any gray list). +*/ +static void atomic2gen (lua_State *L, global_State *g) { + cleargraylists(g); + /* sweep all elements making them old */ + g->gcstate = GCSswpallgc; + sweep2old(L, &g->allgc); + /* everything alive now is old */ + g->reallyold = g->old1 = g->survival = g->allgc; + g->firstold1 = NULL; /* there are no OLD1 objects anywhere */ + + /* repeat for 'finobj' lists */ + sweep2old(L, &g->finobj); + g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj; + + sweep2old(L, &g->tobefnz); + + g->gckind = KGC_GEN; + g->lastatomic = 0; + g->GCestimate = gettotalbytes(g); /* base for memory control */ + finishgencycle(L, g); +} + + +/* +** Enter generational mode. Must go until the end of an atomic cycle +** to ensure that all objects are correctly marked and weak tables +** are cleared. Then, turn all objects into old and finishes the +** collection. +*/ +static lu_mem entergen (lua_State *L, global_State *g) { + lu_mem numobjs; + luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ + luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ + numobjs = atomic(L); /* propagates all and then do the atomic stuff */ + atomic2gen(L, g); + return numobjs; +} + + +/* +** Enter incremental mode. Turn all objects white, make all +** intermediate lists point to NULL (to avoid invalid pointers), +** and go to the pause state. +*/ +static void enterinc (global_State *g) { + whitelist(g, g->allgc); + g->reallyold = g->old1 = g->survival = NULL; + whitelist(g, g->finobj); + whitelist(g, g->tobefnz); + g->finobjrold = g->finobjold1 = g->finobjsur = NULL; + g->gcstate = GCSpause; + g->gckind = KGC_INC; + g->lastatomic = 0; +} + + +/* +** Change collector mode to 'newmode'. +*/ +void luaC_changemode (lua_State *L, int newmode) { + global_State *g = G(L); + if (newmode != g->gckind) { + if (newmode == KGC_GEN) /* entering generational mode? */ + entergen(L, g); + else + enterinc(g); /* entering incremental mode */ + } + g->lastatomic = 0; +} + + +/* +** Does a full collection in generational mode. +*/ +static lu_mem fullgen (lua_State *L, global_State *g) { + enterinc(g); + return entergen(L, g); +} + + +/* +** Set debt for the next minor collection, which will happen when +** memory grows 'genminormul'%. +*/ +static void setminordebt (global_State *g) { + luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); +} + + +/* +** Does a major collection after last collection was a "bad collection". +** +** When the program is building a big structure, it allocates lots of +** memory but generates very little garbage. In those scenarios, +** the generational mode just wastes time doing small collections, and +** major collections are frequently what we call a "bad collection", a +** collection that frees too few objects. To avoid the cost of switching +** between generational mode and the incremental mode needed for full +** (major) collections, the collector tries to stay in incremental mode +** after a bad collection, and to switch back to generational mode only +** after a "good" collection (one that traverses less than 9/8 objects +** of the previous one). +** The collector must choose whether to stay in incremental mode or to +** switch back to generational mode before sweeping. At this point, it +** does not know the real memory in use, so it cannot use memory to +** decide whether to return to generational mode. Instead, it uses the +** number of objects traversed (returned by 'atomic') as a proxy. The +** field 'g->lastatomic' keeps this count from the last collection. +** ('g->lastatomic != 0' also means that the last collection was bad.) +*/ +static void stepgenfull (lua_State *L, global_State *g) { + lu_mem newatomic; /* count of traversed objects */ + lu_mem lastatomic = g->lastatomic; /* count from last collection */ + if (g->gckind == KGC_GEN) /* still in generational mode? */ + enterinc(g); /* enter incremental mode */ + luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ + newatomic = atomic(L); /* mark everybody */ + if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ + atomic2gen(L, g); /* return to generational mode */ + setminordebt(g); + } + else { /* another bad collection; stay in incremental mode */ + g->GCestimate = gettotalbytes(g); /* first estimate */; + entersweep(L); + luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + setpause(g); + g->lastatomic = newatomic; + } +} + + +/* +** Does a generational "step". +** Usually, this means doing a minor collection and setting the debt to +** make another collection when memory grows 'genminormul'% larger. +** +** However, there are exceptions. If memory grows 'genmajormul'% +** larger than it was at the end of the last major collection (kept +** in 'g->GCestimate'), the function does a major collection. At the +** end, it checks whether the major collection was able to free a +** decent amount of memory (at least half the growth in memory since +** previous major collection). If so, the collector keeps its state, +** and the next collection will probably be minor again. Otherwise, +** we have what we call a "bad collection". In that case, set the field +** 'g->lastatomic' to signal that fact, so that the next collection will +** go to 'stepgenfull'. +** +** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; +** in that case, do a minor collection. +*/ +static void genstep (lua_State *L, global_State *g) { + if (g->lastatomic != 0) /* last collection was a bad one? */ + stepgenfull(L, g); /* do a full step */ + else { + lu_mem majorbase = g->GCestimate; /* memory after last major collection */ + lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); + if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { + lu_mem numobjs = fullgen(L, g); /* do a major collection */ + if (gettotalbytes(g) < majorbase + (majorinc / 2)) { + /* collected at least half of memory growth since last major + collection; keep doing minor collections */ + setminordebt(g); + } + else { /* bad collection */ + g->lastatomic = numobjs; /* signal that last collection was bad */ + setpause(g); /* do a long wait for next (major) collection */ + } + } + else { /* regular case; do a minor collection */ + youngcollection(L, g); + setminordebt(g); + g->GCestimate = majorbase; /* preserve base value */ + } + } + lua_assert(isdecGCmodegen(g)); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** GC control +** ======================================================= +*/ + + +/* +** Set the "time" to wait before starting a new GC cycle; cycle will +** start when memory use hits the threshold of ('estimate' * pause / +** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, +** because Lua cannot even start with less than PAUSEADJ bytes). +*/ +static void setpause (global_State *g) { + l_mem threshold, debt; + int pause = getgcparam(g->gcpause); + l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ + lua_assert(estimate > 0); + threshold = (pause < MAX_LMEM / estimate) /* overflow? */ + ? estimate * pause /* no overflow */ + : MAX_LMEM; /* overflow; truncate to maximum */ + debt = gettotalbytes(g) - threshold; + if (debt > 0) debt = 0; + luaE_setdebt(g, debt); +} + + +/* +** Enter first sweep phase. +** The call to 'sweeptolive' makes the pointer point to an object +** inside the list (instead of to the header), so that the real sweep do +** not need to skip objects created between "now" and the start of the +** real sweep. +*/ +static void entersweep (lua_State *L) { + global_State *g = G(L); + g->gcstate = GCSswpallgc; + lua_assert(g->sweepgc == NULL); + g->sweepgc = sweeptolive(L, &g->allgc); +} + + +/* +** Delete all objects in list 'p' until (but not including) object +** 'limit'. +*/ +static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { + while (p != limit) { + GCObject *next = p->next; + freeobj(L, p); + p = next; + } +} + + +/* +** Call all finalizers of the objects in the given Lua state, and +** then free all objects, except for the main thread. +*/ +void luaC_freeallobjects (lua_State *L) { + global_State *g = G(L); + luaC_changemode(L, KGC_INC); + separatetobefnz(g, 1); /* separate all objects with finalizers */ + lua_assert(g->finobj == NULL); + callallpendingfinalizers(L); + deletelist(L, g->allgc, obj2gco(g->mainthread)); + deletelist(L, g->finobj, NULL); + deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ + lua_assert(g->strt.nuse == 0); +} + + +static lu_mem atomic (lua_State *L) { + global_State *g = G(L); + lu_mem work = 0; + GCObject *origweak, *origall; + GCObject *grayagain = g->grayagain; /* save original list */ + g->grayagain = NULL; + lua_assert(g->ephemeron == NULL && g->weak == NULL); + lua_assert(!iswhite(g->mainthread)); + g->gcstate = GCSatomic; + markobject(g, L); /* mark running thread */ + /* registry and global metatables may be changed by API */ + markvalue(g, &g->l_registry); + markmt(g); /* mark global metatables */ + work += propagateall(g); /* empties 'gray' list */ + /* remark occasional upvalues of (maybe) dead threads */ + work += remarkupvals(g); + work += propagateall(g); /* propagate changes */ + g->gray = grayagain; + work += propagateall(g); /* traverse 'grayagain' list */ + convergeephemerons(g); + /* at this point, all strongly accessible objects are marked. */ + /* Clear values from weak tables, before checking finalizers */ + clearbyvalues(g, g->weak, NULL); + clearbyvalues(g, g->allweak, NULL); + origweak = g->weak; origall = g->allweak; + separatetobefnz(g, 0); /* separate objects to be finalized */ + work += markbeingfnz(g); /* mark objects that will be finalized */ + work += propagateall(g); /* remark, to propagate 'resurrection' */ + convergeephemerons(g); + /* at this point, all resurrected objects are marked. */ + /* remove dead objects from weak tables */ + clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ + clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ + /* clear values from resurrected weak tables */ + clearbyvalues(g, g->weak, origweak); + clearbyvalues(g, g->allweak, origall); + luaS_clearcache(g); + g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ + lua_assert(g->gray == NULL); + return work; /* estimate of slots marked by 'atomic' */ +} + + +static int sweepstep (lua_State *L, global_State *g, + int nextstate, GCObject **nextlist) { + if (g->sweepgc) { + l_mem olddebt = g->GCdebt; + int count; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); + g->GCestimate += g->GCdebt - olddebt; /* update estimate */ + return count; + } + else { /* enter next state */ + g->gcstate = nextstate; + g->sweepgc = nextlist; + return 0; /* no work done */ + } +} + + +static lu_mem singlestep (lua_State *L) { + global_State *g = G(L); + switch (g->gcstate) { + case GCSpause: { + restartcollection(g); + g->gcstate = GCSpropagate; + return 1; + } + case GCSpropagate: { + if (g->gray == NULL) { /* no more gray objects? */ + g->gcstate = GCSenteratomic; /* finish propagate phase */ + return 0; + } + else + return propagatemark(g); /* traverse one gray object */ + } + case GCSenteratomic: { + lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */ + entersweep(L); + g->GCestimate = gettotalbytes(g); /* first estimate */; + return work; + } + case GCSswpallgc: { /* sweep "regular" objects */ + return sweepstep(L, g, GCSswpfinobj, &g->finobj); + } + case GCSswpfinobj: { /* sweep objects with finalizers */ + return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + } + case GCSswptobefnz: { /* sweep objects to be finalized */ + return sweepstep(L, g, GCSswpend, NULL); + } + case GCSswpend: { /* finish sweeps */ + checkSizes(L, g); + g->gcstate = GCScallfin; + return 0; + } + case GCScallfin: { /* call remaining finalizers */ + if (g->tobefnz && !g->gcemergency) { + int n = runafewfinalizers(L, GCFINMAX); + return n * GCFINALIZECOST; + } + else { /* emergency mode or no more finalizers */ + g->gcstate = GCSpause; /* finish collection */ + return 0; + } + } + default: lua_assert(0); return 0; + } +} + + +/* +** advances the garbage collector until it reaches a state allowed +** by 'statemask' +*/ +void luaC_runtilstate (lua_State *L, int statesmask) { + global_State *g = G(L); + while (!testbit(statesmask, g->gcstate)) + singlestep(L); +} + + +/* +** Performs a basic incremental step. The debt and step size are +** converted from bytes to "units of work"; then the function loops +** running single steps until adding that many units of work or +** finishing a cycle (pause state). Finally, it sets the debt that +** controls when next step will be performed. +*/ +static void incstep (lua_State *L, global_State *g) { + int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ + l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; + l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) + ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul + : MAX_LMEM; /* overflow; keep maximum value */ + do { /* repeat until pause or enough "credit" (negative debt) */ + lu_mem work = singlestep(L); /* perform one single step */ + debt -= work; + } while (debt > -stepsize && g->gcstate != GCSpause); + if (g->gcstate == GCSpause) + setpause(g); /* pause until next cycle */ + else { + debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ + luaE_setdebt(g, debt); + } +} + +/* +** performs a basic GC step if collector is running +*/ +void luaC_step (lua_State *L) { + global_State *g = G(L); + lua_assert(!g->gcemergency); + if (g->gcrunning) { /* running? */ + if(isdecGCmodegen(g)) + genstep(L, g); + else + incstep(L, g); + } +} + + +/* +** Perform a full collection in incremental mode. +** Before running the collection, check 'keepinvariant'; if it is true, +** there may be some objects marked as black, so the collector has +** to sweep all objects to turn them back to white (as white has not +** changed, nothing will be collected). +*/ +static void fullinc (lua_State *L, global_State *g) { + if (keepinvariant(g)) /* black objects? */ + entersweep(L); /* sweep everything to turn them back to white */ + /* finish any pending sweep phase to start a new cycle */ + luaC_runtilstate(L, bitmask(GCSpause)); + luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ + /* estimate must be correct after a full GC cycle */ + lua_assert(g->GCestimate == gettotalbytes(g)); + luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + setpause(g); +} + + +/* +** Performs a full GC cycle; if 'isemergency', set a flag to avoid +** some operations which could change the interpreter state in some +** unexpected ways (running finalizers and shrinking some structures). +*/ +void luaC_fullgc (lua_State *L, int isemergency) { + global_State *g = G(L); + lua_assert(!g->gcemergency); + g->gcemergency = isemergency; /* set flag */ + if (g->gckind == KGC_INC) + fullinc(L, g); + else + fullgen(L, g); + g->gcemergency = 0; +} + +/* }====================================================== */ + + diff --git a/Lua/lgc.h b/Lua/lgc.h new file mode 100644 index 00000000..073e2a40 --- /dev/null +++ b/Lua/lgc.h @@ -0,0 +1,189 @@ +/* +** $Id: lgc.h $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +#include "lobject.h" +#include "lstate.h" + +/* +** Collectable objects may have one of three colors: white, which means +** the object is not marked; gray, which means the object is marked, but +** its references may be not marked; and black, which means that the +** object and all its references are marked. The main invariant of the +** garbage collector, while marking objects, is that a black object can +** never point to a white one. Moreover, any gray object must be in a +** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it +** can be visited again before finishing the collection cycle. (Open +** upvalues are an exception to this rule.) These lists have no meaning +** when the invariant is not being enforced (e.g., sweep phase). +*/ + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpropagate 0 +#define GCSenteratomic 1 +#define GCSatomic 2 +#define GCSswpallgc 3 +#define GCSswpfinobj 4 +#define GCSswptobefnz 5 +#define GCSswpend 6 +#define GCScallfin 7 +#define GCSpause 8 + + +#define issweepphase(g) \ + (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) + + +/* +** macro to tell when main invariant (white objects cannot point to black +** ones) must be kept. During a collection, the sweep +** phase may break the invariant, as objects turned white may point to +** still-black objects. The invariant is restored when sweep ends and +** all objects are white again. +*/ + +#define keepinvariant(g) ((g)->gcstate <= GCSatomic) + + +/* +** some useful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast_byte(~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) + + +/* +** Layout for bit use in 'marked' field. First three bits are +** used for object "age" in generational mode. Last bit is used +** by tests. +*/ +#define WHITE0BIT 3 /* object is white (type 0) */ +#define WHITE1BIT 4 /* object is white (type 1) */ +#define BLACKBIT 5 /* object is black */ +#define FINALIZEDBIT 6 /* object has been marked for finalization */ + +#define TESTBIT 7 + + + +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) testbits((x)->marked, WHITEBITS) +#define isblack(x) testbit((x)->marked, BLACKBIT) +#define isgray(x) /* neither white nor black */ \ + (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) + +#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) + +#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) +#define isdeadm(ow,m) ((m) & (ow)) +#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) + +#define changewhite(x) ((x)->marked ^= WHITEBITS) +#define nw2black(x) \ + check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT)) + +#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS) + + +/* object age in generational mode */ +#define G_NEW 0 /* created in current cycle */ +#define G_SURVIVAL 1 /* created in previous cycle */ +#define G_OLD0 2 /* marked old by frw. barrier in this cycle */ +#define G_OLD1 3 /* first full cycle as old */ +#define G_OLD 4 /* really old object (not to be visited) */ +#define G_TOUCHED1 5 /* old object touched this cycle */ +#define G_TOUCHED2 6 /* old object touched in previous cycle */ + +#define AGEBITS 7 /* all age bits (111) */ + +#define getage(o) ((o)->marked & AGEBITS) +#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a)) +#define isold(o) (getage(o) > G_SURVIVAL) + +#define changeage(o,f,t) \ + check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) + + +/* Default Values for GC parameters */ +#define LUAI_GENMAJORMUL 100 +#define LUAI_GENMINORMUL 20 + +/* wait memory to double before starting new cycle */ +#define LUAI_GCPAUSE 200 + +/* +** some gc parameters are stored divided by 4 to allow a maximum value +** up to 1023 in a 'lu_byte'. +*/ +#define getgcparam(p) ((p) * 4) +#define setgcparam(p,v) ((p) = (v) / 4) + +#define LUAI_GCMUL 100 + +/* how much to allocate before next GC step (log2) */ +#define LUAI_GCSTEPSIZE 13 /* 8 KB */ + + +/* +** Check whether the declared GC mode is generational. While in +** generational mode, the collector can go temporarily to incremental +** mode to improve performance. This is signaled by 'g->lastatomic != 0'. +*/ +#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) + +/* +** Does one step of collection when debt becomes positive. 'pre'/'pos' +** allows some adjustments to be done only when needed. macro +** 'condchangemem' is used only for heavy tests (forcing a full +** GC cycle on every opportunity) +*/ +#define luaC_condGC(L,pre,pos) \ + { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ + condchangemem(L,pre,pos); } + +/* more often than not, 'pre'/'pos' are empty */ +#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) + + +#define luaC_barrier(L,p,v) ( \ + (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ + luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) + +#define luaC_barrierback(L,p,v) ( \ + (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ + luaC_barrierback_(L,p) : cast_void(0)) + +#define luaC_objbarrier(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? \ + luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) + +LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_freeallobjects (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); +LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); +LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); +LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); +LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); + + +#endif diff --git a/Lua/linit.c b/Lua/linit.c new file mode 100644 index 00000000..69808f84 --- /dev/null +++ b/Lua/linit.c @@ -0,0 +1,65 @@ +/* +** $Id: linit.c $ +** Initialization of libraries for lua.c and other clients +** See Copyright Notice in lua.h +*/ + + +#define linit_c +#define LUA_LIB + +/* +** If you embed Lua in your program and need to open the standard +** libraries, call luaL_openlibs in your program. If you need a +** different set of libraries, copy this file to your project and edit +** it to suit your needs. +** +** You can also *preload* libraries, so that a later 'require' can +** open the library, which is already linked to the application. +** For that, do the following code: +** +** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); +** lua_pushcfunction(L, luaopen_modname); +** lua_setfield(L, -2, modname); +** lua_pop(L, 1); // remove PRELOAD table +*/ + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "lualib.h" +#include "lauxlib.h" + + +/* +** these libs are loaded by lua.c and are readily available to any Lua +** program +*/ +static const luaL_Reg loadedlibs[] = { + {LUA_GNAME, luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_COLIBNAME, luaopen_coroutine}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_UTF8LIBNAME, luaopen_utf8}, + {LUA_DBLIBNAME, luaopen_debug}, + {NULL, NULL} +}; + + +LUALIB_API void luaL_openlibs (lua_State *L) { + const luaL_Reg *lib; + /* "require" functions from 'loadedlibs' and set results to global table */ + for (lib = loadedlibs; lib->func; lib++) { + luaL_requiref(L, lib->name, lib->func, 1); + lua_pop(L, 1); /* remove lib */ + } +} + diff --git a/Lua/liolib.c b/Lua/liolib.c new file mode 100644 index 00000000..60ab1bfa --- /dev/null +++ b/Lua/liolib.c @@ -0,0 +1,821 @@ +/* +** $Id: liolib.c $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + +#define liolib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + + +/* +** Change this macro to accept other modes for 'fopen' besides +** the standard ones. +*/ +#if !defined(l_checkmode) + +/* accepted extensions to 'mode' in 'fopen' */ +#if !defined(L_MODEEXT) +#define L_MODEEXT "b" +#endif + +/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ +static int l_checkmode (const char *mode) { + return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && + (*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */ + (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ +} + +#endif + +/* +** {====================================================== +** l_popen spawns a new process connected to the current +** one through the file streams. +** ======================================================= +*/ + +#if !defined(l_checkmodep) +/* By default, Lua accepts only "r" or "w" as mode */ +#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0') +#endif + + +#if !defined(l_popen) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#define l_popen(L,c,m) (fflush(NULL), popen(c,m)) +#define l_pclose(L,file) (pclose(file)) + +#elif defined(LUA_USE_WINDOWS) /* }{ */ + +#define l_popen(L,c,m) (_popen(c,m)) +#define l_pclose(L,file) (_pclose(file)) + +#else /* }{ */ + +/* ISO C definitions */ +#define l_popen(L,c,m) \ + ((void)c, (void)m, \ + luaL_error(L, "'popen' not supported"), \ + (FILE*)0) +#define l_pclose(L,file) ((void)L, (void)file, -1) + +#endif /* } */ + +#endif /* } */ + +/* }====================================================== */ + + +#if !defined(l_getc) /* { */ + +#if defined(LUA_USE_POSIX) +#define l_getc(f) getc_unlocked(f) +#define l_lockfile(f) flockfile(f) +#define l_unlockfile(f) funlockfile(f) +#else +#define l_getc(f) getc(f) +#define l_lockfile(f) ((void)0) +#define l_unlockfile(f) ((void)0) +#endif + +#endif /* } */ + + +/* +** {====================================================== +** l_fseek: configuration for longer offsets +** ======================================================= +*/ + +#if !defined(l_fseek) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include + +#define l_fseek(f,o,w) fseeko(f,o,w) +#define l_ftell(f) ftello(f) +#define l_seeknum off_t + +#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ + && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ + +/* Windows (but not DDK) and Visual C++ 2005 or higher */ +#define l_fseek(f,o,w) _fseeki64(f,o,w) +#define l_ftell(f) _ftelli64(f) +#define l_seeknum __int64 + +#else /* }{ */ + +/* ISO C definitions */ +#define l_fseek(f,o,w) fseek(f,o,w) +#define l_ftell(f) ftell(f) +#define l_seeknum long + +#endif /* } */ + +#endif /* } */ + +/* }====================================================== */ + + + +#define IO_PREFIX "_IO_" +#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) +#define IO_INPUT (IO_PREFIX "input") +#define IO_OUTPUT (IO_PREFIX "output") + + +typedef luaL_Stream LStream; + + +#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + +#define isclosed(p) ((p)->closef == NULL) + + +static int io_type (lua_State *L) { + LStream *p; + luaL_checkany(L, 1); + p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); + if (p == NULL) + luaL_pushfail(L); /* not a file */ + else if (isclosed(p)) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static int f_tostring (lua_State *L) { + LStream *p = tolstream(L); + if (isclosed(p)) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", p->f); + return 1; +} + + +static FILE *tofile (lua_State *L) { + LStream *p = tolstream(L); + if (isclosed(p)) + luaL_error(L, "attempt to use a closed file"); + lua_assert(p->f); + return p->f; +} + + +/* +** When creating file handles, always creates a 'closed' file handle +** before opening the actual file; so, if there is a memory error, the +** handle is in a consistent state. +*/ +static LStream *newprefile (lua_State *L) { + LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0); + p->closef = NULL; /* mark file handle as 'closed' */ + luaL_setmetatable(L, LUA_FILEHANDLE); + return p; +} + + +/* +** Calls the 'close' function from a file handle. The 'volatile' avoids +** a bug in some versions of the Clang compiler (e.g., clang 3.0 for +** 32 bits). +*/ +static int aux_close (lua_State *L) { + LStream *p = tolstream(L); + volatile lua_CFunction cf = p->closef; + p->closef = NULL; /* mark stream as closed */ + return (*cf)(L); /* close it */ +} + + +static int f_close (lua_State *L) { + tofile(L); /* make sure argument is an open stream */ + return aux_close(L); +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) /* no argument? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */ + return f_close(L); +} + + +static int f_gc (lua_State *L) { + LStream *p = tolstream(L); + if (!isclosed(p) && p->f != NULL) + aux_close(L); /* ignore closed and incompletely open files */ + return 0; +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + LStream *p = tolstream(L); + int res = fclose(p->f); + return luaL_fileresult(L, (res == 0), NULL); +} + + +static LStream *newfile (lua_State *L) { + LStream *p = newprefile(L); + p->f = NULL; + p->closef = &io_fclose; + return p; +} + + +static void opencheck (lua_State *L, const char *fname, const char *mode) { + LStream *p = newfile(L); + p->f = fopen(fname, mode); + if (p->f == NULL) + luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + LStream *p = newfile(L); + const char *md = mode; /* to traverse/check mode */ + luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); + p->f = fopen(filename, mode); + return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; +} + + +/* +** function to close 'popen' files +*/ +static int io_pclose (lua_State *L) { + LStream *p = tolstream(L); + errno = 0; + return luaL_execresult(L, l_pclose(L, p->f)); +} + + +static int io_popen (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + LStream *p = newprefile(L); + luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); + p->f = l_popen(L, filename, mode); + p->closef = &io_pclose; + return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; +} + + +static int io_tmpfile (lua_State *L) { + LStream *p = newfile(L); + p->f = tmpfile(); + return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, const char *findex) { + LStream *p; + lua_getfield(L, LUA_REGISTRYINDEX, findex); + p = (LStream *)lua_touserdata(L, -1); + if (isclosed(p)) + luaL_error(L, "default %s file is closed", findex + IOPREF_LEN); + return p->f; +} + + +static int g_iofile (lua_State *L, const char *f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) + opencheck(L, filename, mode); + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_setfield(L, LUA_REGISTRYINDEX, f); + } + /* return current value */ + lua_getfield(L, LUA_REGISTRYINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +/* +** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit +** in the limit for upvalues of a closure) +*/ +#define MAXARGLINE 250 + +/* +** Auxiliary function to create the iteration function for 'lines'. +** The iteration function is a closure over 'io_readline', with +** the following upvalues: +** 1) The file being read (first value in the stack) +** 2) the number of arguments to read +** 3) a boolean, true iff file has to be closed when finished ('toclose') +** *) a variable number of format arguments (rest of the stack) +*/ +static void aux_lines (lua_State *L, int toclose) { + int n = lua_gettop(L) - 1; /* number of arguments to read */ + luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); + lua_pushvalue(L, 1); /* file */ + lua_pushinteger(L, n); /* number of arguments to read */ + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_rotate(L, 2, 3); /* move the three values to their positions */ + lua_pushcclosure(L, io_readline, 3 + n); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 0); + return 1; +} + + +/* +** Return an iteration function for 'io.lines'. If file has to be +** closed, also returns the file itself as a second result (to be +** closed as the state at the exit of a generic for). +*/ +static int io_lines (lua_State *L) { + int toclose; + if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ + if (lua_isnil(L, 1)) { /* no file name? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ + lua_replace(L, 1); /* put it at index 1 */ + tofile(L); /* check that it's a valid file handle */ + toclose = 0; /* do not close it after iteration */ + } + else { /* open a new file */ + const char *filename = luaL_checkstring(L, 1); + opencheck(L, filename, "r"); + lua_replace(L, 1); /* put file at index 1 */ + toclose = 1; /* close it after iteration */ + } + aux_lines(L, toclose); /* push iteration function */ + if (toclose) { + lua_pushnil(L); /* state */ + lua_pushnil(L); /* control */ + lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */ + return 4; + } + else + return 1; +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +/* maximum length of a numeral */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + + +/* auxiliary structure used by 'read_number' */ +typedef struct { + FILE *f; /* file being read */ + int c; /* current character (look ahead) */ + int n; /* number of elements in buffer 'buff' */ + char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ +} RN; + + +/* +** Add current char to buffer (if not out of space) and read next one +*/ +static int nextc (RN *rn) { + if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */ + rn->buff[0] = '\0'; /* invalidate result */ + return 0; /* fail */ + } + else { + rn->buff[rn->n++] = rn->c; /* save current char */ + rn->c = l_getc(rn->f); /* read next one */ + return 1; + } +} + + +/* +** Accept current char if it is in 'set' (of size 2) +*/ +static int test2 (RN *rn, const char *set) { + if (rn->c == set[0] || rn->c == set[1]) + return nextc(rn); + else return 0; +} + + +/* +** Read a sequence of (hex)digits +*/ +static int readdigits (RN *rn, int hex) { + int count = 0; + while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) + count++; + return count; +} + + +/* +** Read a number: first reads a valid prefix of a numeral into a buffer. +** Then it calls 'lua_stringtonumber' to check whether the format is +** correct and to convert it to a Lua number. +*/ +static int read_number (lua_State *L, FILE *f) { + RN rn; + int count = 0; + int hex = 0; + char decp[2]; + rn.f = f; rn.n = 0; + decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ + decp[1] = '.'; /* always accept a dot */ + l_lockfile(rn.f); + do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ + test2(&rn, "-+"); /* optional sign */ + if (test2(&rn, "00")) { + if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ + else count = 1; /* count initial '0' as a valid digit */ + } + count += readdigits(&rn, hex); /* integral part */ + if (test2(&rn, decp)) /* decimal point? */ + count += readdigits(&rn, hex); /* fractional part */ + if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ + test2(&rn, "-+"); /* exponent sign */ + readdigits(&rn, 0); /* exponent digits */ + } + ungetc(rn.c, rn.f); /* unread look-ahead char */ + l_unlockfile(rn.f); + rn.buff[rn.n] = '\0'; /* finish string */ + if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */ + return 1; /* ok */ + else { /* invalid format */ + lua_pushnil(L); /* "result" to be removed */ + return 0; /* read fails */ + } +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); /* no-op when c == EOF */ + lua_pushliteral(L, ""); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f, int chop) { + luaL_Buffer b; + int c; + luaL_buffinit(L, &b); + do { /* may need to read several chunks to get whole line */ + char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */ + int i = 0; + l_lockfile(f); /* no memory errors can happen inside the lock */ + while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') + buff[i++] = c; /* read up to end of line or buffer limit */ + l_unlockfile(f); + luaL_addsize(&b, i); + } while (c != EOF && c != '\n'); /* repeat until end of line */ + if (!chop && c == '\n') /* want a newline and have one? */ + luaL_addchar(&b, c); /* add ending newline to result */ + luaL_pushresult(&b); /* close buffer */ + /* return ok if read something (either a newline or something else) */ + return (c == '\n' || lua_rawlen(L, -1) > 0); +} + + +static void read_all (lua_State *L, FILE *f) { + size_t nr; + luaL_Buffer b; + luaL_buffinit(L, &b); + do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ + char *p = luaL_prepbuffer(&b); + nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); + luaL_addsize(&b, nr); + } while (nr == LUAL_BUFFERSIZE); + luaL_pushresult(&b); /* close buffer */ +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t nr; /* number of chars actually read */ + char *p; + luaL_Buffer b; + luaL_buffinit(L, &b); + p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ + nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ + luaL_addsize(&b, nr); + luaL_pushresult(&b); /* close buffer */ + return (nr > 0); /* true iff read something */ +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int n, success; + clearerr(f); + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f, 1); + n = first + 1; /* to return 1 result */ + } + else { + /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)luaL_checkinteger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = luaL_checkstring(L, n); + if (*p == '*') p++; /* skip optional '*' (for compatibility) */ + switch (*p) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f, 1); + break; + case 'L': /* line with end-of-line */ + success = read_line(L, f, 0); + break; + case 'a': /* file */ + read_all(L, f); /* read entire file */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (ferror(f)) + return luaL_fileresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + luaL_pushfail(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +/* +** Iteration function for 'lines'. +*/ +static int io_readline (lua_State *L) { + LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); + int i; + int n = (int)lua_tointeger(L, lua_upvalueindex(2)); + if (isclosed(p)) /* file is already closed? */ + return luaL_error(L, "file is already closed"); + lua_settop(L , 1); + luaL_checkstack(L, n, "too many arguments"); + for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ + lua_pushvalue(L, lua_upvalueindex(3 + i)); + n = g_read(L, p->f, 2); /* 'n' is number of results */ + lua_assert(n > 0); /* should return at least a nil */ + if (lua_toboolean(L, -n)) /* read at least one value? */ + return n; /* return them */ + else { /* first result is false: EOF or error */ + if (n > 1) { /* is there error information? */ + /* 2nd result is error message */ + return luaL_error(L, "%s", lua_tostring(L, -n + 1)); + } + if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ + lua_settop(L, 0); /* clear stack */ + lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */ + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - arg; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + int len = lua_isinteger(L, arg) + ? fprintf(f, LUA_INTEGER_FMT, + (LUAI_UACINT)lua_tointeger(L, arg)) + : fprintf(f, LUA_NUMBER_FMT, + (LUAI_UACNUMBER)lua_tonumber(L, arg)); + status = status && (len > 0); + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + if (status) return 1; /* file handle already on stack top */ + else return luaL_fileresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + FILE *f = tofile(L); + lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ + return g_write(L, f, 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + lua_Integer p3 = luaL_optinteger(L, 3, 0); + l_seeknum offset = (l_seeknum)p3; + luaL_argcheck(L, (lua_Integer)offset == p3, 3, + "not an integer in proper range"); + op = l_fseek(f, offset, mode[op]); + if (op) + return luaL_fileresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, (lua_Integer)l_ftell(f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res = setvbuf(f, NULL, mode[op], (size_t)sz); + return luaL_fileresult(L, res == 0, NULL); +} + + + +static int io_flush (lua_State *L) { + return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); +} + + +/* +** functions for 'io' library +*/ +static const luaL_Reg iolib[] = { + {"close", io_close}, + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +/* +** methods for file handles +*/ +static const luaL_Reg meth[] = { + {"read", f_read}, + {"write", f_write}, + {"lines", f_lines}, + {"flush", f_flush}, + {"seek", f_seek}, + {"close", f_close}, + {"setvbuf", f_setvbuf}, + {NULL, NULL} +}; + + +/* +** metamethods for file handles +*/ +static const luaL_Reg metameth[] = { + {"__index", NULL}, /* place holder */ + {"__gc", f_gc}, + {"__close", f_gc}, + {"__tostring", f_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */ + luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */ + luaL_newlibtable(L, meth); /* create method table */ + luaL_setfuncs(L, meth, 0); /* add file methods to method table */ + lua_setfield(L, -2, "__index"); /* metatable.__index = method table */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + LStream *p = tolstream(L); + p->closef = &io_noclose; /* keep file opened */ + luaL_pushfail(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +static void createstdfile (lua_State *L, FILE *f, const char *k, + const char *fname) { + LStream *p = newprefile(L); + p->f = f; + p->closef = &io_noclose; + if (k != NULL) { + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ + } + lua_setfield(L, -2, fname); /* add file to module */ +} + + +LUAMOD_API int luaopen_io (lua_State *L) { + luaL_newlib(L, iolib); /* new module */ + createmeta(L); + /* create (and set) default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, NULL, "stderr"); + return 1; +} + diff --git a/Lua/ljumptab.h b/Lua/ljumptab.h new file mode 100644 index 00000000..8306f250 --- /dev/null +++ b/Lua/ljumptab.h @@ -0,0 +1,112 @@ +/* +** $Id: ljumptab.h $ +** Jump Table for the Lua interpreter +** See Copyright Notice in lua.h +*/ + + +#undef vmdispatch +#undef vmcase +#undef vmbreak + +#define vmdispatch(x) goto *disptab[x]; + +#define vmcase(l) L_##l: + +#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i)); + + +static const void *const disptab[NUM_OPCODES] = { + +#if 0 +** you can update the following list with this command: +** +** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h +** +#endif + +&&L_OP_MOVE, +&&L_OP_LOADI, +&&L_OP_LOADF, +&&L_OP_LOADK, +&&L_OP_LOADKX, +&&L_OP_LOADFALSE, +&&L_OP_LFALSESKIP, +&&L_OP_LOADTRUE, +&&L_OP_LOADNIL, +&&L_OP_GETUPVAL, +&&L_OP_SETUPVAL, +&&L_OP_GETTABUP, +&&L_OP_GETTABLE, +&&L_OP_GETI, +&&L_OP_GETFIELD, +&&L_OP_SETTABUP, +&&L_OP_SETTABLE, +&&L_OP_SETI, +&&L_OP_SETFIELD, +&&L_OP_NEWTABLE, +&&L_OP_SELF, +&&L_OP_ADDI, +&&L_OP_ADDK, +&&L_OP_SUBK, +&&L_OP_MULK, +&&L_OP_MODK, +&&L_OP_POWK, +&&L_OP_DIVK, +&&L_OP_IDIVK, +&&L_OP_BANDK, +&&L_OP_BORK, +&&L_OP_BXORK, +&&L_OP_SHRI, +&&L_OP_SHLI, +&&L_OP_ADD, +&&L_OP_SUB, +&&L_OP_MUL, +&&L_OP_MOD, +&&L_OP_POW, +&&L_OP_DIV, +&&L_OP_IDIV, +&&L_OP_BAND, +&&L_OP_BOR, +&&L_OP_BXOR, +&&L_OP_SHL, +&&L_OP_SHR, +&&L_OP_MMBIN, +&&L_OP_MMBINI, +&&L_OP_MMBINK, +&&L_OP_UNM, +&&L_OP_BNOT, +&&L_OP_NOT, +&&L_OP_LEN, +&&L_OP_CONCAT, +&&L_OP_CLOSE, +&&L_OP_TBC, +&&L_OP_JMP, +&&L_OP_EQ, +&&L_OP_LT, +&&L_OP_LE, +&&L_OP_EQK, +&&L_OP_EQI, +&&L_OP_LTI, +&&L_OP_LEI, +&&L_OP_GTI, +&&L_OP_GEI, +&&L_OP_TEST, +&&L_OP_TESTSET, +&&L_OP_CALL, +&&L_OP_TAILCALL, +&&L_OP_RETURN, +&&L_OP_RETURN0, +&&L_OP_RETURN1, +&&L_OP_FORLOOP, +&&L_OP_FORPREP, +&&L_OP_TFORPREP, +&&L_OP_TFORCALL, +&&L_OP_TFORLOOP, +&&L_OP_SETLIST, +&&L_OP_CLOSURE, +&&L_OP_VARARG, +&&L_OP_VARARGPREP, +&&L_OP_EXTRAARG + +}; diff --git a/Lua/llex.c b/Lua/llex.c new file mode 100644 index 00000000..3d6b2b97 --- /dev/null +++ b/Lua/llex.c @@ -0,0 +1,577 @@ +/* +** $Id: llex.c $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#define llex_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include + +#include "lua.h" + +#include "lctype.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lobject.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lzio.h" + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +static const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "goto", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "//", "..", "...", "==", ">=", "<=", "~=", + "<<", ">>", "::", "", + "", "", "", "" +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static l_noret lexerror (LexState *ls, const char *msg, int token); + + +static void save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { + size_t newsize; + if (luaZ_sizebuffer(b) >= MAX_SIZE/2) + lexerror(ls, "lexical element too long", 0); + newsize = luaZ_sizebuffer(b) * 2; + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[luaZ_bufflen(b)++] = cast_char(c); +} + + +void luaX_init (lua_State *L) { + int i; + TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */ + luaC_fix(L, obj2gco(e)); /* never collect this name */ + for (i=0; iextra = cast_byte(i+1); /* reserved word */ + } +} + + +const char *luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { /* single-byte symbols? */ + if (lisprint(token)) + return luaO_pushfstring(ls->L, "'%c'", token); + else /* control character */ + return luaO_pushfstring(ls->L, "'<\\%d>'", token); + } + else { + const char *s = luaX_tokens[token - FIRST_RESERVED]; + if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ + return luaO_pushfstring(ls->L, "'%s'", s); + else /* names, strings, and numerals */ + return s; + } +} + + +static const char *txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: case TK_STRING: + case TK_FLT: case TK_INT: + save(ls, '\0'); + return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); + default: + return luaX_token2str(ls, token); + } +} + + +static l_noret lexerror (LexState *ls, const char *msg, int token) { + msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber); + if (token) + luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +l_noret luaX_syntaxerror (LexState *ls, const char *msg) { + lexerror(ls, msg, ls->t.token); +} + + +/* +** creates a new string and anchors it in scanner's table so that +** it will not be collected until the end of the compilation +** (by that time it should be anchored somewhere) +*/ +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + lua_State *L = ls->L; + TValue *o; /* entry for 'str' */ + TString *ts = luaS_newlstr(L, str, l); /* create new string */ + setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */ + o = luaH_set(L, ls->h, s2v(L->top - 1)); + if (isempty(o)) { /* not in use yet? */ + /* boolean value does not need GC barrier; + table is not a metatable, so it does not need to invalidate cache */ + setbtvalue(o); /* t[string] = true */ + luaC_checkGC(L); + } + else { /* string already present */ + ts = keystrval(nodefromval(o)); /* re-use value previously stored */ + } + L->top--; /* remove string from stack */ + return ts; +} + + +/* +** increment line number and skips newline sequence (any of +** \n, \r, \n\r, or \r\n) +*/ +static void inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip '\n' or '\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip '\n\r' or '\r\n' */ + if (++ls->linenumber >= MAX_INT) + lexerror(ls, "chunk has too many lines", 0); +} + + +void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, + int firstchar) { + ls->t.token = 0; + ls->L = L; + ls->current = firstchar; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + +static int check_next1 (LexState *ls, int c) { + if (ls->current == c) { + next(ls); + return 1; + } + else return 0; +} + + +/* +** Check whether current char is in set 'set' (with two chars) and +** saves it +*/ +static int check_next2 (LexState *ls, const char *set) { + lua_assert(set[2] == '\0'); + if (ls->current == set[0] || ls->current == set[1]) { + save_and_next(ls); + return 1; + } + else return 0; +} + + +/* LUA_NUMBER */ +/* +** This function is quite liberal in what it accepts, as 'luaO_str2num' +** will reject ill-formed numerals. Roughly, it accepts the following +** pattern: +** +** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))* +** +** The only tricky part is to accept [+-] only after a valid exponent +** mark, to avoid reading '3-4' or '0xe+1' as a single number. +** +** The caller might have already read an initial dot. +*/ +static int read_numeral (LexState *ls, SemInfo *seminfo) { + TValue obj; + const char *expo = "Ee"; + int first = ls->current; + lua_assert(lisdigit(ls->current)); + save_and_next(ls); + if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */ + expo = "Pp"; + for (;;) { + if (check_next2(ls, expo)) /* exponent mark? */ + check_next2(ls, "-+"); /* optional exponent sign */ + else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */ + save_and_next(ls); + else break; + } + if (lislalpha(ls->current)) /* is numeral touching a letter? */ + save_and_next(ls); /* force an error */ + save(ls, '\0'); + if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ + lexerror(ls, "malformed number", TK_FLT); + if (ttisinteger(&obj)) { + seminfo->i = ivalue(&obj); + return TK_INT; + } + else { + lua_assert(ttisfloat(&obj)); + seminfo->r = fltvalue(&obj); + return TK_FLT; + } +} + + +/* +** reads a sequence '[=*[' or ']=*]', leaving the last bracket. +** If sequence is well formed, return its number of '='s + 2; otherwise, +** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...'). +*/ +static size_t skip_sep (LexState *ls) { + size_t count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count + 2 + : (count == 0) ? 1 + : 0; +} + + +static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) { + int line = ls->linenumber; /* initial line (for error message) */ + save_and_next(ls); /* skip 2nd '[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: { /* error */ + const char *what = (seminfo ? "string" : "comment"); + const char *msg = luaO_pushfstring(ls->L, + "unfinished long %s (starting at line %d)", what, line); + lexerror(ls, msg, TK_EOS); + break; /* to avoid warnings */ + } + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd ']' */ + goto endloop; + } + break; + } + case '\n': case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep, + luaZ_bufflen(ls->buff) - 2 * sep); +} + + +static void esccheck (LexState *ls, int c, const char *msg) { + if (!c) { + if (ls->current != EOZ) + save_and_next(ls); /* add current to buffer for error message */ + lexerror(ls, msg, TK_STRING); + } +} + + +static int gethexa (LexState *ls) { + save_and_next(ls); + esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected"); + return luaO_hexavalue(ls->current); +} + + +static int readhexaesc (LexState *ls) { + int r = gethexa(ls); + r = (r << 4) + gethexa(ls); + luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */ + return r; +} + + +static unsigned long readutf8esc (LexState *ls) { + unsigned long r; + int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ + save_and_next(ls); /* skip 'u' */ + esccheck(ls, ls->current == '{', "missing '{'"); + r = gethexa(ls); /* must have at least one digit */ + while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) { + i++; + esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large"); + r = (r << 4) + luaO_hexavalue(ls->current); + } + esccheck(ls, ls->current == '}', "missing '}'"); + next(ls); /* skip '}' */ + luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */ + return r; +} + + +static void utf8esc (LexState *ls) { + char buff[UTF8BUFFSZ]; + int n = luaO_utf8esc(buff, readutf8esc(ls)); + for (; n > 0; n--) /* add 'buff' to string */ + save(ls, buff[UTF8BUFFSZ - n]); +} + + +static int readdecesc (LexState *ls) { + int i; + int r = 0; /* result accumulator */ + for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ + r = 10*r + ls->current - '0'; + save_and_next(ls); + } + esccheck(ls, r <= UCHAR_MAX, "decimal escape too large"); + luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */ + return r; +} + + +static void read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); /* keep delimiter (for error messages) */ + while (ls->current != del) { + switch (ls->current) { + case EOZ: + lexerror(ls, "unfinished string", TK_EOS); + break; /* to avoid warnings */ + case '\n': + case '\r': + lexerror(ls, "unfinished string", TK_STRING); + break; /* to avoid warnings */ + case '\\': { /* escape sequences */ + int c; /* final character to be saved */ + save_and_next(ls); /* keep '\\' for error messages */ + switch (ls->current) { + case 'a': c = '\a'; goto read_save; + case 'b': c = '\b'; goto read_save; + case 'f': c = '\f'; goto read_save; + case 'n': c = '\n'; goto read_save; + case 'r': c = '\r'; goto read_save; + case 't': c = '\t'; goto read_save; + case 'v': c = '\v'; goto read_save; + case 'x': c = readhexaesc(ls); goto read_save; + case 'u': utf8esc(ls); goto no_save; + case '\n': case '\r': + inclinenumber(ls); c = '\n'; goto only_save; + case '\\': case '\"': case '\'': + c = ls->current; goto read_save; + case EOZ: goto no_save; /* will raise an error next loop */ + case 'z': { /* zap following span of spaces */ + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + next(ls); /* skip the 'z' */ + while (lisspace(ls->current)) { + if (currIsNewline(ls)) inclinenumber(ls); + else next(ls); + } + goto no_save; + } + default: { + esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); + c = readdecesc(ls); /* digital escape '\ddd' */ + goto only_save; + } + } + read_save: + next(ls); + /* go through */ + only_save: + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + save(ls, c); + /* go through */ + no_save: break; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': case '\r': { /* line breaks */ + inclinenumber(ls); + break; + } + case ' ': case '\f': case '\t': case '\v': { /* spaces */ + next(ls); + break; + } + case '-': { /* '-' or '--' (comment) */ + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { /* long comment? */ + size_t sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ + if (sep >= 2) { + read_long_string(ls, NULL, sep); /* skip long comment */ + luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ + break; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); /* skip until end of line (or end of file) */ + break; + } + case '[': { /* long string or simply '[' */ + size_t sep = skip_sep(ls); + if (sep >= 2) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == 0) /* '[=...' missing second bracket? */ + lexerror(ls, "invalid long string delimiter", TK_STRING); + return '['; + } + case '=': { + next(ls); + if (check_next1(ls, '=')) return TK_EQ; + else return '='; + } + case '<': { + next(ls); + if (check_next1(ls, '=')) return TK_LE; + else if (check_next1(ls, '<')) return TK_SHL; + else return '<'; + } + case '>': { + next(ls); + if (check_next1(ls, '=')) return TK_GE; + else if (check_next1(ls, '>')) return TK_SHR; + else return '>'; + } + case '/': { + next(ls); + if (check_next1(ls, '/')) return TK_IDIV; + else return '/'; + } + case '~': { + next(ls); + if (check_next1(ls, '=')) return TK_NE; + else return '~'; + } + case ':': { + next(ls); + if (check_next1(ls, ':')) return TK_DBCOLON; + else return ':'; + } + case '"': case '\'': { /* short literal strings */ + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { /* '.', '..', '...', or number */ + save_and_next(ls); + if (check_next1(ls, '.')) { + if (check_next1(ls, '.')) + return TK_DOTS; /* '...' */ + else return TK_CONCAT; /* '..' */ + } + else if (!lisdigit(ls->current)) return '.'; + else return read_numeral(ls, seminfo); + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + return read_numeral(ls, seminfo); + } + case EOZ: { + return TK_EOS; + } + default: { + if (lislalpha(ls->current)) { /* identifier or reserved word? */ + TString *ts; + do { + save_and_next(ls); + } while (lislalnum(ls->current)); + ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + seminfo->ts = ts; + if (isreserved(ts)) /* reserved word? */ + return ts->extra - 1 + FIRST_RESERVED; + else { + return TK_NAME; + } + } + else { /* single-char tokens (+ - / ...) */ + int c = ls->current; + next(ls); + return c; + } + } + } + } +} + + +void luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +int luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); + return ls->lookahead.token; +} + diff --git a/Lua/llex.h b/Lua/llex.h new file mode 100644 index 00000000..389d2f86 --- /dev/null +++ b/Lua/llex.h @@ -0,0 +1,91 @@ +/* +** $Id: llex.h $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include + +#include "lobject.h" +#include "lzio.h" + + +/* +** Single-char tokens (terminal symbols) are represented by their own +** numeric code. Other tokens start at the following value. +*/ +#define FIRST_RESERVED (UCHAR_MAX + 1) + + +#if !defined(LUA_ENV) +#define LUA_ENV "_ENV" +#endif + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, + TK_SHL, TK_SHR, + TK_DBCOLON, TK_EOS, + TK_FLT, TK_INT, TK_NAME, TK_STRING +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1)) + + +typedef union { + lua_Number r; + lua_Integer i; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +/* state of the lexer plus state of the parser when shared by all + functions */ +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token 'consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* current function (parser) */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + Table *h; /* to avoid collection/reuse strings */ + struct Dyndata *dyd; /* dynamic structures used by the parser */ + TString *source; /* current source name */ + TString *envn; /* environment variable name */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source, int firstchar); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC int luaX_lookahead (LexState *ls); +LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif diff --git a/Lua/llimits.h b/Lua/llimits.h new file mode 100644 index 00000000..48c97f95 --- /dev/null +++ b/Lua/llimits.h @@ -0,0 +1,357 @@ +/* +** $Id: llimits.h $ +** Limits, basic types, and some other 'installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#include +#include + + +#include "lua.h" + + +/* +** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count +** the total memory used by Lua (in bytes). Usually, 'size_t' and +** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. +*/ +#if defined(LUAI_MEM) /* { external definitions? */ +typedef LUAI_UMEM lu_mem; +typedef LUAI_MEM l_mem; +#elif LUAI_IS32INT /* }{ */ +typedef size_t lu_mem; +typedef ptrdiff_t l_mem; +#else /* 16-bit ints */ /* }{ */ +typedef unsigned long lu_mem; +typedef long l_mem; +#endif /* } */ + + +/* chars used as small naturals (so that 'char' is reserved for characters) */ +typedef unsigned char lu_byte; +typedef signed char ls_byte; + + +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +/* maximum size visible for Lua (must be representable in a lua_Integer) */ +#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ + : (size_t)(LUA_MAXINTEGER)) + + +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)) + +#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1)) + + +#define MAX_INT INT_MAX /* maximum value of an int */ + + +/* +** floor of the log2 of the maximum signed value for integral type 't'. +** (That is, maximum 'n' such that '2^n' fits in the given signed type.) +*/ +#define log2maxs(t) (sizeof(t) * 8 - 2) + + +/* +** test whether an unsigned value is a power of 2 (or zero) +*/ +#define ispow2(x) (((x) & ((x) - 1)) == 0) + + +/* number of chars of a literal string without the ending \0 */ +#define LL(x) (sizeof(x)/sizeof(char) - 1) + + +/* +** conversion of pointer to unsigned integer: +** this is for hashing only; there is no problem if the integer +** cannot hold the whole pointer value +*/ +#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX)) + + + +/* types of 'usual argument conversions' for lua_Number and lua_Integer */ +typedef LUAI_UACNUMBER l_uacNumber; +typedef LUAI_UACINT l_uacInt; + + +/* +** Internal assertions for in-house debugging +*/ +#if defined LUAI_ASSERT +#undef NDEBUG +#include +#define lua_assert(c) assert(c) +#endif + +#if defined(lua_assert) +#define check_exp(c,e) (lua_assert(c), (e)) +/* to avoid problems with conditions too long */ +#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) +#else +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define lua_longassert(c) ((void)0) +#endif + +/* +** assertion for checking API calls +*/ +#if !defined(luai_apicheck) +#define luai_apicheck(l,e) ((void)l, lua_assert(e)) +#endif + +#define api_check(l,e,msg) luai_apicheck(l,(e) && msg) + + +/* macro to avoid warnings about unused variables */ +#if !defined(UNUSED) +#define UNUSED(x) ((void)(x)) +#endif + + +/* type casts (a macro highlights casts in the code) */ +#define cast(t, exp) ((t)(exp)) + +#define cast_void(i) cast(void, (i)) +#define cast_voidp(i) cast(void *, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) +#define cast_uint(i) cast(unsigned int, (i)) +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_uchar(i) cast(unsigned char, (i)) +#define cast_char(i) cast(char, (i)) +#define cast_charp(i) cast(char *, (i)) +#define cast_sizet(i) cast(size_t, (i)) + + +/* cast a signed lua_Integer to lua_Unsigned */ +#if !defined(l_castS2U) +#define l_castS2U(i) ((lua_Unsigned)(i)) +#endif + +/* +** cast a lua_Unsigned to a signed lua_Integer; this cast is +** not strict ISO C, but two-complement architectures should +** work fine. +*/ +#if !defined(l_castU2S) +#define l_castU2S(i) ((lua_Integer)(i)) +#endif + + +/* +** macros to improve jump prediction (used mainly for error handling) +*/ +#if !defined(likely) + +#if defined(__GNUC__) +#define likely(x) (__builtin_expect(((x) != 0), 1)) +#define unlikely(x) (__builtin_expect(((x) != 0), 0)) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#endif + + +/* +** non-return type +*/ +#if !defined(l_noret) + +#if defined(__GNUC__) +#define l_noret void __attribute__((noreturn)) +#elif defined(_MSC_VER) && _MSC_VER >= 1200 +#define l_noret void __declspec(noreturn) +#else +#define l_noret void +#endif + +#endif + + +/* +** type for virtual-machine instructions; +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +*/ +#if LUAI_IS32INT +typedef unsigned int l_uint32; +#else +typedef unsigned long l_uint32; +#endif + +typedef l_uint32 Instruction; + + + +/* +** Maximum length for short strings, that is, strings that are +** internalized. (Cannot be smaller than reserved words or tags for +** metamethods, as these strings must be internalized; +** #("function") = 8, #("__newindex") = 10.) +*/ +#if !defined(LUAI_MAXSHORTLEN) +#define LUAI_MAXSHORTLEN 40 +#endif + + +/* +** Initial size for the string table (must be power of 2). +** The Lua core alone registers ~50 strings (reserved words + +** metaevent keys + a few others). Libraries would typically add +** a few dozens more. +*/ +#if !defined(MINSTRTABSIZE) +#define MINSTRTABSIZE 128 +#endif + + +/* +** Size of cache for strings in the API. 'N' is the number of +** sets (better be a prime) and "M" is the size of each set (M == 1 +** makes a direct cache.) +*/ +#if !defined(STRCACHE_N) +#define STRCACHE_N 53 +#define STRCACHE_M 2 +#endif + + +/* minimum size for string buffer */ +#if !defined(LUA_MINBUFFER) +#define LUA_MINBUFFER 32 +#endif + + +/* +** macros that are executed whenever program enters the Lua core +** ('lua_lock') and leaves the core ('lua_unlock') +*/ +#if !defined(lua_lock) +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +/* +** macro executed during Lua functions at points where the +** function can yield. +*/ +#if !defined(luai_threadyield) +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** these macros allow user-specific actions when a thread is +** created/deleted/resumed/yielded. +*/ +#if !defined(luai_userstateopen) +#define luai_userstateopen(L) ((void)L) +#endif + +#if !defined(luai_userstateclose) +#define luai_userstateclose(L) ((void)L) +#endif + +#if !defined(luai_userstatethread) +#define luai_userstatethread(L,L1) ((void)L) +#endif + +#if !defined(luai_userstatefree) +#define luai_userstatefree(L,L1) ((void)L) +#endif + +#if !defined(luai_userstateresume) +#define luai_userstateresume(L,n) ((void)L) +#endif + +#if !defined(luai_userstateyield) +#define luai_userstateyield(L,n) ((void)L) +#endif + + + +/* +** The luai_num* macros define the primitive operations over numbers. +*/ + +/* floor division (defined as 'floor(a/b)') */ +#if !defined(luai_numidiv) +#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) +#endif + +/* float division */ +#if !defined(luai_numdiv) +#define luai_numdiv(L,a,b) ((a)/(b)) +#endif + +/* +** modulo: defined as 'a - floor(a/b)*b'; the direct computation +** using this definition has several problems with rounding errors, +** so it is better to use 'fmod'. 'fmod' gives the result of +** 'a - trunc(a/b)*b', and therefore must be corrected when +** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a +** non-integer negative result: non-integer result is equivalent to +** a non-zero remainder 'm'; negative result is equivalent to 'a' and +** 'b' with different signs, or 'm' and 'b' with different signs +** (as the result 'm' of 'fmod' has the same sign of 'a'). +*/ +#if !defined(luai_nummod) +#define luai_nummod(L,a,b,m) \ + { (void)L; (m) = l_mathop(fmod)(a,b); \ + if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); } +#endif + +/* exponentiation */ +#if !defined(luai_numpow) +#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b)) +#endif + +/* the others are quite standard operations */ +#if !defined(luai_numadd) +#define luai_numadd(L,a,b) ((a)+(b)) +#define luai_numsub(L,a,b) ((a)-(b)) +#define luai_nummul(L,a,b) ((a)*(b)) +#define luai_numunm(L,a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numgt(a,b) ((a)>(b)) +#define luai_numge(a,b) ((a)>=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + + + + +/* +** macro to control inclusion of some hard tests on stack reallocation +*/ +#if !defined(HARDSTACKTESTS) +#define condmovestack(L,pre,pos) ((void)0) +#else +/* realloc stack keeping its size */ +#define condmovestack(L,pre,pos) \ + { int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_, 0); pos; } +#endif + +#if !defined(HARDMEMTESTS) +#define condchangemem(L,pre,pos) ((void)0) +#else +#define condchangemem(L,pre,pos) \ + { if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } } +#endif + +#endif diff --git a/Lua/lmathlib.c b/Lua/lmathlib.c new file mode 100644 index 00000000..86def470 --- /dev/null +++ b/Lua/lmathlib.c @@ -0,0 +1,763 @@ +/* +** $Id: lmathlib.c $ +** Standard mathematical library +** See Copyright Notice in lua.h +*/ + +#define lmathlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#undef PI +#define PI (l_mathop(3.141592653589793238462643383279502884)) + + +static int math_abs (lua_State *L) { + if (lua_isinteger(L, 1)) { + lua_Integer n = lua_tointeger(L, 1); + if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); + lua_pushinteger(L, n); + } + else + lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_Number y = luaL_checknumber(L, 1); + lua_Number x = luaL_optnumber(L, 2, 1); + lua_pushnumber(L, l_mathop(atan2)(y, x)); + return 1; +} + + +static int math_toint (lua_State *L) { + int valid; + lua_Integer n = lua_tointegerx(L, 1, &valid); + if (valid) + lua_pushinteger(L, n); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); /* value is not convertible to integer */ + } + return 1; +} + + +static void pushnumint (lua_State *L, lua_Number d) { + lua_Integer n; + if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */ + lua_pushinteger(L, n); /* result is integer */ + else + lua_pushnumber(L, d); /* result is float */ +} + + +static int math_floor (lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own floor */ + else { + lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + +static int math_ceil (lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own ceil */ + else { + lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + +static int math_fmod (lua_State *L) { + if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) { + lua_Integer d = lua_tointeger(L, 2); + if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */ + luaL_argcheck(L, d != 0, 2, "zero"); + lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */ + } + else + lua_pushinteger(L, lua_tointeger(L, 1) % d); + } + else + lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), + luaL_checknumber(L, 2))); + return 1; +} + + +/* +** next function does not use 'modf', avoiding problems with 'double*' +** (which is not compatible with 'float*') when lua_Number is not +** 'double'. +*/ +static int math_modf (lua_State *L) { + if (lua_isinteger(L ,1)) { + lua_settop(L, 1); /* number is its own integer part */ + lua_pushnumber(L, 0); /* no fractional part */ + } + else { + lua_Number n = luaL_checknumber(L, 1); + /* integer part (rounds toward zero) */ + lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n); + pushnumint(L, ip); + /* fractional part (test needed for inf/-inf) */ + lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip)); + } + return 2; +} + + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_ult (lua_State *L) { + lua_Integer a = luaL_checkinteger(L, 1); + lua_Integer b = luaL_checkinteger(L, 2); + lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b); + return 1; +} + +static int math_log (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number res; + if (lua_isnoneornil(L, 2)) + res = l_mathop(log)(x); + else { + lua_Number base = luaL_checknumber(L, 2); +#if !defined(LUA_USE_C89) + if (base == l_mathop(2.0)) + res = l_mathop(log2)(x); else +#endif + if (base == l_mathop(10.0)) + res = l_mathop(log10)(x); + else + res = l_mathop(log)(x)/l_mathop(log)(base); + } + lua_pushnumber(L, res); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); + return 1; +} + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imin = 1; /* index of current minimum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, i, imin, LUA_OPLT)) + imin = i; + } + lua_pushvalue(L, imin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imax = 1; /* index of current maximum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, imax, i, LUA_OPLT)) + imax = i; + } + lua_pushvalue(L, imax); + return 1; +} + + +static int math_type (lua_State *L) { + if (lua_type(L, 1) == LUA_TNUMBER) + lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float"); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); + } + return 1; +} + + + +/* +** {================================================================== +** Pseudo-Random Number Generator based on 'xoshiro256**'. +** =================================================================== +*/ + +/* number of binary digits in the mantissa of a float */ +#define FIGS l_floatatt(MANT_DIG) + +#if FIGS > 64 +/* there are only 64 random bits; use them all */ +#undef FIGS +#define FIGS 64 +#endif + + +/* +** LUA_RAND32 forces the use of 32-bit integers in the implementation +** of the PRN generator (mainly for testing). +*/ +#if !defined(LUA_RAND32) && !defined(Rand64) + +/* try to find an integer type with at least 64 bits */ + +#if (ULONG_MAX >> 31 >> 31) >= 3 + +/* 'long' has at least 64 bits */ +#define Rand64 unsigned long + +#elif !defined(LUA_USE_C89) && defined(LLONG_MAX) + +/* there is a 'long long' type (which must have at least 64 bits) */ +#define Rand64 unsigned long long + +#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3 + +/* 'lua_Integer' has at least 64 bits */ +#define Rand64 lua_Unsigned + +#endif + +#endif + + +#if defined(Rand64) /* { */ + +/* +** Standard implementation, using 64-bit integers. +** If 'Rand64' has more than 64 bits, the extra bits do not interfere +** with the 64 initial bits, except in a right shift. Moreover, the +** final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim64(x) ((x) & 0xffffffffffffffffu) + + +/* rotate left 'x' by 'n' bits */ +static Rand64 rotl (Rand64 x, int n) { + return (x << n) | (trim64(x) >> (64 - n)); +} + +static Rand64 nextrand (Rand64 *state) { + Rand64 state0 = state[0]; + Rand64 state1 = state[1]; + Rand64 state2 = state[2] ^ state0; + Rand64 state3 = state[3] ^ state1; + Rand64 res = rotl(state1 * 5, 7) * 9; + state[0] = state0 ^ state3; + state[1] = state1 ^ state2; + state[2] = state2 ^ (state1 << 17); + state[3] = rotl(state3, 45); + return res; +} + + +/* must take care to not shift stuff by more than 63 slots */ + + +/* +** Convert bits from a random integer into a float in the +** interval [0,1), getting the higher FIG bits from the +** random unsigned integer and converting that to a float. +*/ + +/* must throw out the extra (64 - FIGS) bits */ +#define shift64_FIG (64 - FIGS) + +/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */ +#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) + +static lua_Number I2d (Rand64 x) { + return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG; +} + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +#define I2UInt(x) ((lua_Unsigned)trim64(x)) + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +#define Int2I(x) ((Rand64)(x)) + + +#else /* no 'Rand64' }{ */ + +/* get an integer with at least 32 bits */ +#if LUAI_IS32INT +typedef unsigned int lu_int32; +#else +typedef unsigned long lu_int32; +#endif + + +/* +** Use two 32-bit integers to represent a 64-bit quantity. +*/ +typedef struct Rand64 { + lu_int32 h; /* higher half */ + lu_int32 l; /* lower half */ +} Rand64; + + +/* +** If 'lu_int32' has more than 32 bits, the extra bits do not interfere +** with the 32 initial bits, except in a right shift and comparisons. +** Moreover, the final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim32(x) ((x) & 0xffffffffu) + + +/* +** basic operations on 'Rand64' values +*/ + +/* build a new Rand64 value */ +static Rand64 packI (lu_int32 h, lu_int32 l) { + Rand64 result; + result.h = h; + result.l = l; + return result; +} + +/* return i << n */ +static Rand64 Ishl (Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n); +} + +/* i1 ^= i2 */ +static void Ixor (Rand64 *i1, Rand64 i2) { + i1->h ^= i2.h; + i1->l ^= i2.l; +} + +/* return i1 + i2 */ +static Rand64 Iadd (Rand64 i1, Rand64 i2) { + Rand64 result = packI(i1.h + i2.h, i1.l + i2.l); + if (trim32(result.l) < trim32(i1.l)) /* carry? */ + result.h++; + return result; +} + +/* return i * 5 */ +static Rand64 times5 (Rand64 i) { + return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */ +} + +/* return i * 9 */ +static Rand64 times9 (Rand64 i) { + return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */ +} + +/* return 'i' rotated left 'n' bits */ +static Rand64 rotl (Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), + (trim32(i.h) >> (32 - n)) | (i.l << n)); +} + +/* for offsets larger than 32, rotate right by 64 - offset */ +static Rand64 rotl1 (Rand64 i, int n) { + lua_assert(n > 32 && n < 64); + n = 64 - n; + return packI((trim32(i.h) >> n) | (i.l << (32 - n)), + (i.h << (32 - n)) | (trim32(i.l) >> n)); +} + +/* +** implementation of 'xoshiro256**' algorithm on 'Rand64' values +*/ +static Rand64 nextrand (Rand64 *state) { + Rand64 res = times9(rotl(times5(state[1]), 7)); + Rand64 t = Ishl(state[1], 17); + Ixor(&state[2], state[0]); + Ixor(&state[3], state[1]); + Ixor(&state[1], state[2]); + Ixor(&state[0], state[3]); + Ixor(&state[2], t); + state[3] = rotl1(state[3], 45); + return res; +} + + +/* +** Converts a 'Rand64' into a float. +*/ + +/* an unsigned 1 with proper type */ +#define UONE ((lu_int32)1) + + +#if FIGS <= 32 + +/* 2^(-FIGS) */ +#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) + +/* +** get up to 32 bits from higher half, shifting right to +** throw out the extra bits. +*/ +static lua_Number I2d (Rand64 x) { + lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS)); + return h * scaleFIG; +} + +#else /* 32 < FIGS <= 64 */ + +/* must take care to not shift stuff by more than 31 slots */ + +/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ +#define scaleFIG \ + ((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33))) + +/* +** use FIGS - 32 bits from lower half, throwing out the other +** (32 - (FIGS - 32)) = (64 - FIGS) bits +*/ +#define shiftLOW (64 - FIGS) + +/* +** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32) +*/ +#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0) + + +static lua_Number I2d (Rand64 x) { + lua_Number h = (lua_Number)trim32(x.h) * shiftHI; + lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW); + return (h + l) * scaleFIG; +} + +#endif + + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +static lua_Unsigned I2UInt (Rand64 x) { + return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l); +} + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +static Rand64 Int2I (lua_Unsigned n) { + return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n); +} + +#endif /* } */ + + +/* +** A state uses four 'Rand64' values. +*/ +typedef struct { + Rand64 s[4]; +} RanState; + + +/* +** Project the random integer 'ran' into the interval [0, n]. +** Because 'ran' has 2^B possible values, the projection can only be +** uniform when the size of the interval is a power of 2 (exact +** division). Otherwise, to get a uniform projection into [0, n], we +** first compute 'lim', the smallest Mersenne number not smaller than +** 'n'. We then project 'ran' into the interval [0, lim]. If the result +** is inside [0, n], we are done. Otherwise, we try with another 'ran', +** until we have a result inside the interval. +*/ +static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n, + RanState *state) { + if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */ + return ran & n; /* no bias */ + else { + lua_Unsigned lim = n; + /* compute the smallest (2^b - 1) not smaller than 'n' */ + lim |= (lim >> 1); + lim |= (lim >> 2); + lim |= (lim >> 4); + lim |= (lim >> 8); + lim |= (lim >> 16); +#if (LUA_MAXUNSIGNED >> 31) >= 3 + lim |= (lim >> 32); /* integer type has more than 32 bits */ +#endif + lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */ + && lim >= n /* not smaller than 'n', */ + && (lim >> 1) < n); /* and it is the smallest one */ + while ((ran &= lim) > n) /* project 'ran' into [0..lim] */ + ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ + return ran; + } +} + + +static int math_random (lua_State *L) { + lua_Integer low, up; + lua_Unsigned p; + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + Rand64 rv = nextrand(state->s); /* next pseudo-random value */ + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */ + return 1; + } + case 1: { /* only upper limit */ + low = 1; + up = luaL_checkinteger(L, 1); + if (up == 0) { /* single 0 as argument? */ + lua_pushinteger(L, I2UInt(rv)); /* full random integer */ + return 1; + } + break; + } + case 2: { /* lower and upper limits */ + low = luaL_checkinteger(L, 1); + up = luaL_checkinteger(L, 2); + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + /* random integer in the interval [low, up] */ + luaL_argcheck(L, low <= up, 1, "interval is empty"); + /* project random integer into the interval [0, up - low] */ + p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state); + lua_pushinteger(L, p + (lua_Unsigned)low); + return 1; +} + + +static void setseed (lua_State *L, Rand64 *state, + lua_Unsigned n1, lua_Unsigned n2) { + int i; + state[0] = Int2I(n1); + state[1] = Int2I(0xff); /* avoid a zero state */ + state[2] = Int2I(n2); + state[3] = Int2I(0); + for (i = 0; i < 16; i++) + nextrand(state); /* discard initial values to "spread" seed */ + lua_pushinteger(L, n1); + lua_pushinteger(L, n2); +} + + +/* +** Set a "random" seed. To get some randomness, use the current time +** and the address of 'L' (in case the machine does address space layout +** randomization). +*/ +static void randseed (lua_State *L, RanState *state) { + lua_Unsigned seed1 = (lua_Unsigned)time(NULL); + lua_Unsigned seed2 = (lua_Unsigned)(size_t)L; + setseed(L, state->s, seed1, seed2); +} + + +static int math_randomseed (lua_State *L) { + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + if (lua_isnone(L, 1)) { + randseed(L, state); + } + else { + lua_Integer n1 = luaL_checkinteger(L, 1); + lua_Integer n2 = luaL_optinteger(L, 2, 0); + setseed(L, state->s, n1, n2); + } + return 2; /* return seeds */ +} + + +static const luaL_Reg randfuncs[] = { + {"random", math_random}, + {"randomseed", math_randomseed}, + {NULL, NULL} +}; + + +/* +** Register the random functions and initialize their state. +*/ +static void setrandfunc (lua_State *L) { + RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0); + randseed(L, state); /* initialize with a "random" seed */ + lua_pop(L, 2); /* remove pushed seeds */ + luaL_setfuncs(L, randfuncs, 1); +} + +/* }================================================================== */ + + +/* +** {================================================================== +** Deprecated functions (for compatibility only) +** =================================================================== +*/ +#if defined(LUA_COMPAT_MATHLIB) + +static int math_cosh (lua_State *L) { + lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh (lua_State *L) { + lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh (lua_State *L) { + lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number y = luaL_checknumber(L, 2); + lua_pushnumber(L, l_mathop(pow)(x, y)); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); + lua_pushinteger(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep = (int)luaL_checkinteger(L, 2); + lua_pushnumber(L, l_mathop(ldexp)(x, ep)); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); + return 1; +} + +#endif +/* }================================================================== */ + + + +static const luaL_Reg mathlib[] = { + {"abs", math_abs}, + {"acos", math_acos}, + {"asin", math_asin}, + {"atan", math_atan}, + {"ceil", math_ceil}, + {"cos", math_cos}, + {"deg", math_deg}, + {"exp", math_exp}, + {"tointeger", math_toint}, + {"floor", math_floor}, + {"fmod", math_fmod}, + {"ult", math_ult}, + {"log", math_log}, + {"max", math_max}, + {"min", math_min}, + {"modf", math_modf}, + {"rad", math_rad}, + {"sin", math_sin}, + {"sqrt", math_sqrt}, + {"tan", math_tan}, + {"type", math_type}, +#if defined(LUA_COMPAT_MATHLIB) + {"atan2", math_atan}, + {"cosh", math_cosh}, + {"sinh", math_sinh}, + {"tanh", math_tanh}, + {"pow", math_pow}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"log10", math_log10}, +#endif + /* placeholders */ + {"random", NULL}, + {"randomseed", NULL}, + {"pi", NULL}, + {"huge", NULL}, + {"maxinteger", NULL}, + {"mininteger", NULL}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUAMOD_API int luaopen_math (lua_State *L) { + luaL_newlib(L, mathlib); + lua_pushnumber(L, PI); + lua_setfield(L, -2, "pi"); + lua_pushnumber(L, (lua_Number)HUGE_VAL); + lua_setfield(L, -2, "huge"); + lua_pushinteger(L, LUA_MAXINTEGER); + lua_setfield(L, -2, "maxinteger"); + lua_pushinteger(L, LUA_MININTEGER); + lua_setfield(L, -2, "mininteger"); + setrandfunc(L); + return 1; +} + diff --git a/Lua/lmem.c b/Lua/lmem.c new file mode 100644 index 00000000..43739bff --- /dev/null +++ b/Lua/lmem.c @@ -0,0 +1,202 @@ +/* +** $Id: lmem.c $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#define lmem_c +#define LUA_CORE + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + +#if defined(EMERGENCYGCTESTS) +/* +** First allocation will fail whenever not building initial state +** and not shrinking a block. (This fail will trigger 'tryagain' and +** a full GC cycle at every allocation.) +*/ +static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { + if (ttisnil(&g->nilvalue) && ns > os) + return NULL; /* fail */ + else /* normal allocation */ + return (*g->frealloc)(g->ud, block, os, ns); +} +#else +#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) +#endif + + + + + +/* +** About the realloc function: +** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** ('osize' is the old size, 'nsize' is the new size) +** +** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL. +** Particularly, frealloc(ud, NULL, 0, 0) does nothing, +** which is equivalent to free(NULL) in ISO C. +** +** - frealloc(ud, NULL, x, s) creates a new block of size 's' +** (no matter 'x'). Returns NULL if it cannot create the new block. +** +** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from +** size 'x' to size 'y'. Returns NULL if it cannot reallocate the +** block to the new size. +*/ + + + + +/* +** {================================================================== +** Functions to allocate/deallocate arrays for the Parser +** =================================================================== +*/ + +/* +** Minimum size for arrays during parsing, to avoid overhead of +** reallocating to size 1, then 2, and then 4. All these arrays +** will be reallocated to exact sizes or erased when parsing ends. +*/ +#define MINSIZEARRAY 4 + + +void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, + int size_elems, int limit, const char *what) { + void *newblock; + int size = *psize; + if (nelems + 1 <= size) /* does one extra element still fit? */ + return block; /* nothing to be done */ + if (size >= limit / 2) { /* cannot double it? */ + if (unlikely(size >= limit)) /* cannot grow even a little? */ + luaG_runerror(L, "too many %s (limit is %d)", what, limit); + size = limit; /* still have at least one free place */ + } + else { + size *= 2; + if (size < MINSIZEARRAY) + size = MINSIZEARRAY; /* minimum size */ + } + lua_assert(nelems + 1 <= size && size <= limit); + /* 'limit' ensures that multiplication will not overflow */ + newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems, + cast_sizet(size) * size_elems); + *psize = size; /* update only when everything else is OK */ + return newblock; +} + + +/* +** In prototypes, the size of the array is also its number of +** elements (to save memory). So, if it cannot shrink an array +** to its number of elements, the only option is to raise an +** error. +*/ +void *luaM_shrinkvector_ (lua_State *L, void *block, int *size, + int final_n, int size_elem) { + void *newblock; + size_t oldsize = cast_sizet((*size) * size_elem); + size_t newsize = cast_sizet(final_n * size_elem); + lua_assert(newsize <= oldsize); + newblock = luaM_saferealloc_(L, block, oldsize, newsize); + *size = final_n; + return newblock; +} + +/* }================================================================== */ + + +l_noret luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); +} + + +/* +** Free memory +*/ +void luaM_free_ (lua_State *L, void *block, size_t osize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + (*g->frealloc)(g->ud, block, osize, 0); + g->GCdebt -= osize; +} + + +/* +** In case of allocation fail, this function will call the GC to try +** to free some memory and then try the allocation again. +** (It should not be called when shrinking a block, because then the +** interpreter may be in the middle of a collection step.) +*/ +static void *tryagain (lua_State *L, void *block, + size_t osize, size_t nsize) { + global_State *g = G(L); + if (ttisnil(&g->nilvalue)) { /* is state fully build? */ + luaC_fullgc(L, 1); /* try to free some memory... */ + return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ + } + else return NULL; /* cannot free any memory without a full state */ +} + + +/* +** Generic allocation routine. +** If allocation fails while shrinking a block, do not try again; the +** GC shrinks some blocks and it is not reentrant. +*/ +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + void *newblock; + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + newblock = firsttry(g, block, osize, nsize); + if (unlikely(newblock == NULL && nsize > 0)) { + if (nsize > osize) /* not shrinking a block? */ + newblock = tryagain(L, block, osize, nsize); + if (newblock == NULL) /* still no memory? */ + return NULL; /* do not update 'GCdebt' */ + } + lua_assert((nsize == 0) == (newblock == NULL)); + g->GCdebt = (g->GCdebt + nsize) - osize; + return newblock; +} + + +void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize, + size_t nsize) { + void *newblock = luaM_realloc_(L, block, osize, nsize); + if (unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */ + luaM_error(L); + return newblock; +} + + +void *luaM_malloc_ (lua_State *L, size_t size, int tag) { + if (size == 0) + return NULL; /* that's all */ + else { + global_State *g = G(L); + void *newblock = firsttry(g, NULL, tag, size); + if (unlikely(newblock == NULL)) { + newblock = tryagain(L, NULL, tag, size); + if (newblock == NULL) + luaM_error(L); + } + g->GCdebt += size; + return newblock; + } +} diff --git a/Lua/lmem.h b/Lua/lmem.h new file mode 100644 index 00000000..8c75a44b --- /dev/null +++ b/Lua/lmem.h @@ -0,0 +1,93 @@ +/* +** $Id: lmem.h $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +#include + +#include "llimits.h" +#include "lua.h" + + +#define luaM_error(L) luaD_throw(L, LUA_ERRMEM) + + +/* +** This macro tests whether it is safe to multiply 'n' by the size of +** type 't' without overflows. Because 'e' is always constant, it avoids +** the runtime division MAX_SIZET/(e). +** (The macro is somewhat complex to avoid warnings: The 'sizeof' +** comparison avoids a runtime comparison when overflow cannot occur. +** The compiler should be able to optimize the real test by itself, but +** when it does it, it may give a warning about "comparison is always +** false due to limited range of data type"; the +1 tricks the compiler, +** avoiding this warning but also this optimization.) +*/ +#define luaM_testsize(n,e) \ + (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e)) + +#define luaM_checksize(L,n,e) \ + (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0)) + + +/* +** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that +** the result is not larger than 'n' and cannot overflow a 'size_t' +** when multiplied by the size of type 't'. (Assumes that 'n' is an +** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.) +*/ +#define luaM_limitN(n,t) \ + ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \ + cast_uint((MAX_SIZET/sizeof(t)))) + + +/* +** Arrays of chars do not need any test +*/ +#define luaM_reallocvchar(L,b,on,n) \ + cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) + +#define luaM_freemem(L, b, s) luaM_free_(L, (b), (s)) +#define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b))) +#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) + +#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) +#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) +#define luaM_newvectorchecked(L,n,t) \ + (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) + +#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ + luaM_limitN(limit,t),e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \ + cast_sizet(n) * sizeof(t)))) + +#define luaM_shrinkvector(L,v,size,fs,t) \ + ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t)))) + +LUAI_FUNC l_noret luaM_toobig (lua_State *L); + +/* not to be called directly */ +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems, + int *size, int size_elem, int limit, + const char *what); +LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem, + int final_n, int size_elem); +LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag); + +#endif + diff --git a/Lua/loadlib.c b/Lua/loadlib.c new file mode 100644 index 00000000..c0ec9a13 --- /dev/null +++ b/Lua/loadlib.c @@ -0,0 +1,759 @@ +/* +** $Id: loadlib.c $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +** +** This module contains an implementation of loadlib for Unix systems +** that have dlfcn, an implementation for Windows, and a stub for other +** systems. +*/ + +#define loadlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* +** LUA_IGMARK is a mark to ignore all before it when building the +** luaopen_ function name. +*/ +#if !defined (LUA_IGMARK) +#define LUA_IGMARK "-" +#endif + + +/* +** LUA_CSUBSEP is the character that replaces dots in submodule names +** when searching for a C loader. +** LUA_LSUBSEP is the character that replaces dots in submodule names +** when searching for a Lua loader. +*/ +#if !defined(LUA_CSUBSEP) +#define LUA_CSUBSEP LUA_DIRSEP +#endif + +#if !defined(LUA_LSUBSEP) +#define LUA_LSUBSEP LUA_DIRSEP +#endif + + +/* prefix for open functions in C libraries */ +#define LUA_POF "luaopen_" + +/* separator for open functions in C libraries */ +#define LUA_OFSEP "_" + + +/* +** key for table in the registry that keeps handles +** for all loaded C libraries +*/ +static const char *const CLIBS = "_CLIBS"; + +#define LIB_FAIL "open" + + +#define setprogdir(L) ((void)0) + + +/* +** Special type equivalent to '(void*)' for functions in gcc +** (to suppress warnings when converting function pointers) +*/ +typedef void (*voidf)(void); + + +/* +** system-dependent functions +*/ + +/* +** unload library 'lib' +*/ +static void lsys_unloadlib (void *lib); + +/* +** load C library in file 'path'. If 'seeglb', load with all names in +** the library global. +** Returns the library; in case of error, returns NULL plus an +** error string in the stack. +*/ +static void *lsys_load (lua_State *L, const char *path, int seeglb); + +/* +** Try to find a function named 'sym' in library 'lib'. +** Returns the function; in case of error, returns NULL plus an +** error string in the stack. +*/ +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym); + + + + +#if defined(LUA_USE_DLOPEN) /* { */ +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + +#include + +/* +** Macro to convert pointer-to-void* to pointer-to-function. This cast +** is undefined according to ISO C, but POSIX assumes that it works. +** (The '__extension__' in gnu compilers is only to avoid warnings.) +*/ +#if defined(__GNUC__) +#define cast_func(p) (__extension__ (lua_CFunction)(p)) +#else +#define cast_func(p) ((lua_CFunction)(p)) +#endif + + +static void lsys_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); + if (lib == NULL) lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = cast_func(dlsym(lib, sym)); + if (f == NULL) lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DLL) /* }{ */ +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include + + +/* +** optional flags for LoadLibraryEx +*/ +#if !defined(LUA_LLE_FLAGS) +#define LUA_LLE_FLAGS 0 +#endif + + +#undef setprogdir + + +/* +** Replace in the path (on the top of the stack) any occurrence +** of LUA_EXEC_DIR with the executable's path. +*/ +static void setprogdir (lua_State *L) { + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff)/sizeof(char); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */ + if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) + luaL_error(L, "unable to get ModuleFileName"); + else { + *lb = '\0'; /* cut name on the last '\\' to get the path */ + luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + + + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer)/sizeof(char), NULL)) + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void lsys_unloadlib (void *lib) { + FreeLibrary((HMODULE)lib); +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS); + (void)(seeglb); /* not used: symbols are 'global' by default */ + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + +#else /* }{ */ +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +#define DLMSG "dynamic libraries not enabled; check your Lua installation" + + +static void lsys_unloadlib (void *lib) { + (void)(lib); /* not used */ +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + (void)(path); (void)(seeglb); /* not used */ + lua_pushliteral(L, DLMSG); + return NULL; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + (void)(lib); (void)(sym); /* not used */ + lua_pushliteral(L, DLMSG); + return NULL; +} + +/* }====================================================== */ +#endif /* } */ + + +/* +** {================================================================== +** Set Paths +** =================================================================== +*/ + +/* +** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment +** variables that Lua check to set its paths. +*/ +#if !defined(LUA_PATH_VAR) +#define LUA_PATH_VAR "LUA_PATH" +#endif + +#if !defined(LUA_CPATH_VAR) +#define LUA_CPATH_VAR "LUA_CPATH" +#endif + + + +/* +** return registry.LUA_NOENV as a boolean +*/ +static int noenv (lua_State *L) { + int b; + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + b = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + return b; +} + + +/* +** Set a path +*/ +static void setpath (lua_State *L, const char *fieldname, + const char *envname, + const char *dft) { + const char *dftmark; + const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); + const char *path = getenv(nver); /* try versioned name */ + if (path == NULL) /* no versioned environment variable? */ + path = getenv(envname); /* try unversioned name */ + if (path == NULL || noenv(L)) /* no environment variable? */ + lua_pushstring(L, dft); /* use default */ + else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL) + lua_pushstring(L, path); /* nothing to change */ + else { /* path contains a ";;": insert default path in its place */ + size_t len = strlen(path); + luaL_Buffer b; + luaL_buffinit(L, &b); + if (path < dftmark) { /* is there a prefix before ';;'? */ + luaL_addlstring(&b, path, dftmark - path); /* add it */ + luaL_addchar(&b, *LUA_PATH_SEP); + } + luaL_addstring(&b, dft); /* add default */ + if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */ + luaL_addchar(&b, *LUA_PATH_SEP); + luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark); + } + luaL_pushresult(&b); + } + setprogdir(L); + lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ + lua_pop(L, 1); /* pop versioned variable name ('nver') */ +} + +/* }================================================================== */ + + +/* +** return registry.CLIBS[path] +*/ +static void *checkclib (lua_State *L, const char *path) { + void *plib; + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_getfield(L, -1, path); + plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */ + lua_pop(L, 2); /* pop CLIBS table and 'plib' */ + return plib; +} + + +/* +** registry.CLIBS[path] = plib -- for queries +** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries +*/ +static void addtoclib (lua_State *L, const char *path, void *plib) { + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_pushlightuserdata(L, plib); + lua_pushvalue(L, -1); + lua_setfield(L, -3, path); /* CLIBS[path] = plib */ + lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */ + lua_pop(L, 1); /* pop CLIBS table */ +} + + +/* +** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib +** handles in list CLIBS +*/ +static int gctm (lua_State *L) { + lua_Integer n = luaL_len(L, 1); + for (; n >= 1; n--) { /* for each handle, in reverse order */ + lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ + lsys_unloadlib(lua_touserdata(L, -1)); + lua_pop(L, 1); /* pop handle */ + } + return 0; +} + + + +/* error codes for 'lookforfunc' */ +#define ERRLIB 1 +#define ERRFUNC 2 + +/* +** Look for a C function named 'sym' in a dynamically loaded library +** 'path'. +** First, check whether the library is already loaded; if not, try +** to load it. +** Then, if 'sym' is '*', return true (as library has been loaded). +** Otherwise, look for symbol 'sym' in the library and push a +** C function with that symbol. +** Return 0 and 'true' or a function in the stack; in case of +** errors, return an error code and an error message in the stack. +*/ +static int lookforfunc (lua_State *L, const char *path, const char *sym) { + void *reg = checkclib(L, path); /* check loaded C libraries */ + if (reg == NULL) { /* must load library? */ + reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */ + if (reg == NULL) return ERRLIB; /* unable to load library */ + addtoclib(L, path, reg); + } + if (*sym == '*') { /* loading only library (no function)? */ + lua_pushboolean(L, 1); /* return 'true' */ + return 0; /* no errors */ + } + else { + lua_CFunction f = lsys_sym(L, reg, sym); + if (f == NULL) + return ERRFUNC; /* unable to find function */ + lua_pushcfunction(L, f); /* else create new function */ + return 0; /* no errors */ + } +} + + +static int ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int stat = lookforfunc(L, path, init); + if (stat == 0) /* no errors? */ + return 1; /* return the loaded function */ + else { /* error; error message is on stack top */ + luaL_pushfail(L); + lua_insert(L, -2); + lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); + return 3; /* return fail, error message, and where */ + } +} + + + +/* +** {====================================================== +** 'require' function +** ======================================================= +*/ + + +static int readable (const char *filename) { + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + fclose(f); + return 1; +} + + +/* +** Get the next name in '*path' = 'name1;name2;name3;...', changing +** the ending ';' to '\0' to create a zero-terminated string. Return +** NULL when list ends. +*/ +static const char *getnextfilename (char **path, char *end) { + char *sep; + char *name = *path; + if (name == end) + return NULL; /* no more names */ + else if (*name == '\0') { /* from previous iteration? */ + *name = *LUA_PATH_SEP; /* restore separator */ + name++; /* skip it */ + } + sep = strchr(name, *LUA_PATH_SEP); /* find next separator */ + if (sep == NULL) /* separator not found? */ + sep = end; /* name goes until the end */ + *sep = '\0'; /* finish file name */ + *path = sep; /* will start next search from here */ + return name; +} + + +/* +** Given a path such as ";blabla.so;blublu.so", pushes the string +** +** no file 'blabla.so' +** no file 'blublu.so' +*/ +static void pusherrornotfound (lua_State *L, const char *path) { + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addstring(&b, "no file '"); + luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '"); + luaL_addstring(&b, "'"); + luaL_pushresult(&b); +} + + +static const char *searchpath (lua_State *L, const char *name, + const char *path, + const char *sep, + const char *dirsep) { + luaL_Buffer buff; + char *pathname; /* path with name inserted */ + char *endpathname; /* its end */ + const char *filename; + /* separator is non-empty and appears in 'name'? */ + if (*sep != '\0' && strchr(name, *sep) != NULL) + name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ + luaL_buffinit(L, &buff); + /* add path to the buffer, replacing marks ('?') with the file name */ + luaL_addgsub(&buff, path, LUA_PATH_MARK, name); + luaL_addchar(&buff, '\0'); + pathname = luaL_buffaddr(&buff); /* writable list of file names */ + endpathname = pathname + luaL_bufflen(&buff) - 1; + while ((filename = getnextfilename(&pathname, endpathname)) != NULL) { + if (readable(filename)) /* does file exist and is readable? */ + return lua_pushstring(L, filename); /* save and return name */ + } + luaL_pushresult(&buff); /* push path to create error message */ + pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */ + return NULL; /* not found */ +} + + +static int ll_searchpath (lua_State *L) { + const char *f = searchpath(L, luaL_checkstring(L, 1), + luaL_checkstring(L, 2), + luaL_optstring(L, 3, "."), + luaL_optstring(L, 4, LUA_DIRSEP)); + if (f != NULL) return 1; + else { /* error message is on top of the stack */ + luaL_pushfail(L); + lua_insert(L, -2); + return 2; /* return fail + error message */ + } +} + + +static const char *findfile (lua_State *L, const char *name, + const char *pname, + const char *dirsep) { + const char *path; + lua_getfield(L, lua_upvalueindex(1), pname); + path = lua_tostring(L, -1); + if (path == NULL) + luaL_error(L, "'package.%s' must be a string", pname); + return searchpath(L, name, path, ".", dirsep); +} + + +static int checkload (lua_State *L, int stat, const char *filename) { + if (stat) { /* module loaded successfully? */ + lua_pushstring(L, filename); /* will be 2nd argument to module */ + return 2; /* return open function and file name */ + } + else + return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + + +static int searcher_Lua (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path", LUA_LSUBSEP); + if (filename == NULL) return 1; /* module not found in this path */ + return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename); +} + + +/* +** Try to find a load function for module 'modname' at file 'filename'. +** First, change '.' to '_' in 'modname'; then, if 'modname' has +** the form X-Y (that is, it has an "ignore mark"), build a function +** name "luaopen_X" and look for it. (For compatibility, if that +** fails, it also tries "luaopen_Y".) If there is no ignore mark, +** look for a function named "luaopen_modname". +*/ +static int loadfunc (lua_State *L, const char *filename, const char *modname) { + const char *openfunc; + const char *mark; + modname = luaL_gsub(L, modname, ".", LUA_OFSEP); + mark = strchr(modname, *LUA_IGMARK); + if (mark) { + int stat; + openfunc = lua_pushlstring(L, modname, mark - modname); + openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); + stat = lookforfunc(L, filename, openfunc); + if (stat != ERRFUNC) return stat; + modname = mark + 1; /* else go ahead and try old-style name */ + } + openfunc = lua_pushfstring(L, LUA_POF"%s", modname); + return lookforfunc(L, filename, openfunc); +} + + +static int searcher_C (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath", LUA_CSUBSEP); + if (filename == NULL) return 1; /* module not found in this path */ + return checkload(L, (loadfunc(L, filename, name) == 0), filename); +} + + +static int searcher_Croot (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = strchr(name, '.'); + int stat; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, p - name); + filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP); + if (filename == NULL) return 1; /* root not found */ + if ((stat = loadfunc(L, filename, name)) != 0) { + if (stat != ERRFUNC) + return checkload(L, 0, filename); /* real error */ + else { /* open function not found */ + lua_pushfstring(L, "no module '%s' in file '%s'", name, filename); + return 1; + } + } + lua_pushstring(L, filename); /* will be 2nd argument to module */ + return 2; +} + + +static int searcher_preload (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ + lua_pushfstring(L, "no field package.preload['%s']", name); + return 1; + } + else { + lua_pushliteral(L, ":preload:"); + return 2; + } +} + + +static void findloader (lua_State *L, const char *name) { + int i; + luaL_Buffer msg; /* to build error message */ + /* push 'package.searchers' to index 3 in the stack */ + if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE) + luaL_error(L, "'package.searchers' must be a table"); + luaL_buffinit(L, &msg); + /* iterate over available searchers to find a loader */ + for (i = 1; ; i++) { + luaL_addstring(&msg, "\n\t"); /* error-message prefix */ + if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */ + lua_pop(L, 1); /* remove nil */ + luaL_buffsub(&msg, 2); /* remove prefix */ + luaL_pushresult(&msg); /* create error message */ + luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); + } + lua_pushstring(L, name); + lua_call(L, 1, 2); /* call it */ + if (lua_isfunction(L, -2)) /* did it find a loader? */ + return; /* module loader found */ + else if (lua_isstring(L, -2)) { /* searcher returned error message? */ + lua_pop(L, 1); /* remove extra return */ + luaL_addvalue(&msg); /* concatenate error message */ + } + else { /* no error message */ + lua_pop(L, 2); /* remove both returns */ + luaL_buffsub(&msg, 2); /* remove prefix */ + } + } +} + + +static int ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_settop(L, 1); /* LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, 2, name); /* LOADED[name] */ + if (lua_toboolean(L, -1)) /* is it there? */ + return 1; /* package is already loaded */ + /* else must load package */ + lua_pop(L, 1); /* remove 'getfield' result */ + findloader(L, name); + lua_rotate(L, -2, 1); /* function <-> loader data */ + lua_pushvalue(L, 1); /* name is 1st argument to module loader */ + lua_pushvalue(L, -3); /* loader data is 2nd argument */ + /* stack: ...; loader data; loader function; mod. name; loader data */ + lua_call(L, 2, 1); /* run loader to load module */ + /* stack: ...; loader data; result from loader */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* LOADED[name] = returned value */ + else + lua_pop(L, 1); /* pop nil */ + if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_copy(L, -1, -2); /* replace loader result */ + lua_setfield(L, 2, name); /* LOADED[name] = true */ + } + lua_rotate(L, -2, 1); /* loader data <-> module result */ + return 2; /* return module result and loader data */ +} + +/* }====================================================== */ + + + + +static const luaL_Reg pk_funcs[] = { + {"loadlib", ll_loadlib}, + {"searchpath", ll_searchpath}, + /* placeholders */ + {"preload", NULL}, + {"cpath", NULL}, + {"path", NULL}, + {"searchers", NULL}, + {"loaded", NULL}, + {NULL, NULL} +}; + + +static const luaL_Reg ll_funcs[] = { + {"require", ll_require}, + {NULL, NULL} +}; + + +static void createsearcherstable (lua_State *L) { + static const lua_CFunction searchers[] = + {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; + int i; + /* create 'searchers' table */ + lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); + /* fill it with predefined searchers */ + for (i=0; searchers[i] != NULL; i++) { + lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ + lua_pushcclosure(L, searchers[i], 1); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ +} + + +/* +** create table CLIBS to keep track of loaded C libraries, +** setting a finalizer to close all libraries when closing state. +*/ +static void createclibstable (lua_State *L) { + luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ + lua_createtable(L, 0, 1); /* create metatable for CLIBS */ + lua_pushcfunction(L, gctm); + lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ + lua_setmetatable(L, -2); +} + + +LUAMOD_API int luaopen_package (lua_State *L) { + createclibstable(L); + luaL_newlib(L, pk_funcs); /* create 'package' table */ + createsearcherstable(L); + /* set paths */ + setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT); + setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT); + /* store config information */ + lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n" + LUA_EXEC_DIR "\n" LUA_IGMARK "\n"); + lua_setfield(L, -2, "config"); + /* set field 'loaded' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_setfield(L, -2, "loaded"); + /* set field 'preload' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + lua_setfield(L, -2, "preload"); + lua_pushglobaltable(L); + lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */ + luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */ + lua_pop(L, 1); /* pop global table */ + return 1; /* return 'package' table */ +} + diff --git a/Lua/lobject.c b/Lua/lobject.c new file mode 100644 index 00000000..f8ea917a --- /dev/null +++ b/Lua/lobject.c @@ -0,0 +1,592 @@ +/* +** $Id: lobject.c $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#define lobject_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include +#include +#include +#include +#include + +#include "lua.h" + +#include "lctype.h" +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "lvm.h" + + +/* +** Computes ceil(log2(x)) +*/ +int luaO_ceillog2 (unsigned int x) { + static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = 0; + x--; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; +} + + +static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, + lua_Integer v2) { + switch (op) { + case LUA_OPADD: return intop(+, v1, v2); + case LUA_OPSUB:return intop(-, v1, v2); + case LUA_OPMUL:return intop(*, v1, v2); + case LUA_OPMOD: return luaV_mod(L, v1, v2); + case LUA_OPIDIV: return luaV_idiv(L, v1, v2); + case LUA_OPBAND: return intop(&, v1, v2); + case LUA_OPBOR: return intop(|, v1, v2); + case LUA_OPBXOR: return intop(^, v1, v2); + case LUA_OPSHL: return luaV_shiftl(v1, v2); + case LUA_OPSHR: return luaV_shiftl(v1, -v2); + case LUA_OPUNM: return intop(-, 0, v1); + case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); + default: lua_assert(0); return 0; + } +} + + +static lua_Number numarith (lua_State *L, int op, lua_Number v1, + lua_Number v2) { + switch (op) { + case LUA_OPADD: return luai_numadd(L, v1, v2); + case LUA_OPSUB: return luai_numsub(L, v1, v2); + case LUA_OPMUL: return luai_nummul(L, v1, v2); + case LUA_OPDIV: return luai_numdiv(L, v1, v2); + case LUA_OPPOW: return luai_numpow(L, v1, v2); + case LUA_OPIDIV: return luai_numidiv(L, v1, v2); + case LUA_OPUNM: return luai_numunm(L, v1); + case LUA_OPMOD: return luaV_modf(L, v1, v2); + default: lua_assert(0); return 0; + } +} + + +int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, + TValue *res) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: + case LUA_OPBNOT: { /* operate only on integers */ + lua_Integer i1; lua_Integer i2; + if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) { + setivalue(res, intarith(L, op, i1, i2)); + return 1; + } + else return 0; /* fail */ + } + case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */ + lua_Number n1; lua_Number n2; + if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } + else return 0; /* fail */ + } + default: { /* other operations */ + lua_Number n1; lua_Number n2; + if (ttisinteger(p1) && ttisinteger(p2)) { + setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); + return 1; + } + else if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } + else return 0; /* fail */ + } + } +} + + +void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, + StkId res) { + if (!luaO_rawarith(L, op, p1, p2, s2v(res))) { + /* could not perform raw operation; try metamethod */ + luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); + } +} + + +int luaO_hexavalue (int c) { + if (lisdigit(c)) return c - '0'; + else return (ltolower(c) - 'a') + 10; +} + + +static int isneg (const char **s) { + if (**s == '-') { (*s)++; return 1; } + else if (**s == '+') (*s)++; + return 0; +} + + + +/* +** {================================================================== +** Lua's implementation for 'lua_strx2number' +** =================================================================== +*/ + +#if !defined(lua_strx2number) + +/* maximum number of significant digits to read (to avoid overflows + even with single floats) */ +#define MAXSIGDIG 30 + +/* +** convert a hexadecimal numeric string to a number, following +** C99 specification for 'strtod' +*/ +static lua_Number lua_strx2number (const char *s, char **endptr) { + int dot = lua_getlocaledecpoint(); + lua_Number r = 0.0; /* result (accumulator) */ + int sigdig = 0; /* number of significant digits */ + int nosigdig = 0; /* number of non-significant digits */ + int e = 0; /* exponent correction */ + int neg; /* 1 if number is negative */ + int hasdot = 0; /* true after seen a dot */ + *endptr = cast_charp(s); /* nothing is valid yet */ + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); /* check sign */ + if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ + return 0.0; /* invalid format (no '0x') */ + for (s += 2; ; s++) { /* skip '0x' and read numeral */ + if (*s == dot) { + if (hasdot) break; /* second dot? stop loop */ + else hasdot = 1; + } + else if (lisxdigit(cast_uchar(*s))) { + if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ + nosigdig++; + else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ + r = (r * cast_num(16.0)) + luaO_hexavalue(*s); + else e++; /* too many digits; ignore, but still count for exponent */ + if (hasdot) e--; /* decimal digit? correct exponent */ + } + else break; /* neither a dot nor a digit */ + } + if (nosigdig + sigdig == 0) /* no digits? */ + return 0.0; /* invalid format */ + *endptr = cast_charp(s); /* valid up to here */ + e *= 4; /* each digit multiplies/divides value by 2^4 */ + if (*s == 'p' || *s == 'P') { /* exponent part? */ + int exp1 = 0; /* exponent value */ + int neg1; /* exponent sign */ + s++; /* skip 'p' */ + neg1 = isneg(&s); /* sign */ + if (!lisdigit(cast_uchar(*s))) + return 0.0; /* invalid; must have at least one digit */ + while (lisdigit(cast_uchar(*s))) /* read exponent */ + exp1 = exp1 * 10 + *(s++) - '0'; + if (neg1) exp1 = -exp1; + e += exp1; + *endptr = cast_charp(s); /* valid up to here */ + } + if (neg) r = -r; + return l_mathop(ldexp)(r, e); +} + +#endif +/* }====================================================== */ + + +/* maximum length of a numeral to be converted to a number */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + +/* +** Convert string 's' to a Lua number (put in 'result'). Return NULL on +** fail or the address of the ending '\0' on success. ('mode' == 'x') +** means a hexadecimal numeral. +*/ +static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { + char *endptr; + *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ + : lua_str2number(s, &endptr); + if (endptr == s) return NULL; /* nothing recognized? */ + while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ + return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */ +} + + +/* +** Convert string 's' to a Lua number (put in 'result') handling the +** current locale. +** This function accepts both the current locale or a dot as the radix +** mark. If the conversion fails, it may mean number has a dot but +** locale accepts something else. In that case, the code copies 's' +** to a buffer (because 's' is read-only), changes the dot to the +** current locale radix mark, and tries to convert again. +** The variable 'mode' checks for special characters in the string: +** - 'n' means 'inf' or 'nan' (which should be rejected) +** - 'x' means a hexadecimal numeral +** - '.' just optimizes the search for the common case (no special chars) +*/ +static const char *l_str2d (const char *s, lua_Number *result) { + const char *endptr; + const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */ + int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; + if (mode == 'n') /* reject 'inf' and 'nan' */ + return NULL; + endptr = l_str2dloc(s, result, mode); /* try to convert */ + if (endptr == NULL) { /* failed? may be a different locale */ + char buff[L_MAXLENNUM + 1]; + const char *pdot = strchr(s, '.'); + if (strlen(s) > L_MAXLENNUM || pdot == NULL) + return NULL; /* string too long or no dot; fail */ + strcpy(buff, s); /* copy string to buffer */ + buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ + endptr = l_str2dloc(buff, result, mode); /* try again */ + if (endptr != NULL) + endptr = s + (endptr - buff); /* make relative to 's' */ + } + return endptr; +} + + +#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) +#define MAXLASTD cast_int(LUA_MAXINTEGER % 10) + +static const char *l_str2int (const char *s, lua_Integer *result) { + lua_Unsigned a = 0; + int empty = 1; + int neg; + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); + if (s[0] == '0' && + (s[1] == 'x' || s[1] == 'X')) { /* hex? */ + s += 2; /* skip '0x' */ + for (; lisxdigit(cast_uchar(*s)); s++) { + a = a * 16 + luaO_hexavalue(*s); + empty = 0; + } + } + else { /* decimal */ + for (; lisdigit(cast_uchar(*s)); s++) { + int d = *s - '0'; + if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ + return NULL; /* do not accept it (as integer) */ + a = a * 10 + d; + empty = 0; + } + } + while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ + if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ + else { + *result = l_castU2S((neg) ? 0u - a : a); + return s; + } +} + + +size_t luaO_str2num (const char *s, TValue *o) { + lua_Integer i; lua_Number n; + const char *e; + if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ + setivalue(o, i); + } + else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ + setfltvalue(o, n); + } + else + return 0; /* conversion failed */ + return (e - s) + 1; /* success; return string size */ +} + + +int luaO_utf8esc (char *buff, unsigned long x) { + int n = 1; /* number of bytes put in buffer (backwards) */ + lua_assert(x <= 0x7FFFFFFFu); + if (x < 0x80) /* ascii? */ + buff[UTF8BUFFSZ - 1] = cast_char(x); + else { /* need continuation bytes */ + unsigned int mfb = 0x3f; /* maximum that fits in first byte */ + do { /* add continuation bytes */ + buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f)); + x >>= 6; /* remove added bits */ + mfb >>= 1; /* now there is one less bit available in first byte */ + } while (x > mfb); /* still needs continuation byte? */ + buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */ + } + return n; +} + + +/* +** Maximum length of the conversion of a number to a string. Must be +** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. +** (For a long long int, this is 19 digits plus a sign and a final '\0', +** adding to 21. For a long double, it can go to a sign, 33 digits, +** the dot, an exponent letter, an exponent sign, 5 exponent digits, +** and a final '\0', adding to 43.) +*/ +#define MAXNUMBER2STR 44 + + +/* +** Convert a number object to a string, adding it to a buffer +*/ +static int tostringbuff (TValue *obj, char *buff) { + int len; + lua_assert(ttisnumber(obj)); + if (ttisinteger(obj)) + len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); + else { + len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); + if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ + buff[len++] = lua_getlocaledecpoint(); + buff[len++] = '0'; /* adds '.0' to result */ + } + } + return len; +} + + +/* +** Convert a number object to a Lua string, replacing the value at 'obj' +*/ +void luaO_tostring (lua_State *L, TValue *obj) { + char buff[MAXNUMBER2STR]; + int len = tostringbuff(obj, buff); + setsvalue(L, obj, luaS_newlstr(L, buff, len)); +} + + + + +/* +** {================================================================== +** 'luaO_pushvfstring' +** =================================================================== +*/ + +/* size for buffer space used by 'luaO_pushvfstring' */ +#define BUFVFS 200 + +/* buffer used by 'luaO_pushvfstring' */ +typedef struct BuffFS { + lua_State *L; + int pushed; /* number of string pieces already on the stack */ + int blen; /* length of partial string in 'space' */ + char space[BUFVFS]; /* holds last part of the result */ +} BuffFS; + + +/* +** Push given string to the stack, as part of the buffer, and +** join the partial strings in the stack into one. +*/ +static void pushstr (BuffFS *buff, const char *str, size_t l) { + lua_State *L = buff->L; + setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); + L->top++; /* may use one extra slot */ + buff->pushed++; + luaV_concat(L, buff->pushed); /* join partial results into one */ + buff->pushed = 1; +} + + +/* +** empty the buffer space into the stack +*/ +static void clearbuff (BuffFS *buff) { + pushstr(buff, buff->space, buff->blen); /* push buffer contents */ + buff->blen = 0; /* space now is empty */ +} + + +/* +** Get a space of size 'sz' in the buffer. If buffer has not enough +** space, empty it. 'sz' must fit in an empty buffer. +*/ +static char *getbuff (BuffFS *buff, int sz) { + lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS); + if (sz > BUFVFS - buff->blen) /* not enough space? */ + clearbuff(buff); + return buff->space + buff->blen; +} + + +#define addsize(b,sz) ((b)->blen += (sz)) + + +/* +** Add 'str' to the buffer. If string is larger than the buffer space, +** push the string directly to the stack. +*/ +static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { + if (slen <= BUFVFS) { /* does string fit into buffer? */ + char *bf = getbuff(buff, cast_int(slen)); + memcpy(bf, str, slen); /* add string to buffer */ + addsize(buff, cast_int(slen)); + } + else { /* string larger than buffer */ + clearbuff(buff); /* string comes after buffer's content */ + pushstr(buff, str, slen); /* push string */ + } +} + + +/* +** Add a number to the buffer. +*/ +static void addnum2buff (BuffFS *buff, TValue *num) { + char *numbuff = getbuff(buff, MAXNUMBER2STR); + int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ + addsize(buff, len); +} + + +/* +** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%' + conventional formats, plus Lua-specific '%I' and '%U' +*/ +const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + BuffFS buff; /* holds last part of the result */ + const char *e; /* points to next '%' */ + buff.pushed = buff.blen = 0; + buff.L = L; + while ((e = strchr(fmt, '%')) != NULL) { + addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */ + switch (*(e + 1)) { /* conversion specifier */ + case 's': { /* zero-terminated string */ + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + addstr2buff(&buff, s, strlen(s)); + break; + } + case 'c': { /* an 'int' as a character */ + char c = cast_uchar(va_arg(argp, int)); + addstr2buff(&buff, &c, sizeof(char)); + break; + } + case 'd': { /* an 'int' */ + TValue num; + setivalue(&num, va_arg(argp, int)); + addnum2buff(&buff, &num); + break; + } + case 'I': { /* a 'lua_Integer' */ + TValue num; + setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); + addnum2buff(&buff, &num); + break; + } + case 'f': { /* a 'lua_Number' */ + TValue num; + setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); + addnum2buff(&buff, &num); + break; + } + case 'p': { /* a pointer */ + const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */ + char *bf = getbuff(&buff, sz); + void *p = va_arg(argp, void *); + int len = lua_pointer2str(bf, sz, p); + addsize(&buff, len); + break; + } + case 'U': { /* a 'long' as a UTF-8 sequence */ + char bf[UTF8BUFFSZ]; + int len = luaO_utf8esc(bf, va_arg(argp, long)); + addstr2buff(&buff, bf + UTF8BUFFSZ - len, len); + break; + } + case '%': { + addstr2buff(&buff, "%", 1); + break; + } + default: { + luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", + *(e + 1)); + } + } + fmt = e + 2; /* skip '%' and the specifier */ + } + addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ + clearbuff(&buff); /* empty buffer into the stack */ + lua_assert(buff.pushed == 1); + return svalue(s2v(L->top - 1)); +} + + +const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + return msg; +} + +/* }================================================================== */ + + +#define RETS "..." +#define PRE "[string \"" +#define POS "\"]" + +#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) + +void luaO_chunkid (char *out, const char *source, size_t srclen) { + size_t bufflen = LUA_IDSIZE; /* free space in buffer */ + if (*source == '=') { /* 'literal' source */ + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); + else { /* truncate it */ + addstr(out, source + 1, bufflen - 1); + *out = '\0'; + } + } + else if (*source == '@') { /* file name */ + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); + else { /* add '...' before rest of name */ + addstr(out, RETS, LL(RETS)); + bufflen -= LL(RETS); + memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char)); + } + } + else { /* string; format as [string "source"] */ + const char *nl = strchr(source, '\n'); /* find first new line (if any) */ + addstr(out, PRE, LL(PRE)); /* add prefix */ + bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ + if (srclen < bufflen && nl == NULL) { /* small one-line source? */ + addstr(out, source, srclen); /* keep it */ + } + else { + if (nl != NULL) srclen = nl - source; /* stop at first newline */ + if (srclen > bufflen) srclen = bufflen; + addstr(out, source, srclen); + addstr(out, RETS, LL(RETS)); + } + memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); + } +} + diff --git a/Lua/lobject.h b/Lua/lobject.h new file mode 100644 index 00000000..a9d45785 --- /dev/null +++ b/Lua/lobject.h @@ -0,0 +1,788 @@ +/* +** $Id: lobject.h $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#include + + +#include "llimits.h" +#include "lua.h" + + +/* +** Extra types for collectable non-values +*/ +#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ +#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ + + +/* +** number of all possible types (including LUA_TNONE) +*/ +#define LUA_TOTALTYPES (LUA_TPROTO + 2) + + +/* +** tags for Tagged Values have the following use of bits: +** bits 0-3: actual tag (a LUA_T* constant) +** bits 4-5: variant bits +** bit 6: whether value is collectable +*/ + +/* add variant bits to a type */ +#define makevariant(t,v) ((t) | ((v) << 4)) + + + +/* +** Union of all Lua values +*/ +typedef union Value { + struct GCObject *gc; /* collectable objects */ + void *p; /* light userdata */ + lua_CFunction f; /* light C functions */ + lua_Integer i; /* integer numbers */ + lua_Number n; /* float numbers */ +} Value; + + +/* +** Tagged Values. This is the basic representation of values in Lua: +** an actual value plus a tag with its type. +*/ + +#define TValuefields Value value_; lu_byte tt_ + +typedef struct TValue { + TValuefields; +} TValue; + + +#define val_(o) ((o)->value_) +#define valraw(o) (&val_(o)) + + +/* raw type tag of a TValue */ +#define rawtt(o) ((o)->tt_) + +/* tag with no variants (bits 0-3) */ +#define novariant(t) ((t) & 0x0F) + +/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ +#define withvariant(t) ((t) & 0x3F) +#define ttypetag(o) withvariant(rawtt(o)) + +/* type of a TValue */ +#define ttype(o) (novariant(rawtt(o))) + + +/* Macros to test type */ +#define checktag(o,t) (rawtt(o) == (t)) +#define checktype(o,t) (ttype(o) == (t)) + + +/* Macros for internal tests */ + +/* collectable object has the same tag as the original value */ +#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt) + +/* +** Any value being manipulated by the program either is non +** collectable, or the collectable object has the right tag +** and it is not dead. The option 'L == NULL' allows other +** macros using this one to be used where L is not available. +*/ +#define checkliveness(L,obj) \ + ((void)L, lua_longassert(!iscollectable(obj) || \ + (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))) + + +/* Macros to set values */ + +/* set a value's tag */ +#define settt_(o,t) ((o)->tt_=(t)) + + +/* main macro to copy values (from 'obj1' to 'obj2') */ +#define setobj(L,obj1,obj2) \ + { TValue *io1=(obj1); const TValue *io2=(obj2); \ + io1->value_ = io2->value_; settt_(io1, io2->tt_); \ + checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); } + +/* +** Different types of assignments, according to source and destination. +** (They are mostly equal now, but may be different in the future.) +*/ + +/* from stack to stack */ +#define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2)) +/* to stack (not from same stack) */ +#define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2) +/* from table to same table */ +#define setobjt2t setobj +/* to new object */ +#define setobj2n setobj +/* to table */ +#define setobj2t setobj + + +/* +** Entries in the Lua stack +*/ +typedef union StackValue { + TValue val; +} StackValue; + + +/* index to stack elements */ +typedef StackValue *StkId; + +/* convert a 'StackValue' to a 'TValue' */ +#define s2v(o) (&(o)->val) + + + +/* +** {================================================================== +** Nil +** =================================================================== +*/ + +/* Standard nil */ +#define LUA_VNIL makevariant(LUA_TNIL, 0) + +/* Empty slot (which might be different from a slot containing nil) */ +#define LUA_VEMPTY makevariant(LUA_TNIL, 1) + +/* Value returned for a key not found in a table (absent key) */ +#define LUA_VABSTKEY makevariant(LUA_TNIL, 2) + + +/* macro to test for (any kind of) nil */ +#define ttisnil(v) checktype((v), LUA_TNIL) + + +/* macro to test for a standard nil */ +#define ttisstrictnil(o) checktag((o), LUA_VNIL) + + +#define setnilvalue(obj) settt_(obj, LUA_VNIL) + + +#define isabstkey(v) checktag((v), LUA_VABSTKEY) + + +/* +** macro to detect non-standard nils (used only in assertions) +*/ +#define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v)) + + +/* +** By default, entries with any kind of nil are considered empty. +** (In any definition, values associated with absent keys must also +** be accepted as empty.) +*/ +#define isempty(v) ttisnil(v) + + +/* macro defining a value corresponding to an absent key */ +#define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY + + +/* mark an entry as empty */ +#define setempty(v) settt_(v, LUA_VEMPTY) + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Booleans +** =================================================================== +*/ + + +#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0) +#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1) + +#define ttisboolean(o) checktype((o), LUA_TBOOLEAN) +#define ttisfalse(o) checktag((o), LUA_VFALSE) +#define ttistrue(o) checktag((o), LUA_VTRUE) + + +#define l_isfalse(o) (ttisfalse(o) || ttisnil(o)) + + +#define setbfvalue(obj) settt_(obj, LUA_VFALSE) +#define setbtvalue(obj) settt_(obj, LUA_VTRUE) + +/* }================================================================== */ + + +/* +** {================================================================== +** Threads +** =================================================================== +*/ + +#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0) + +#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD)) + +#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) + +#define setthvalue(L,obj,x) \ + { TValue *io = (obj); lua_State *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \ + checkliveness(L,io); } + +#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t) + +/* }================================================================== */ + + +/* +** {================================================================== +** Collectable Objects +** =================================================================== +*/ + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked + + +/* Common type for all collectable objects */ +typedef struct GCObject { + CommonHeader; +} GCObject; + + +/* Bit mark for collectable types */ +#define BIT_ISCOLLECTABLE (1 << 6) + +#define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE) + +/* mark a tag as collectable */ +#define ctb(t) ((t) | BIT_ISCOLLECTABLE) + +#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) + +#define gcvalueraw(v) ((v).gc) + +#define setgcovalue(L,obj,x) \ + { TValue *io = (obj); GCObject *i_g=(x); \ + val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Numbers +** =================================================================== +*/ + +/* Variant tags for numbers */ +#define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */ +#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */ + +#define ttisnumber(o) checktype((o), LUA_TNUMBER) +#define ttisfloat(o) checktag((o), LUA_VNUMFLT) +#define ttisinteger(o) checktag((o), LUA_VNUMINT) + +#define nvalue(o) check_exp(ttisnumber(o), \ + (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o))) +#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n) +#define ivalue(o) check_exp(ttisinteger(o), val_(o).i) + +#define fltvalueraw(v) ((v).n) +#define ivalueraw(v) ((v).i) + +#define setfltvalue(obj,x) \ + { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); } + +#define chgfltvalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); } + +#define setivalue(obj,x) \ + { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); } + +#define chgivalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Strings +** =================================================================== +*/ + +/* Variant tags for strings */ +#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */ +#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */ + +#define ttisstring(o) checktype((o), LUA_TSTRING) +#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR)) +#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR)) + +#define tsvalueraw(v) (gco2ts((v).gc)) + +#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc)) + +#define setsvalue(L,obj,x) \ + { TValue *io = (obj); TString *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ + checkliveness(L,io); } + +/* set a string to the stack */ +#define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s) + +/* set a string to a new object */ +#define setsvalue2n setsvalue + + +/* +** Header for a string value. +*/ +typedef struct TString { + CommonHeader; + lu_byte extra; /* reserved words for short strings; "has hash" for longs */ + lu_byte shrlen; /* length for short strings */ + unsigned int hash; + union { + size_t lnglen; /* length for long strings */ + struct TString *hnext; /* linked list for hash table */ + } u; + char contents[1]; +} TString; + + + +/* +** Get the actual string (array of bytes) from a 'TString'. +*/ +#define getstr(ts) ((ts)->contents) + + +/* get the actual string (array of bytes) from a Lua value */ +#define svalue(o) getstr(tsvalue(o)) + +/* get string length from 'TString *s' */ +#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen) + +/* get string length from 'TValue *o' */ +#define vslen(o) tsslen(tsvalue(o)) + +/* }================================================================== */ + + +/* +** {================================================================== +** Userdata +** =================================================================== +*/ + + +/* +** Light userdata should be a variant of userdata, but for compatibility +** reasons they are also different types. +*/ +#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0) + +#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0) + +#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA) +#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA)) + +#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) +#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc)) + +#define pvalueraw(v) ((v).p) + +#define setpvalue(obj,x) \ + { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); } + +#define setuvalue(L,obj,x) \ + { TValue *io = (obj); Udata *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \ + checkliveness(L,io); } + + +/* Ensures that addresses after this type are always fully aligned. */ +typedef union UValue { + TValue uv; + LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */ +} UValue; + + +/* +** Header for userdata with user values; +** memory area follows the end of this structure. +*/ +typedef struct Udata { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + GCObject *gclist; + UValue uv[1]; /* user values */ +} Udata; + + +/* +** Header for userdata with no user values. These userdata do not need +** to be gray during GC, and therefore do not need a 'gclist' field. +** To simplify, the code always use 'Udata' for both kinds of userdata, +** making sure it never accesses 'gclist' on userdata with no user values. +** This structure here is used only to compute the correct size for +** this representation. (The 'bindata' field in its end ensures correct +** alignment for binary data following this header.) +*/ +typedef struct Udata0 { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + union {LUAI_MAXALIGN;} bindata; +} Udata0; + + +/* compute the offset of the memory area of a userdata */ +#define udatamemoffset(nuv) \ + ((nuv) == 0 ? offsetof(Udata0, bindata) \ + : offsetof(Udata, uv) + (sizeof(UValue) * (nuv))) + +/* get the address of the memory block inside 'Udata' */ +#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue)) + +/* compute the size of a userdata */ +#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb)) + +/* }================================================================== */ + + +/* +** {================================================================== +** Prototypes +** =================================================================== +*/ + +#define LUA_VPROTO makevariant(LUA_TPROTO, 0) + + +/* +** Description of an upvalue for function prototypes +*/ +typedef struct Upvaldesc { + TString *name; /* upvalue name (for debug information) */ + lu_byte instack; /* whether it is in stack (register) */ + lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ + lu_byte kind; /* kind of corresponding variable */ +} Upvaldesc; + + +/* +** Description of a local variable for function prototypes +** (used for debug information) +*/ +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + +/* +** Associates the absolute line source for a given instruction ('pc'). +** The array 'lineinfo' gives, for each instruction, the difference in +** lines from the previous instruction. When that difference does not +** fit into a byte, Lua saves the absolute line for that instruction. +** (Lua also saves the absolute line periodically, to speed up the +** computation of a line number: we can use binary search in the +** absolute-line array, but we must traverse the 'lineinfo' array +** linearly to compute a line.) +*/ +typedef struct AbsLineInfo { + int pc; + int line; +} AbsLineInfo; + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + lu_byte numparams; /* number of fixed (named) parameters */ + lu_byte is_vararg; + lu_byte maxstacksize; /* number of registers needed by this function */ + int sizeupvalues; /* size of 'upvalues' */ + int sizek; /* size of 'k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of 'p' */ + int sizelocvars; + int sizeabslineinfo; /* size of 'abslineinfo' */ + int linedefined; /* debug information */ + int lastlinedefined; /* debug information */ + TValue *k; /* constants used by the function */ + Instruction *code; /* opcodes */ + struct Proto **p; /* functions defined inside the function */ + Upvaldesc *upvalues; /* upvalue information */ + ls_byte *lineinfo; /* information about source lines (debug information) */ + AbsLineInfo *abslineinfo; /* idem */ + LocVar *locvars; /* information about local variables (debug information) */ + TString *source; /* used for debug information */ + GCObject *gclist; +} Proto; + +/* }================================================================== */ + + +/* +** {================================================================== +** Closures +** =================================================================== +*/ + +#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0) + + +/* Variant tags for functions */ +#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */ +#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */ +#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */ + +#define ttisfunction(o) checktype(o, LUA_TFUNCTION) +#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_VLCL) +#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL)) +#define ttislcf(o) checktag((o), LUA_VLCF) +#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL)) + +#define isLfunction(o) ttisLclosure(o) + +#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc)) +#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc)) +#define fvalue(o) check_exp(ttislcf(o), val_(o).f) +#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc)) + +#define fvalueraw(v) ((v).f) + +#define setclLvalue(L,obj,x) \ + { TValue *io = (obj); LClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \ + checkliveness(L,io); } + +#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl) + +#define setfvalue(obj,x) \ + { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); } + +#define setclCvalue(L,obj,x) \ + { TValue *io = (obj); CClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \ + checkliveness(L,io); } + + +/* +** Upvalues for Lua closures +*/ +typedef struct UpVal { + CommonHeader; + lu_byte tbc; /* true if it represents a to-be-closed variable */ + TValue *v; /* points to stack or to its own value */ + union { + struct { /* (when open) */ + struct UpVal *next; /* linked list */ + struct UpVal **previous; + } open; + TValue value; /* the value (when closed) */ + } u; +} UpVal; + + + +#define ClosureHeader \ + CommonHeader; lu_byte nupvalues; GCObject *gclist + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; /* list of upvalues */ +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; /* list of upvalues */ +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define getproto(o) (clLvalue(o)->p) + +/* }================================================================== */ + + +/* +** {================================================================== +** Tables +** =================================================================== +*/ + +#define LUA_VTABLE makevariant(LUA_TTABLE, 0) + +#define ttistable(o) checktag((o), ctb(LUA_VTABLE)) + +#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) + +#define sethvalue(L,obj,x) \ + { TValue *io = (obj); Table *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \ + checkliveness(L,io); } + +#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h) + + +/* +** Nodes for Hash tables: A pack of two TValue's (key-value pairs) +** plus a 'next' field to link colliding entries. The distribution +** of the key's fields ('key_tt' and 'key_val') not forming a proper +** 'TValue' allows for a smaller size for 'Node' both in 4-byte +** and 8-byte alignments. +*/ +typedef union Node { + struct NodeKey { + TValuefields; /* fields for value */ + lu_byte key_tt; /* key type */ + int next; /* for chaining */ + Value key_val; /* key value */ + } u; + TValue i_val; /* direct access to node's value as a proper 'TValue' */ +} Node; + + +/* copy a value into a key */ +#define setnodekey(L,node,obj) \ + { Node *n_=(node); const TValue *io_=(obj); \ + n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \ + checkliveness(L,io_); } + + +/* copy a value from a key */ +#define getnodekey(L,obj,node) \ + { TValue *io_=(obj); const Node *n_=(node); \ + io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \ + checkliveness(L,io_); } + + +/* +** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the +** real size of 'array'. Otherwise, the real size of 'array' is the +** smallest power of two not smaller than 'alimit' (or zero iff 'alimit' +** is zero); 'alimit' is then used as a hint for #t. +*/ + +#define BITRAS (1 << 7) +#define isrealasize(t) (!((t)->flags & BITRAS)) +#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS)) +#define setnorealasize(t) ((t)->flags |= BITRAS) + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<

u.key_tt) +#define keyval(node) ((node)->u.key_val) + +#define keyisnil(node) (keytt(node) == LUA_TNIL) +#define keyisinteger(node) (keytt(node) == LUA_VNUMINT) +#define keyival(node) (keyval(node).i) +#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR)) +#define keystrval(node) (gco2ts(keyval(node).gc)) + +#define setnilkey(node) (keytt(node) = LUA_TNIL) + +#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE) + +#define gckey(n) (keyval(n).gc) +#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL) + + +/* +** Use a "nil table" to mark dead keys in a table. Those keys serve +** to keep space for removed entries, which may still be part of +** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE +** set, so these values are considered not collectable and are different +** from any valid value. +*/ +#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL) + +/* }================================================================== */ + + + +/* +** 'module' operation for hashing (size is always a power of 2) +*/ +#define lmod(s,size) \ + (check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1))))) + + +#define twoto(x) (1<<(x)) +#define sizenode(t) (twoto((t)->lsizenode)) + + +/* size of buffer for 'luaO_utf8esc' function */ +#define UTF8BUFFSZ 8 + +LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); +LUAI_FUNC int luaO_ceillog2 (unsigned int x); +LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, + const TValue *p2, TValue *res); +LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1, + const TValue *p2, StkId res); +LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); +LUAI_FUNC int luaO_hexavalue (int c); +LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen); + + +#endif + diff --git a/Lua/lopcodes.c b/Lua/lopcodes.c new file mode 100644 index 00000000..c67aa227 --- /dev/null +++ b/Lua/lopcodes.c @@ -0,0 +1,104 @@ +/* +** $Id: lopcodes.c $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#define lopcodes_c +#define LUA_CORE + +#include "lprefix.h" + + +#include "lopcodes.h" + + +/* ORDER OP */ + +LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* MM OT IT T A mode opcode */ + opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */ + ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */ + ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADFALSE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */ + ,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */ + ,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */ + ,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */ + ,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */ + ,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */ + ,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ + ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ + ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ +}; + diff --git a/Lua/lopcodes.h b/Lua/lopcodes.h new file mode 100644 index 00000000..122e5d21 --- /dev/null +++ b/Lua/lopcodes.h @@ -0,0 +1,392 @@ +/* +** $Id: lopcodes.h $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +#include "llimits.h" + + +/*=========================================================================== + We assume that instructions are unsigned 32-bit integers. + All instructions have an opcode in the first 7 bits. + Instructions can have the following formats: + + 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 + 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +iABC C(8) | B(8) |k| A(8) | Op(7) | +iABx Bx(17) | A(8) | Op(7) | +iAsBx sBx (signed)(17) | A(8) | Op(7) | +iAx Ax(25) | Op(7) | +isJ sJ(25) | Op(7) | + + A signed argument is represented in excess K: the represented value is + the written unsigned value minus K, where K is half the maximum for the + corresponding unsigned argument. +===========================================================================*/ + + +enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 8 +#define SIZE_B 8 +#define SIZE_Bx (SIZE_C + SIZE_B + 1) +#define SIZE_A 8 +#define SIZE_Ax (SIZE_Bx + SIZE_A) +#define SIZE_sJ (SIZE_Bx + SIZE_A) + +#define SIZE_OP 7 + +#define POS_OP 0 + +#define POS_A (POS_OP + SIZE_OP) +#define POS_k (POS_A + SIZE_A) +#define POS_B (POS_k + 1) +#define POS_C (POS_B + SIZE_B) + +#define POS_Bx POS_k + +#define POS_Ax POS_A + +#define POS_sJ POS_A + + +/* +** limits for opcode arguments. +** we use (signed) 'int' to manipulate most arguments, +** so they must fit in ints. +*/ + +/* Check whether type 'int' has at least 'b' bits ('b' < 32) */ +#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1) + + +#if L_INTHASBITS(SIZE_Bx) +#define MAXARG_Bx ((1<>1) /* 'sBx' is signed */ + + +#if L_INTHASBITS(SIZE_Ax) +#define MAXARG_Ax ((1<> 1) + + +#define MAXARG_A ((1<> 1) + +#define int2sC(i) ((i) + OFFSET_sC) +#define sC2int(i) ((i) - OFFSET_sC) + + +/* creates a mask with 'n' 1 bits at position 'p' */ +#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p)) + +/* creates a mask with 'n' 0 bits at position 'p' */ +#define MASK0(n,p) (~MASK1(n,p)) + +/* +** the following macros help to manipulate instructions +*/ + +#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<>(pos)) & MASK1(size,0))) +#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ + ((cast(Instruction, v)<> sC */ +OP_SHLI,/* A B sC R[A] := sC << R[B] */ + +OP_ADD,/* A B C R[A] := R[B] + R[C] */ +OP_SUB,/* A B C R[A] := R[B] - R[C] */ +OP_MUL,/* A B C R[A] := R[B] * R[C] */ +OP_MOD,/* A B C R[A] := R[B] % R[C] */ +OP_POW,/* A B C R[A] := R[B] ^ R[C] */ +OP_DIV,/* A B C R[A] := R[B] / R[C] */ +OP_IDIV,/* A B C R[A] := R[B] // R[C] */ + +OP_BAND,/* A B C R[A] := R[B] & R[C] */ +OP_BOR,/* A B C R[A] := R[B] | R[C] */ +OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ +OP_SHL,/* A B C R[A] := R[B] << R[C] */ +OP_SHR,/* A B C R[A] := R[B] >> R[C] */ + +OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */ +OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ +OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ + +OP_UNM,/* A B R[A] := -R[B] */ +OP_BNOT,/* A B R[A] := ~R[B] */ +OP_NOT,/* A B R[A] := not R[B] */ +OP_LEN,/* A B R[A] := length of R[B] */ + +OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ + +OP_CLOSE,/* A close all upvalues >= R[A] */ +OP_TBC,/* A mark variable A "to be closed" */ +OP_JMP,/* sJ pc += sJ */ +OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */ +OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */ +OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */ + +OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */ +OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */ +OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */ +OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */ +OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ +OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ + +OP_TEST,/* A k if (not R[A] == k) then pc++ */ +OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */ + +OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ +OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ + +OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */ +OP_RETURN0,/* return */ +OP_RETURN1,/* A return R[A] */ + +OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */ +OP_FORPREP,/* A Bx ; + if not to run then pc+=Bx+1; */ + +OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */ +OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ +OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ + +OP_SETLIST,/* A B C k R[A][(C-1)*FPF+i] := R[A+i], 1 <= i <= B */ + +OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ + +OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ + +OP_VARARGPREP,/*A (adjust vararg parameters) */ + +OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ +} OpCode; + + +#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1) + + + +/*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then + 'top' is set to last_result+1, so next open instruction (OP_CALL, + OP_RETURN*, OP_SETLIST) may use 'top'. + + (*) In OP_VARARG, if (C == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to 'top'. + + (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always + OP_EXTRAARG. + + (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then + real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the + bits of C). + + (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a + power of 2) plus 1, or zero for size zero. If not k, the array size + is C. Otherwise, the array size is EXTRAARG _ C. + + (*) For comparisons, k specifies what condition the test should accept + (true or false). + + (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped + (the constant is the first operand). + + (*) All 'skips' (pc++) assume that next instruction is a jump. + + (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the + function builds upvalues, which may need to be closed. C > 0 means + the function is vararg, so that its 'func' must be corrected before + returning; in this case, (C - 1) is its number of fixed parameters. + + (*) In comparisons with an immediate operand, C signals whether the + original operand was a float. (It must be corrected in case of + metamethods.) + +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-2: op mode +** bit 3: instruction set register A +** bit 4: operator is a test (next instruction must be a jump) +** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0) +** bit 6: instruction sets 'L->top' for next instruction (when C == 0) +** bit 7: instruction is an MM instruction (call a metamethod) +*/ + +LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 3)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 4)) +#define testITMode(m) (luaP_opmodes[m] & (1 << 5)) +#define testOTMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) + +/* "out top" (set top for next instruction) */ +#define isOT(i) \ + ((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \ + GET_OPCODE(i) == OP_TAILCALL) + +/* "in top" (uses top from previous instruction) */ +#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0) + +#define opmode(mm,ot,it,t,a,m) \ + (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 50 + +#endif diff --git a/Lua/lopnames.h b/Lua/lopnames.h new file mode 100644 index 00000000..965cec9b --- /dev/null +++ b/Lua/lopnames.h @@ -0,0 +1,103 @@ +/* +** $Id: lopnames.h $ +** Opcode names +** See Copyright Notice in lua.h +*/ + +#if !defined(lopnames_h) +#define lopnames_h + +#include + + +/* ORDER OP */ + +static const char *const opnames[] = { + "MOVE", + "LOADI", + "LOADF", + "LOADK", + "LOADKX", + "LOADFALSE", + "LFALSESKIP", + "LOADTRUE", + "LOADNIL", + "GETUPVAL", + "SETUPVAL", + "GETTABUP", + "GETTABLE", + "GETI", + "GETFIELD", + "SETTABUP", + "SETTABLE", + "SETI", + "SETFIELD", + "NEWTABLE", + "SELF", + "ADDI", + "ADDK", + "SUBK", + "MULK", + "MODK", + "POWK", + "DIVK", + "IDIVK", + "BANDK", + "BORK", + "BXORK", + "SHRI", + "SHLI", + "ADD", + "SUB", + "MUL", + "MOD", + "POW", + "DIV", + "IDIV", + "BAND", + "BOR", + "BXOR", + "SHL", + "SHR", + "MMBIN", + "MMBINI", + "MMBINK", + "UNM", + "BNOT", + "NOT", + "LEN", + "CONCAT", + "CLOSE", + "TBC", + "JMP", + "EQ", + "LT", + "LE", + "EQK", + "EQI", + "LTI", + "LEI", + "GTI", + "GEI", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "RETURN0", + "RETURN1", + "FORLOOP", + "FORPREP", + "TFORPREP", + "TFORCALL", + "TFORLOOP", + "SETLIST", + "CLOSURE", + "VARARG", + "VARARGPREP", + "EXTRAARG", + NULL +}; + +#endif + diff --git a/Lua/loslib.c b/Lua/loslib.c new file mode 100644 index 00000000..e65e188b --- /dev/null +++ b/Lua/loslib.c @@ -0,0 +1,430 @@ +/* +** $Id: loslib.c $ +** Standard Operating System library +** See Copyright Notice in lua.h +*/ + +#define loslib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* +** {================================================================== +** List of valid conversion specifiers for the 'strftime' function; +** options are grouped by length; group of length 2 start with '||'. +** =================================================================== +*/ +#if !defined(LUA_STRFTIMEOPTIONS) /* { */ + +/* options for ANSI C 89 (only 1-char options) */ +#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" + +/* options for ISO C 99 and POSIX */ +#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ + "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ + +/* options for Windows */ +#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ + "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ + +#if defined(LUA_USE_WINDOWS) +#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN +#elif defined(LUA_USE_C89) +#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 +#else /* C99 specification */ +#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 +#endif + +#endif /* } */ +/* }================================================================== */ + + +/* +** {================================================================== +** Configuration for time-related stuff +** =================================================================== +*/ + +/* +** type to represent time_t in Lua +*/ +#if !defined(LUA_NUMTIME) /* { */ + +#define l_timet lua_Integer +#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) +#define l_gettime(L,arg) luaL_checkinteger(L, arg) + +#else /* }{ */ + +#define l_timet lua_Number +#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) +#define l_gettime(L,arg) luaL_checknumber(L, arg) + +#endif /* } */ + + +#if !defined(l_gmtime) /* { */ +/* +** By default, Lua uses gmtime/localtime, except when POSIX is available, +** where it uses gmtime_r/localtime_r +*/ + +#if defined(LUA_USE_POSIX) /* { */ + +#define l_gmtime(t,r) gmtime_r(t,r) +#define l_localtime(t,r) localtime_r(t,r) + +#else /* }{ */ + +/* ISO C definitions */ +#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) +#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) + +#endif /* } */ + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Configuration for 'tmpnam': +** By default, Lua uses tmpnam except when POSIX is available, where +** it uses mkstemp. +** =================================================================== +*/ +#if !defined(lua_tmpnam) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include + +#define LUA_TMPNAMBUFSIZE 32 + +#if !defined(LUA_TMPNAMTEMPLATE) +#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" +#endif + +#define lua_tmpnam(b,e) { \ + strcpy(b, LUA_TMPNAMTEMPLATE); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else /* }{ */ + +/* ISO C definitions */ +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } + +#endif /* } */ + +#endif /* } */ +/* }================================================================== */ + + + +static int os_execute (lua_State *L) { + const char *cmd = luaL_optstring(L, 1, NULL); + int stat; + errno = 0; + stat = system(cmd); + if (cmd != NULL) + return luaL_execresult(L, stat); + else { + lua_pushboolean(L, stat); /* true if there is a shell */ + return 1; + } +} + + +static int os_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return luaL_fileresult(L, remove(filename) == 0, filename); +} + + +static int os_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); +} + + +static int os_tmpname (lua_State *L) { + char buff[LUA_TMPNAMBUFSIZE]; + int err; + lua_tmpnam(buff, err); + if (err) + return luaL_error(L, "unable to generate a unique filename"); + lua_pushstring(L, buff); + return 1; +} + + +static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int os_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +/* +** About the overflow check: an overflow cannot occur when time +** is represented by a lua_Integer, because either lua_Integer is +** large enough to represent all int fields or it is not large enough +** to represent a time that cause a field to overflow. However, if +** times are represented as doubles and lua_Integer is int, then the +** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 +** to compute the year. +*/ +static void setfield (lua_State *L, const char *key, int value, int delta) { + #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) + if (value > LUA_MAXINTEGER - delta) + luaL_error(L, "field '%s' is out-of-bound", key); + #endif + lua_pushinteger(L, (lua_Integer)value + delta); + lua_setfield(L, -2, key); +} + + +static void setboolfield (lua_State *L, const char *key, int value) { + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + + +/* +** Set all fields from structure 'tm' in the table on top of the stack +*/ +static void setallfields (lua_State *L, struct tm *stm) { + setfield(L, "year", stm->tm_year, 1900); + setfield(L, "month", stm->tm_mon, 1); + setfield(L, "day", stm->tm_mday, 0); + setfield(L, "hour", stm->tm_hour, 0); + setfield(L, "min", stm->tm_min, 0); + setfield(L, "sec", stm->tm_sec, 0); + setfield(L, "yday", stm->tm_yday, 1); + setfield(L, "wday", stm->tm_wday, 1); + setboolfield(L, "isdst", stm->tm_isdst); +} + + +static int getboolfield (lua_State *L, const char *key) { + int res; + res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d, int delta) { + int isnum; + int t = lua_getfield(L, -1, key); /* get field and its type */ + lua_Integer res = lua_tointegerx(L, -1, &isnum); + if (!isnum) { /* field is not an integer? */ + if (t != LUA_TNIL) /* some other value? */ + return luaL_error(L, "field '%s' is not an integer", key); + else if (d < 0) /* absent field; no default? */ + return luaL_error(L, "field '%s' missing in date table", key); + res = d; + } + else { + /* unsigned avoids overflow when lua_Integer has 32 bits */ + if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta + : (lua_Integer)INT_MIN + delta <= res)) + return luaL_error(L, "field '%s' is out-of-bound", key); + res -= delta; + } + lua_pop(L, 1); + return (int)res; +} + + +static const char *checkoption (lua_State *L, const char *conv, + ptrdiff_t convlen, char *buff) { + const char *option = LUA_STRFTIMEOPTIONS; + int oplen = 1; /* length of options being checked */ + for (; *option != '\0' && oplen <= convlen; option += oplen) { + if (*option == '|') /* next block? */ + oplen++; /* will check options with next length (+1) */ + else if (memcmp(conv, option, oplen) == 0) { /* match? */ + memcpy(buff, conv, oplen); /* copy valid option to buffer */ + buff[oplen] = '\0'; + return conv + oplen; /* return next item */ + } + } + luaL_argerror(L, 1, + lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); + return conv; /* to avoid warnings */ +} + + +static time_t l_checktime (lua_State *L, int arg) { + l_timet t = l_gettime(L, arg); + luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); + return (time_t)t; +} + + +/* maximum size for an individual 'strftime' item */ +#define SIZETIMEFMT 250 + + +static int os_date (lua_State *L) { + size_t slen; + const char *s = luaL_optlstring(L, 1, "%c", &slen); + time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); + const char *se = s + slen; /* 's' end */ + struct tm tmr, *stm; + if (*s == '!') { /* UTC? */ + stm = l_gmtime(&t, &tmr); + s++; /* skip '!' */ + } + else + stm = l_localtime(&t, &tmr); + if (stm == NULL) /* invalid date? */ + return luaL_error(L, + "date result cannot be represented in this installation"); + if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setallfields(L, stm); + } + else { + char cc[4]; /* buffer for individual conversion specifiers */ + luaL_Buffer b; + cc[0] = '%'; + luaL_buffinit(L, &b); + while (s < se) { + if (*s != '%') /* not a conversion specifier? */ + luaL_addchar(&b, *s++); + else { + size_t reslen; + char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); + s++; /* skip '%' */ + s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ + reslen = strftime(buff, SIZETIMEFMT, cc, stm); + luaL_addsize(&b, reslen); + } + } + luaL_pushresult(&b); + } + return 1; +} + + +static int os_time (lua_State *L) { + time_t t; + if (lua_isnoneornil(L, 1)) /* called without args? */ + t = time(NULL); /* get current time */ + else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_year = getfield(L, "year", -1, 1900); + ts.tm_mon = getfield(L, "month", -1, 1); + ts.tm_mday = getfield(L, "day", -1, 0); + ts.tm_hour = getfield(L, "hour", 12, 0); + ts.tm_min = getfield(L, "min", 0, 0); + ts.tm_sec = getfield(L, "sec", 0, 0); + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + setallfields(L, &ts); /* update fields with normalized values */ + } + if (t != (time_t)(l_timet)t || t == (time_t)(-1)) + return luaL_error(L, + "time result cannot be represented in this installation"); + l_pushtime(L, t); + return 1; +} + + +static int os_difftime (lua_State *L) { + time_t t1 = l_checktime(L, 1); + time_t t2 = l_checktime(L, 2); + lua_pushnumber(L, (lua_Number)difftime(t1, t2)); + return 1; +} + +/* }====================================================== */ + + +static int os_setlocale (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = luaL_optstring(L, 1, NULL); + int op = luaL_checkoption(L, 2, "all", catnames); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int os_exit (lua_State *L) { + int status; + if (lua_isboolean(L, 1)) + status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); + else + status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); + if (lua_toboolean(L, 2)) + lua_close(L); + if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ + return 0; +} + + +static const luaL_Reg syslib[] = { + {"clock", os_clock}, + {"date", os_date}, + {"difftime", os_difftime}, + {"execute", os_execute}, + {"exit", os_exit}, + {"getenv", os_getenv}, + {"remove", os_remove}, + {"rename", os_rename}, + {"setlocale", os_setlocale}, + {"time", os_time}, + {"tmpname", os_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUAMOD_API int luaopen_os (lua_State *L) { + luaL_newlib(L, syslib); + return 1; +} + diff --git a/Lua/lparser.c b/Lua/lparser.c new file mode 100644 index 00000000..bc7d9a4f --- /dev/null +++ b/Lua/lparser.c @@ -0,0 +1,1996 @@ +/* +** $Id: lparser.c $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#define lparser_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" + + + +/* maximum number of local variables per function (must be smaller + than 250, due to the bytecode format) */ +#define MAXVARS 200 + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + + +/* because all strings are unified by the scanner, the parser + can use pointer equality for string equality */ +#define eqstr(a,b) ((a) == (b)) + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + int firstlabel; /* index of first label in this block */ + int firstgoto; /* index of first pending goto in this block */ + lu_byte nactvar; /* # active locals outside the block */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isloop; /* true if 'block' is a loop */ + lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void statement (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static l_noret error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token))); +} + + +static l_noret errorlimit (FuncState *fs, int limit, const char *what) { + lua_State *L = fs->ls->L; + const char *msg; + int line = fs->f->linedefined; + const char *where = (line == 0) + ? "main function" + : luaO_pushfstring(L, "function at line %d", line); + msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s", + what, limit, where); + luaX_syntaxerror(fs->ls, msg); +} + + +static void checklimit (FuncState *fs, int v, int l, const char *what) { + if (v > l) errorlimit(fs, l, what); +} + + +/* +** Test whether next token is 'c'; if so, skip it. +*/ +static int testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +/* +** Check that next token is 'c'. +*/ +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + + +/* +** Check that next token is 'c' and skip it. +*/ +static void checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + +/* +** Check that next token is 'what' and skip it. In case of error, +** raise an error that the expected 'what' should match a 'who' +** in line 'where' (if that is not the current line). +*/ +static void check_match (LexState *ls, int what, int who, int where) { + if (unlikely(!testnext(ls, what))) { + if (where == ls->linenumber) /* all in the same line? */ + error_expected(ls, what); /* do not need a complex message */ + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + "%s expected (to close %s at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.info = i; +} + + +static void codestring (expdesc *e, TString *s) { + e->f = e->t = NO_JUMP; + e->k = VKSTR; + e->u.strval = s; +} + + +static void codename (LexState *ls, expdesc *e) { + codestring(e, str_checkname(ls)); +} + + +/* +** Register a new local variable in the active 'Proto' (for debug +** information). +*/ +static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) { + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars, + LocVar, SHRT_MAX, "local variables"); + while (oldsize < f->sizelocvars) + f->locvars[oldsize++].varname = NULL; + f->locvars[fs->ndebugvars].varname = varname; + f->locvars[fs->ndebugvars].startpc = fs->pc; + luaC_objbarrier(ls->L, f, varname); + return fs->ndebugvars++; +} + + +/* +** Create a new local variable with the given 'name'. Return its index +** in the function. +*/ +static int new_localvar (LexState *ls, TString *name) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Dyndata *dyd = ls->dyd; + Vardesc *var; + checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, + MAXVARS, "local variables"); + luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, + dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); + var = &dyd->actvar.arr[dyd->actvar.n++]; + var->vd.kind = VDKREG; /* default */ + var->vd.name = name; + return dyd->actvar.n - 1 - fs->firstlocal; +} + +#define new_localvarliteral(ls,v) \ + new_localvar(ls, \ + luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); + + + +/* +** Return the "variable description" (Vardesc) of a given variable. +** (Unless noted otherwise, all variables are referred to by their +** compiler indices.) +*/ +static Vardesc *getlocalvardesc (FuncState *fs, int vidx) { + return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx]; +} + + +/* +** Convert 'nvar', a compiler index level, to it corresponding +** stack index level. For that, search for the highest variable +** below that level that is in the stack and uses its stack +** index ('sidx'). +*/ +static int stacklevel (FuncState *fs, int nvar) { + while (nvar-- > 0) { + Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */ + if (vd->vd.kind != RDKCTC) /* is in the stack? */ + return vd->vd.sidx + 1; + } + return 0; /* no variables in the stack */ +} + + +/* +** Return the number of variables in the stack for function 'fs' +*/ +int luaY_nvarstack (FuncState *fs) { + return stacklevel(fs, fs->nactvar); +} + + +/* +** Get the debug-information entry for current variable 'vidx'. +*/ +static LocVar *localdebuginfo (FuncState *fs, int vidx) { + Vardesc *vd = getlocalvardesc(fs, vidx); + if (vd->vd.kind == RDKCTC) + return NULL; /* no debug info. for constants */ + else { + int idx = vd->vd.pidx; + lua_assert(idx < fs->ndebugvars); + return &fs->f->locvars[idx]; + } +} + + +/* +** Create an expression representing variable 'vidx' +*/ +static void init_var (FuncState *fs, expdesc *e, int vidx) { + e->f = e->t = NO_JUMP; + e->k = VLOCAL; + e->u.var.vidx = vidx; + e->u.var.sidx = getlocalvardesc(fs, vidx)->vd.sidx; +} + + +/* +** Raises an error if variable described by 'e' is read only +*/ +static void check_readonly (LexState *ls, expdesc *e) { + FuncState *fs = ls->fs; + TString *varname = NULL; /* to be set if variable is const */ + switch (e->k) { + case VCONST: { + varname = ls->dyd->actvar.arr[e->u.info].vd.name; + break; + } + case VLOCAL: { + Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); + if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ + varname = vardesc->vd.name; + break; + } + case VUPVAL: { + Upvaldesc *up = &fs->f->upvalues[e->u.info]; + if (up->kind != VDKREG) + varname = up->name; + break; + } + default: + return; /* other cases cannot be read-only */ + } + if (varname) { + const char *msg = luaO_pushfstring(ls->L, + "attempt to assign to const variable '%s'", getstr(varname)); + luaK_semerror(ls, msg); /* error */ + } +} + + +/* +** Start the scope for the last 'nvars' created variables. +*/ +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + int stklevel = luaY_nvarstack(fs); + int i; + for (i = 0; i < nvars; i++) { + int vidx = fs->nactvar++; + Vardesc *var = getlocalvardesc(fs, vidx); + var->vd.sidx = stklevel++; + var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); + } +} + + +/* +** Close the scope for all variables up to level 'tolevel'. +** (debug info.) +*/ +static void removevars (FuncState *fs, int tolevel) { + fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); + while (fs->nactvar > tolevel) { + LocVar *var = localdebuginfo(fs, --fs->nactvar); + if (var) /* does it have debug information? */ + var->endpc = fs->pc; + } +} + + +/* +** Search the upvalues of the function 'fs' for one +** with the given 'name'. +*/ +static int searchupvalue (FuncState *fs, TString *name) { + int i; + Upvaldesc *up = fs->f->upvalues; + for (i = 0; i < fs->nups; i++) { + if (eqstr(up[i].name, name)) return i; + } + return -1; /* not found */ +} + + +static Upvaldesc *allocupvalue (FuncState *fs) { + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); + luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, + Upvaldesc, MAXUPVAL, "upvalues"); + while (oldsize < f->sizeupvalues) + f->upvalues[oldsize++].name = NULL; + return &f->upvalues[fs->nups++]; +} + + +static int newupvalue (FuncState *fs, TString *name, expdesc *v) { + Upvaldesc *up = allocupvalue(fs); + FuncState *prev = fs->prev; + if (v->k == VLOCAL) { + up->instack = 1; + up->idx = v->u.var.sidx; + up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind; + lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name)); + } + else { + up->instack = 0; + up->idx = cast_byte(v->u.info); + up->kind = prev->f->upvalues[v->u.info].kind; + lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); + } + up->name = name; + luaC_objbarrier(fs->ls->L, fs->f, name); + return fs->nups - 1; +} + + +/* +** Look for an active local variable with the name 'n' in the +** function 'fs'. If found, initialize 'var' with it and return +** its expression kind; otherwise return -1. +*/ +static int searchvar (FuncState *fs, TString *n, expdesc *var) { + int i; + for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { + Vardesc *vd = getlocalvardesc(fs, i); + if (eqstr(n, vd->vd.name)) { /* found? */ + if (vd->vd.kind == RDKCTC) /* compile-time constant? */ + init_exp(var, VCONST, fs->firstlocal + i); + else /* real variable */ + init_var(fs, var, i); + return var->k; + } + } + return -1; /* not found */ +} + + +/* +** Mark block where variable at given level was defined +** (to emit close instructions later). +*/ +static void markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl->nactvar > level) + bl = bl->previous; + bl->upval = 1; + fs->needclose = 1; +} + + +/* +** Find a variable with the given name 'n'. If it is an upvalue, add +** this upvalue into all intermediate functions. If it is a global, set +** 'var' as 'void' as a flag. +*/ +static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + if (fs == NULL) /* no more levels? */ + init_exp(var, VVOID, 0); /* default is global */ + else { + int v = searchvar(fs, n, var); /* look up locals at current level */ + if (v >= 0) { /* found? */ + if (v == VLOCAL && !base) + markupval(fs, var->u.var.vidx); /* local will be used as an upval */ + } + else { /* not found as local at current level; try upvalues */ + int idx = searchupvalue(fs, n); /* try existing upvalues */ + if (idx < 0) { /* not found? */ + singlevaraux(fs->prev, n, var, 0); /* try upper levels */ + if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ + idx = newupvalue(fs, n, var); /* will be a new upvalue */ + else /* it is a global or a constant */ + return; /* don't need to do anything at this level */ + } + init_exp(var, VUPVAL, idx); /* new or old upvalue */ + } + } +} + + +/* +** Find a variable with the given name 'n', handling global variables +** too. +*/ +static void singlevar (LexState *ls, expdesc *var) { + TString *varname = str_checkname(ls); + FuncState *fs = ls->fs; + singlevaraux(fs, varname, var, 1); + if (var->k == VVOID) { /* global name? */ + expdesc key; + singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ + lua_assert(var->k != VVOID); /* this one must exist */ + codestring(&key, varname); /* key is variable name */ + luaK_indexed(fs, var, &key); /* env[varname] */ + } +} + + +/* +** Adjust the number of results from an expression list 'e' with 'nexps' +** expressions to 'nvars' values. +*/ +static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int needed = nvars - nexps; /* extra values needed */ + if (hasmultret(e->k)) { /* last expression has multiple returns? */ + int extra = needed + 1; /* discount last expression itself */ + if (extra < 0) + extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + } + else { + if (e->k != VVOID) /* at least one expression? */ + luaK_exp2nextreg(fs, e); /* close last expression */ + if (needed > 0) /* missing values? */ + luaK_nil(fs, fs->freereg, needed); /* complete with nils */ + } + if (needed > 0) + luaK_reserveregs(fs, needed); /* registers for extra values */ + else /* adding 'needed' is actually a subtraction */ + fs->freereg += needed; /* remove extra values */ +} + + +/* +** Macros to limit the maximum recursion depth while parsing +*/ +#define enterlevel(ls) luaE_enterCcall((ls)->L) + +#define leavelevel(ls) luaE_exitCcall((ls)->L) + + +/* +** Generates an error that a goto jumps into the scope of some +** local variable. +*/ +static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { + const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name); + const char *msg = " at line %d jumps into the scope of local '%s'"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); + luaK_semerror(ls, msg); /* raise the error */ +} + + +/* +** Solves the goto at index 'g' to given 'label' and removes it +** from the list of pending goto's. +** If it jumps into the scope of some variable, raises an error. +*/ +static void solvegoto (LexState *ls, int g, Labeldesc *label) { + int i; + Labellist *gl = &ls->dyd->gt; /* list of goto's */ + Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ + lua_assert(eqstr(gt->name, label->name)); + if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ + jumpscopeerror(ls, gt); + luaK_patchlist(ls->fs, gt->pc, label->pc); + for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ + gl->arr[i] = gl->arr[i + 1]; + gl->n--; +} + + +/* +** Search for an active label with the given name. +*/ +static Labeldesc *findlabel (LexState *ls, TString *name) { + int i; + Dyndata *dyd = ls->dyd; + /* check labels in current function for a match */ + for (i = ls->fs->firstlabel; i < dyd->label.n; i++) { + Labeldesc *lb = &dyd->label.arr[i]; + if (eqstr(lb->name, name)) /* correct label? */ + return lb; + } + return NULL; /* label not found */ +} + + +/* +** Adds a new label/goto in the corresponding list. +*/ +static int newlabelentry (LexState *ls, Labellist *l, TString *name, + int line, int pc) { + int n = l->n; + luaM_growvector(ls->L, l->arr, n, l->size, + Labeldesc, SHRT_MAX, "labels/gotos"); + l->arr[n].name = name; + l->arr[n].line = line; + l->arr[n].nactvar = ls->fs->nactvar; + l->arr[n].close = 0; + l->arr[n].pc = pc; + l->n = n + 1; + return n; +} + + +static int newgotoentry (LexState *ls, TString *name, int line, int pc) { + return newlabelentry(ls, &ls->dyd->gt, name, line, pc); +} + + +/* +** Solves forward jumps. Check whether new label 'lb' matches any +** pending gotos in current block and solves them. Return true +** if any of the goto's need to close upvalues. +*/ +static int solvegotos (LexState *ls, Labeldesc *lb) { + Labellist *gl = &ls->dyd->gt; + int i = ls->fs->bl->firstgoto; + int needsclose = 0; + while (i < gl->n) { + if (eqstr(gl->arr[i].name, lb->name)) { + needsclose |= gl->arr[i].close; + solvegoto(ls, i, lb); /* will remove 'i' from the list */ + } + else + i++; + } + return needsclose; +} + + +/* +** Create a new label with the given 'name' at the given 'line'. +** 'last' tells whether label is the last non-op statement in its +** block. Solves all pending goto's to this new label and adds +** a close instruction if necessary. +** Returns true iff it added a close instruction. +*/ +static int createlabel (LexState *ls, TString *name, int line, + int last) { + FuncState *fs = ls->fs; + Labellist *ll = &ls->dyd->label; + int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs)); + if (last) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + ll->arr[l].nactvar = fs->bl->nactvar; + } + if (solvegotos(ls, &ll->arr[l])) { /* need close? */ + luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0); + return 1; + } + return 0; +} + + +/* +** Adjust pending gotos to outer level of a block. +*/ +static void movegotosout (FuncState *fs, BlockCnt *bl) { + int i; + Labellist *gl = &fs->ls->dyd->gt; + /* correct pending gotos to current block */ + for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */ + Labeldesc *gt = &gl->arr[i]; + /* leaving a variable scope? */ + if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar)) + gt->close |= bl->upval; /* jump may need a close */ + gt->nactvar = bl->nactvar; /* update goto level */ + } +} + + +static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { + bl->isloop = isloop; + bl->nactvar = fs->nactvar; + bl->firstlabel = fs->ls->dyd->label.n; + bl->firstgoto = fs->ls->dyd->gt.n; + bl->upval = 0; + bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc); + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == luaY_nvarstack(fs)); +} + + +/* +** generates an error for an undefined 'goto'. +*/ +static l_noret undefgoto (LexState *ls, Labeldesc *gt) { + const char *msg; + if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) { + msg = "break outside loop at line %d"; + msg = luaO_pushfstring(ls->L, msg, gt->line); + } + else { + msg = "no visible label '%s' for at line %d"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); + } + luaK_semerror(ls, msg); +} + + +static void leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + LexState *ls = fs->ls; + int hasclose = 0; + int stklevel = stacklevel(fs, bl->nactvar); /* level outside the block */ + if (bl->isloop) /* fix pending breaks? */ + hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); + if (!hasclose && bl->previous && bl->upval) + luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); + fs->bl = bl->previous; + removevars(fs, bl->nactvar); + lua_assert(bl->nactvar == fs->nactvar); + fs->freereg = stklevel; /* free registers */ + ls->dyd->label.n = bl->firstlabel; /* remove local labels */ + if (bl->previous) /* inner block? */ + movegotosout(fs, bl); /* update pending gotos to outer block */ + else { + if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ + undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ + } +} + + +/* +** adds a new prototype into list of prototypes +*/ +static Proto *addprototype (LexState *ls) { + Proto *clp; + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; /* prototype of current function */ + if (fs->np >= f->sizep) { + int oldsize = f->sizep; + luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); + while (oldsize < f->sizep) + f->p[oldsize++] = NULL; + } + f->p[fs->np++] = clp = luaF_newproto(L); + luaC_objbarrier(L, f, clp); + return clp; +} + + +/* +** codes instruction to create new closure in parent function. +** The OP_CLOSURE instruction uses the last available register, +** so that, if it invokes the GC, the GC knows which registers +** are in use at that time. + +*/ +static void codeclosure (LexState *ls, expdesc *v) { + FuncState *fs = ls->fs->prev; + init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); + luaK_exp2nextreg(fs, v); /* fix it at the last register */ +} + + +static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { + Proto *f = fs->f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + ls->fs = fs; + fs->pc = 0; + fs->previousline = f->linedefined; + fs->iwthabs = 0; + fs->lasttarget = 0; + fs->freereg = 0; + fs->nk = 0; + fs->nabslineinfo = 0; + fs->np = 0; + fs->nups = 0; + fs->ndebugvars = 0; + fs->nactvar = 0; + fs->needclose = 0; + fs->firstlocal = ls->dyd->actvar.n; + fs->firstlabel = ls->dyd->label.n; + fs->bl = NULL; + f->source = ls->source; + luaC_objbarrier(ls->L, f, f->source); + f->maxstacksize = 2; /* registers 0/1 are always valid */ + enterblock(fs, bl, 0); +} + + +static void close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ + leaveblock(fs); + lua_assert(fs->bl == NULL); + luaK_finish(fs); + luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction); + luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte); + luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo, + fs->nabslineinfo, AbsLineInfo); + luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue); + luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); + luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); + luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); + ls->fs = fs->prev; + luaC_checkGC(L); +} + + + +/*============================================================*/ +/* GRAMMAR RULES */ +/*============================================================*/ + + +/* +** check whether current token is in the follow set of a block. +** 'until' closes syntactical blocks, but do not close scope, +** so it is handled in separate. +*/ +static int block_follow (LexState *ls, int withuntil) { + switch (ls->t.token) { + case TK_ELSE: case TK_ELSEIF: + case TK_END: case TK_EOS: + return 1; + case TK_UNTIL: return withuntil; + default: return 0; + } +} + + +static void statlist (LexState *ls) { + /* statlist -> { stat [';'] } */ + while (!block_follow(ls, 1)) { + if (ls->t.token == TK_RETURN) { + statement(ls); + return; /* 'return' must be last statement */ + } + statement(ls); + } +} + + +static void fieldsel (LexState *ls, expdesc *v) { + /* fieldsel -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyregup(fs, v); + luaX_next(ls); /* skip the dot or colon */ + codename(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +typedef struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of 'record' elements */ + int na; /* number of array elements already stored */ + int tostore; /* number of array elements pending to be stored */ +} ConsControl; + + +static void recfield (LexState *ls, ConsControl *cc) { + /* recfield -> (NAME | '['exp']') = exp */ + FuncState *fs = ls->fs; + int reg = ls->fs->freereg; + expdesc tab, key, val; + if (ls->t.token == TK_NAME) { + checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + codename(ls, &key); + } + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + tab = *cc->t; + luaK_indexed(fs, &tab, &key); + expr(ls, &val); + luaK_storevar(fs, &tab, &val); + fs->freereg = reg; /* free registers */ +} + + +static void closelistfield (FuncState *fs, ConsControl *cc) { + if (cc->v.k == VVOID) return; /* there is no list item */ + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore == LFIELDS_PER_FLUSH) { + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ + cc->na += cc->tostore; + cc->tostore = 0; /* no more items pending */ + } +} + + +static void lastlistfield (FuncState *fs, ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); + } + cc->na += cc->tostore; +} + + +static void listfield (LexState *ls, ConsControl *cc) { + /* listfield -> exp */ + expr(ls, &cc->v); + cc->tostore++; +} + + +static void field (LexState *ls, ConsControl *cc) { + /* field -> listfield | recfield */ + switch(ls->t.token) { + case TK_NAME: { /* may be 'listfield' or 'recfield' */ + if (luaX_lookahead(ls) != '=') /* expression? */ + listfield(ls, cc); + else + recfield(ls, cc); + break; + } + case '[': { + recfield(ls, cc); + break; + } + default: { + listfield(ls, cc); + break; + } + } +} + + +static void constructor (LexState *ls, expdesc *t) { + /* constructor -> '{' [ field { sep field } [sep] ] '}' + sep -> ',' | ';' */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + ConsControl cc; + luaK_code(fs, 0); /* space for extra arg. */ + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */ + luaK_reserveregs(fs, 1); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + checknext(ls, '{'); + do { + lua_assert(cc.v.k == VVOID || cc.tostore > 0); + if (ls->t.token == '}') break; + closelistfield(fs, &cc); + field(ls, &cc); + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, '}', '{', line); + lastlistfield(fs, &cc); + luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh); +} + +/* }====================================================================== */ + + +static void setvararg (FuncState *fs, int nparams) { + fs->f->is_vararg = 1; + luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0); +} + + +static void parlist (LexState *ls) { + /* parlist -> [ param { ',' param } ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + int isvararg = 0; + if (ls->t.token != ')') { /* is 'parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { /* param -> NAME */ + new_localvar(ls, str_checkname(ls)); + nparams++; + break; + } + case TK_DOTS: { /* param -> '...' */ + luaX_next(ls); + isvararg = 1; + break; + } + default: luaX_syntaxerror(ls, " or '...' expected"); + } + } while (!isvararg && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar); + if (isvararg) + setvararg(fs, f->numparams); /* declared vararg */ + luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */ +} + + +static void body (LexState *ls, expdesc *e, int ismethod, int line) { + /* body -> '(' parlist ')' block END */ + FuncState new_fs; + BlockCnt bl; + new_fs.f = addprototype(ls); + new_fs.f->linedefined = line; + open_func(ls, &new_fs, &bl); + checknext(ls, '('); + if (ismethod) { + new_localvarliteral(ls, "self"); /* create 'self' parameter */ + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + statlist(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + codeclosure(ls, e); + close_func(ls); +} + + +static int explist (LexState *ls, expdesc *v) { + /* explist -> expr { ',' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void funcargs (LexState *ls, expdesc *f, int line) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + switch (ls->t.token) { + case '(': { /* funcargs -> '(' [ explist ] ')' */ + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist(ls, &args); + if (hasmultret(args.k)) + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(&args, ls->t.seminfo.ts); + luaX_next(ls); /* must use 'seminfo' before 'next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; /* call remove function and arguments and leaves + (unless changed) one result */ +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + } + } +} + + +static void suffixedexp (LexState *ls, expdesc *v) { + /* suffixedexp -> + primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + primaryexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* fieldsel */ + fieldsel(ls, v); + break; + } + case '[': { /* '[' exp ']' */ + expdesc key; + luaK_exp2anyregup(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* ':' NAME funcargs */ + expdesc key; + luaX_next(ls); + codename(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v, line); + break; + } + case '(': case TK_STRING: case '{': { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v, line); + break; + } + default: return; + } + } +} + + +static void simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | + constructor | FUNCTION body | suffixedexp */ + switch (ls->t.token) { + case TK_FLT: { + init_exp(v, VKFLT, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_INT: { + init_exp(v, VKINT, 0); + v->u.ival = ls->t.seminfo.i; + break; + } + case TK_STRING: { + codestring(v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, fs->f->is_vararg, + "cannot use '...' outside a vararg function"); + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1)); + break; + } + case '{': { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + suffixedexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '~': return OPR_BNOT; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case '/': return OPR_DIV; + case TK_IDIV: return OPR_IDIV; + case '&': return OPR_BAND; + case '|': return OPR_BOR; + case '~': return OPR_BXOR; + case TK_SHL: return OPR_SHL; + case TK_SHR: return OPR_SHR; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +/* +** Priority table for binary operators. +*/ +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {10, 10}, {10, 10}, /* '+' '-' */ + {11, 11}, {11, 11}, /* '*' '%' */ + {14, 13}, /* '^' (right associative) */ + {11, 11}, {11, 11}, /* '/' '//' */ + {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */ + {7, 7}, {7, 7}, /* '<<' '>>' */ + {9, 8}, /* '..' (right associative) */ + {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ + {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ + {2, 2}, {1, 1} /* and, or */ +}; + +#define UNARY_PRIORITY 12 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where 'binop' is any binary operator with a priority higher than 'limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */ + int line = ls->linenumber; + luaX_next(ls); /* skip operator */ + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v, line); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than 'limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + int line = ls->linenumber; + luaX_next(ls); /* skip operator */ + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2, line); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static void block (LexState *ls) { + /* block -> statlist */ + FuncState *fs = ls->fs; + BlockCnt bl; + enterblock(fs, &bl, 0); + statlist(ls); + leaveblock(fs); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to an upvalue/local variable, the +** upvalue/local variable is begin used in a previous assignment to a +** table. If so, save original upvalue/local value in a safe place and +** use this safe copy in the previous assignment. +*/ +static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + int extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { /* check all previous assignments */ + if (vkisindexed(lh->v.k)) { /* assignment to table field? */ + if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ + if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { + conflict = 1; /* table is the upvalue being assigned now */ + lh->v.k = VINDEXSTR; + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + } + else { /* table is a register */ + if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) { + conflict = 1; /* table is the local being assigned now */ + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + /* is index the local being assigned? */ + if (lh->v.k == VINDEXED && v->k == VLOCAL && + lh->v.u.ind.idx == v->u.var.sidx) { + conflict = 1; + lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ + } + } + } + } + if (conflict) { + /* copy upvalue/local value to a temporary (in position 'extra') */ + if (v->k == VLOCAL) + luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0); + else + luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0); + luaK_reserveregs(fs, 1); + } +} + +/* +** Parse and compile a multiple assignment. The first "variable" +** (a 'suffixedexp') was already read by the caller. +** +** assignment -> suffixedexp restassign +** restassign -> ',' suffixedexp restassign | '=' explist +*/ +static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, vkisvar(lh->v.k), "syntax error"); + check_readonly(ls, &lh->v); + if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */ + struct LHS_assign nv; + nv.prev = lh; + suffixedexp(ls, &nv.v); + if (!vkisindexed(nv.v.k)) + check_conflict(ls, lh, &nv.v); + enterlevel(ls); /* control recursion depth */ + restassign(ls, &nv, nvars+1); + leavelevel(ls); + } + else { /* restassign -> '=' explist */ + int nexps; + checknext(ls, '='); + nexps = explist(ls, &e); + if (nexps != nvars) + adjust_assign(ls, nvars, nexps, &e); + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + luaK_storevar(ls->fs, &lh->v, &e); +} + + +static int cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void gotostat (LexState *ls) { + FuncState *fs = ls->fs; + int line = ls->linenumber; + TString *name = str_checkname(ls); /* label's name */ + Labeldesc *lb = findlabel(ls, name); + if (lb == NULL) /* no label? */ + /* forward jump; will be resolved when the label is declared */ + newgotoentry(ls, name, line, luaK_jump(fs)); + else { /* found a label */ + /* backward jump; will be resolved here */ + int lblevel = stacklevel(fs, lb->nactvar); /* label level */ + if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */ + luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0); + /* create jump and link it to the label */ + luaK_patchlist(fs, luaK_jump(fs), lb->pc); + } +} + + +/* +** Break statement. Semantically equivalent to "goto break". +*/ +static void breakstat (LexState *ls) { + int line = ls->linenumber; + luaX_next(ls); /* skip break */ + newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs)); +} + + +/* +** Check whether there is already a label with the given 'name'. +*/ +static void checkrepeated (LexState *ls, TString *name) { + Labeldesc *lb = findlabel(ls, name); + if (unlikely(lb != NULL)) { /* already defined? */ + const char *msg = "label '%s' already defined on line %d"; + msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); + luaK_semerror(ls, msg); /* error */ + } +} + + +static void labelstat (LexState *ls, TString *name, int line) { + /* label -> '::' NAME '::' */ + checknext(ls, TK_DBCOLON); /* skip double colon */ + while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) + statement(ls); /* skip other no-op statements */ + checkrepeated(ls, name); /* check for repeated labels */ + createlabel(ls, name, line, block_follow(ls, 0)); +} + + +static void whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_jumpto(fs, whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + statlist(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + leaveblock(fs); /* finish scope */ + if (bl2.upval) { /* upvalues? */ + int exit = luaK_jump(fs); /* normal exit must jump over fix */ + luaK_patchtohere(fs, condexit); /* repetition must close upvalues */ + luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0); + condexit = luaK_jump(fs); /* repeat after closing upvalues */ + luaK_patchtohere(fs, exit); /* normal exit comes to here */ + } + luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ + leaveblock(fs); /* finish loop */ +} + + +/* +** Read an expression and generate code to put its results in next +** stack slot. +** +*/ +static void exp1 (LexState *ls) { + expdesc e; + expr(ls, &e); + luaK_exp2nextreg(ls->fs, &e); + lua_assert(e.k == VNONRELOC); +} + + +/* +** Fix for instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua). 'back' true means +** a back jump. +*/ +static void fixforjump (FuncState *fs, int pc, int dest, int back) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + if (back) + offset = -offset; + if (unlikely(offset > MAXARG_Bx)) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_Bx(*jmp, offset); +} + + +/* +** Generate code for a 'for' loop. +*/ +static void forbody (LexState *ls, int base, int line, int nvars, int isgen) { + /* forbody -> DO block */ + static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP}; + static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP}; + BlockCnt bl; + FuncState *fs = ls->fs; + int prep, endfor; + checknext(ls, TK_DO); + prep = luaK_codeABx(fs, forprep[isgen], base, 0); + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + fixforjump(fs, prep, luaK_getlabel(fs), 0); + if (isgen) { /* generic for? */ + luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); + luaK_fixline(fs, line); + } + endfor = luaK_codeABx(fs, forloop[isgen], base, 0); + fixforjump(fs, endfor, prep + 1, 1); + luaK_fixline(fs, line); +} + + +static void fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp,exp[,exp] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvar(ls, varname); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_int(fs, fs->freereg, 1); + luaK_reserveregs(fs, 1); + } + adjustlocalvars(ls, 3); /* control variables */ + forbody(ls, base, line, 1, 0); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 5; /* gen, state, control, toclose, 'indexname' */ + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + /* create declared variables */ + new_localvar(ls, indexname); + while (testnext(ls, ',')) { + new_localvar(ls, str_checkname(ls)); + nvars++; + } + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 4, explist(ls, &e), &e); + adjustlocalvars(ls, 4); /* control variables */ + markupval(fs, fs->nactvar); /* last control var. must be closed */ + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 4, 1); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip 'for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, "'=' or 'in' expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope ('break' jumps to this point) */ +} + + +/* +** Check whether next instruction is a single jump (a 'break', a 'goto' +** to a forward label, or a 'goto' to a backward label with no variable +** to close). If so, set the name of the 'label' it is jumping to +** ("break" for a 'break') or to where it is jumping to ('target') and +** return true. If not a single jump, leave input unchanged, to be +** handled as a regular statement. +*/ +static int issinglejump (LexState *ls, TString **label, int *target) { + if (testnext(ls, TK_BREAK)) { /* a break? */ + *label = luaS_newliteral(ls->L, "break"); + return 1; + } + else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME) + return 0; /* not a valid goto */ + else { + TString *lname = ls->lookahead.seminfo.ts; /* label's id */ + Labeldesc *lb = findlabel(ls, lname); + if (lb) { /* a backward jump? */ + /* does it need to close variables? */ + if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar)) + return 0; /* not a single jump; cannot optimize */ + *target = lb->pc; + } + else /* jump forward */ + *label = lname; + luaX_next(ls); /* skip goto */ + luaX_next(ls); /* skip name */ + return 1; + } +} + + +static void test_then_block (LexState *ls, int *escapelist) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + BlockCnt bl; + int line; + FuncState *fs = ls->fs; + TString *jlb = NULL; + int target = NO_JUMP; + expdesc v; + int jf; /* instruction to skip 'then' code (if condition is false) */ + luaX_next(ls); /* skip IF or ELSEIF */ + expr(ls, &v); /* read condition */ + checknext(ls, TK_THEN); + line = ls->linenumber; + if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */ + luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ + enterblock(fs, &bl, 0); /* must enter block before 'goto' */ + if (jlb != NULL) /* forward jump? */ + newgotoentry(ls, jlb, line, v.t); /* will be resolved later */ + else /* backward jump */ + luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */ + while (testnext(ls, ';')) {} /* skip semicolons */ + if (block_follow(ls, 0)) { /* jump is the entire block? */ + leaveblock(fs); + return; /* and that is it */ + } + else /* must skip over 'then' part if condition is false */ + jf = luaK_jump(fs); + } + else { /* regular case (not a jump) */ + luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ + enterblock(fs, &bl, 0); + jf = v.f; + } + statlist(ls); /* 'then' part */ + leaveblock(fs); + if (ls->t.token == TK_ELSE || + ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ + luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ + luaK_patchtohere(fs, jf); +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int escapelist = NO_JUMP; /* exit list for finished parts */ + test_then_block(ls, &escapelist); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) + test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ + if (testnext(ls, TK_ELSE)) + block(ls); /* 'else' part */ + check_match(ls, TK_END, TK_IF, line); + luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ +} + + +static void localfunc (LexState *ls) { + expdesc b; + FuncState *fs = ls->fs; + int fvar = fs->nactvar; /* function's variable index */ + new_localvar(ls, str_checkname(ls)); /* new local variable */ + adjustlocalvars(ls, 1); /* enter its scope */ + body(ls, &b, 0, ls->linenumber); /* function created in next register */ + /* debug information will only see the variable after this point! */ + localdebuginfo(fs, fvar)->startpc = fs->pc; +} + + +static int getlocalattribute (LexState *ls) { + /* ATTRIB -> ['<' Name '>'] */ + if (testnext(ls, '<')) { + const char *attr = getstr(str_checkname(ls)); + checknext(ls, '>'); + if (strcmp(attr, "const") == 0) + return RDKCONST; /* read-only variable */ + else if (strcmp(attr, "close") == 0) + return RDKTOCLOSE; /* to-be-closed variable */ + else + luaK_semerror(ls, + luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); + } + return VDKREG; /* regular variable */ +} + + +static void checktoclose (LexState *ls, int level) { + if (level != -1) { /* is there a to-be-closed variable? */ + FuncState *fs = ls->fs; + markupval(fs, level + 1); + fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ + luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0); + } +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */ + FuncState *fs = ls->fs; + int toclose = -1; /* index of to-be-closed variable (if any) */ + Vardesc *var; /* last variable */ + int vidx, kind; /* index and kind of last variable */ + int nvars = 0; + int nexps; + expdesc e; + do { + vidx = new_localvar(ls, str_checkname(ls)); + kind = getlocalattribute(ls); + getlocalvardesc(fs, vidx)->vd.kind = kind; + if (kind == RDKTOCLOSE) { /* to-be-closed? */ + if (toclose != -1) /* one already present? */ + luaK_semerror(ls, "multiple to-be-closed variables in local list"); + toclose = fs->nactvar + nvars; + } + nvars++; + } while (testnext(ls, ',')); + if (testnext(ls, '=')) + nexps = explist(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + var = getlocalvardesc(fs, vidx); /* get last variable */ + if (nvars == nexps && /* no adjustments? */ + var->vd.kind == RDKCONST && /* last variable is const? */ + luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ + var->vd.kind = RDKCTC; /* variable is a compile-time constant */ + adjustlocalvars(ls, nvars - 1); /* exclude last variable */ + fs->nactvar++; /* but count it */ + } + else { + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); + } + checktoclose(ls, toclose); +} + + +static int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {fieldsel} [':' NAME] */ + int ismethod = 0; + singlevar(ls, v); + while (ls->t.token == '.') + fieldsel(ls, v); + if (ls->t.token == ':') { + ismethod = 1; + fieldsel(ls, v); + } + return ismethod; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int ismethod; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + ismethod = funcname(ls, &v); + body(ls, &b, ismethod, line); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ +} + + +static void exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + suffixedexp(ls, &v.v); + if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */ + v.prev = NULL; + restassign(ls, &v, 1); + } + else { /* stat -> func */ + Instruction *inst; + check_condition(ls, v.v.k == VCALL, "syntax error"); + inst = &getinstruction(fs, &v.v); + SETARG_C(*inst, 1); /* call statement uses no results */ + } +} + + +static void retstat (LexState *ls) { + /* stat -> RETURN [explist] [';'] */ + FuncState *fs = ls->fs; + expdesc e; + int nret; /* number of values being returned */ + int first = luaY_nvarstack(fs); /* first slot to be returned */ + if (block_follow(ls, 1) || ls->t.token == ';') + nret = 0; /* return no values */ + else { + nret = explist(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */ + SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs)); + } + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); /* can use original slot */ + else { /* values must go to the top of the stack */ + luaK_exp2nextreg(fs, &e); + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); + testnext(ls, ';'); /* skip optional semicolon */ +} + + +static void statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + enterlevel(ls); + switch (ls->t.token) { + case ';': { /* stat -> ';' (empty statement) */ + luaX_next(ls); /* skip ';' */ + break; + } + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + break; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + break; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + break; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + break; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + break; + } + case TK_FUNCTION: { /* stat -> funcstat */ + funcstat(ls, line); + break; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + break; + } + case TK_DBCOLON: { /* stat -> label */ + luaX_next(ls); /* skip double colon */ + labelstat(ls, str_checkname(ls), line); + break; + } + case TK_RETURN: { /* stat -> retstat */ + luaX_next(ls); /* skip RETURN */ + retstat(ls); + break; + } + case TK_BREAK: { /* stat -> breakstat */ + breakstat(ls); + break; + } + case TK_GOTO: { /* stat -> 'goto' NAME */ + luaX_next(ls); /* skip 'goto' */ + gotostat(ls); + break; + } + default: { /* stat -> func | assignment */ + exprstat(ls); + break; + } + } + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= luaY_nvarstack(ls->fs)); + ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */ + leavelevel(ls); +} + +/* }====================================================================== */ + + +/* +** compiles the main function, which is a regular vararg function with an +** upvalue named LUA_ENV +*/ +static void mainfunc (LexState *ls, FuncState *fs) { + BlockCnt bl; + Upvaldesc *env; + open_func(ls, fs, &bl); + setvararg(fs, 0); /* main function is always declared vararg */ + env = allocupvalue(fs); /* ...set environment upvalue */ + env->instack = 1; + env->idx = 0; + env->kind = VDKREG; + env->name = ls->envn; + luaC_objbarrier(ls->L, fs->f, env->name); + luaX_next(ls); /* read first token */ + statlist(ls); /* parse main body */ + check(ls, TK_EOS); + close_func(ls); +} + + +LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar) { + LexState lexstate; + FuncState funcstate; + LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ + setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */ + luaD_inctop(L); + lexstate.h = luaH_new(L); /* create table for scanner */ + sethvalue2s(L, L->top, lexstate.h); /* anchor it */ + luaD_inctop(L); + funcstate.f = cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); + funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ + luaC_objbarrier(L, funcstate.f, funcstate.f->source); + lexstate.buff = buff; + lexstate.dyd = dyd; + dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; + luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar); + mainfunc(&lexstate, &funcstate); + lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); + /* all scopes should be correctly finished */ + lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); + L->top--; /* remove scanner's table */ + return cl; /* closure is on the stack, too */ +} + diff --git a/Lua/lparser.h b/Lua/lparser.h new file mode 100644 index 00000000..618cb010 --- /dev/null +++ b/Lua/lparser.h @@ -0,0 +1,170 @@ +/* +** $Id: lparser.h $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* +** Expression and variable descriptor. +** Code generation for variables and expressions can be delayed to allow +** optimizations; An 'expdesc' structure describes a potentially-delayed +** variable/expression. It has a description of its "main" value plus a +** list of conditional jumps that can also produce its value (generated +** by short-circuit operators 'and'/'or'). +*/ + +/* kinds of variables/expressions */ +typedef enum { + VVOID, /* when 'expdesc' describes the last expression a list, + this kind means an empty list (so, no expression) */ + VNIL, /* constant nil */ + VTRUE, /* constant true */ + VFALSE, /* constant false */ + VK, /* constant in 'k'; info = index of constant in 'k' */ + VKFLT, /* floating constant; nval = numerical float value */ + VKINT, /* integer constant; ival = numerical integer value */ + VKSTR, /* string constant; strval = TString address; + (string is fixed by the lexer) */ + VNONRELOC, /* expression has its value in a fixed register; + info = result register */ + VLOCAL, /* local variable; var.sidx = stack index (local register); + var.vidx = relative index in 'actvar.arr' */ + VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ + VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */ + VINDEXED, /* indexed variable; + ind.t = table register; + ind.idx = key's R index */ + VINDEXUP, /* indexed upvalue; + ind.t = table upvalue; + ind.idx = key's K index */ + VINDEXI, /* indexed variable with constant integer; + ind.t = table register; + ind.idx = key's value */ + VINDEXSTR, /* indexed variable with literal string; + ind.t = table register; + ind.idx = key's K index */ + VJMP, /* expression is a test/comparison; + info = pc of corresponding jump instruction */ + VRELOC, /* expression can put result in any register; + info = instruction pc */ + VCALL, /* expression is a function call; info = instruction pc */ + VVARARG /* vararg expression; info = instruction pc */ +} expkind; + + +#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR) +#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR) + + +typedef struct expdesc { + expkind k; + union { + lua_Integer ival; /* for VKINT */ + lua_Number nval; /* for VKFLT */ + TString *strval; /* for VKSTR */ + int info; /* for generic use */ + struct { /* for indexed variables */ + short idx; /* index (R or "long" K) */ + lu_byte t; /* table (register or upvalue) */ + } ind; + struct { /* for local variables */ + lu_byte sidx; /* index in the stack */ + unsigned short vidx; /* compiler index (in 'actvar.arr') */ + } var; + } u; + int t; /* patch list of 'exit when true' */ + int f; /* patch list of 'exit when false' */ +} expdesc; + + +/* kinds of variables */ +#define VDKREG 0 /* regular */ +#define RDKCONST 1 /* constant */ +#define RDKTOCLOSE 2 /* to-be-closed */ +#define RDKCTC 3 /* compile-time constant */ + +/* description of an active local variable */ +typedef union Vardesc { + struct { + TValuefields; /* constant value (if it is a compile-time constant) */ + lu_byte kind; + lu_byte sidx; /* index of the variable in the stack */ + short pidx; /* index of the variable in the Proto's 'locvars' array */ + TString *name; /* variable name */ + } vd; + TValue k; /* constant value (if any) */ +} Vardesc; + + + +/* description of pending goto statements and label statements */ +typedef struct Labeldesc { + TString *name; /* label identifier */ + int pc; /* position in code */ + int line; /* line where it appeared */ + lu_byte nactvar; /* number of active variables in that position */ + lu_byte close; /* goto that escapes upvalues */ +} Labeldesc; + + +/* list of labels or gotos */ +typedef struct Labellist { + Labeldesc *arr; /* array */ + int n; /* number of entries in use */ + int size; /* array size */ +} Labellist; + + +/* dynamic structures used by the parser */ +typedef struct Dyndata { + struct { /* list of all active local variables */ + Vardesc *arr; + int n; + int size; + } actvar; + Labellist gt; /* list of pending gotos */ + Labellist label; /* list of active labels */ +} Dyndata; + + +/* control of blocks */ +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct BlockCnt *bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to 'ncode') */ + int lasttarget; /* 'label' of last 'jump label' */ + int previousline; /* last line that was saved in 'lineinfo' */ + int nk; /* number of elements in 'k' */ + int np; /* number of elements in 'p' */ + int nabslineinfo; /* number of elements in 'abslineinfo' */ + int firstlocal; /* index of first local var (in Dyndata array) */ + int firstlabel; /* index of first label (in 'dyd->label->arr') */ + short ndebugvars; /* number of elements in 'f->locvars' */ + lu_byte nactvar; /* number of active local variables */ + lu_byte nups; /* number of upvalues */ + lu_byte freereg; /* first free register */ + lu_byte iwthabs; /* instructions issued since last absolute line info */ + lu_byte needclose; /* function needs to close upvalues when returning */ +} FuncState; + + +LUAI_FUNC int luaY_nvarstack (FuncState *fs); +LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar); + + +#endif diff --git a/Lua/lprefix.h b/Lua/lprefix.h new file mode 100644 index 00000000..484f2ad6 --- /dev/null +++ b/Lua/lprefix.h @@ -0,0 +1,45 @@ +/* +** $Id: lprefix.h $ +** Definitions for Lua code that must come before any other header file +** See Copyright Notice in lua.h +*/ + +#ifndef lprefix_h +#define lprefix_h + + +/* +** Allows POSIX/XSI stuff +*/ +#if !defined(LUA_USE_C89) /* { */ + +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 600 +#elif _XOPEN_SOURCE == 0 +#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ +#endif + +/* +** Allows manipulation of large files in gcc and some other compilers +*/ +#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#endif + +#endif /* } */ + + +/* +** Windows stuff +*/ +#if defined(_WIN32) /* { */ + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ +#endif + +#endif /* } */ + +#endif + diff --git a/Lua/lstate.c b/Lua/lstate.c new file mode 100644 index 00000000..86b3761f --- /dev/null +++ b/Lua/lstate.c @@ -0,0 +1,470 @@ +/* +** $Id: lstate.c $ +** Global State +** See Copyright Notice in lua.h +*/ + +#define lstate_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + + +/* +** thread state + extra space +*/ +typedef struct LX { + lu_byte extra_[LUA_EXTRASPACE]; + lua_State l; +} LX; + + +/* +** Main thread combines a thread state and the global state +*/ +typedef struct LG { + LX l; + global_State g; +} LG; + + + +#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) + + +/* +** A macro to create a "random" seed when a state is created; +** the seed is used to randomize string hashes. +*/ +#if !defined(luai_makeseed) + +#include + +/* +** Compute an initial seed with some level of randomness. +** Rely on Address Space Layout Randomization (if present) and +** current time. +*/ +#define addbuff(b,p,e) \ + { size_t t = cast_sizet(e); \ + memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } + +static unsigned int luai_makeseed (lua_State *L) { + char buff[3 * sizeof(size_t)]; + unsigned int h = cast_uint(time(NULL)); + int p = 0; + addbuff(buff, p, L); /* heap variable */ + addbuff(buff, p, &h); /* local variable */ + addbuff(buff, p, &lua_newstate); /* public function */ + lua_assert(p == sizeof(buff)); + return luaS_hash(buff, p, h, 1); +} + +#endif + + +/* +** set GCdebt to a new value keeping the value (totalbytes + GCdebt) +** invariant (and avoiding underflows in 'totalbytes') +*/ +void luaE_setdebt (global_State *g, l_mem debt) { + l_mem tb = gettotalbytes(g); + lua_assert(tb > 0); + if (debt < tb - MAX_LMEM) + debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ + g->totalbytes = tb - debt; + g->GCdebt = debt; +} + + +LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { + global_State *g = G(L); + int ccalls; + luaE_freeCI(L); /* release unused CIs */ + ccalls = getCcalls(L); + if (limit >= 40000) + return 0; /* out of bounds */ + limit += CSTACKERR; + if (L != g-> mainthread) + return 0; /* only main thread can change the C stack */ + else if (ccalls <= CSTACKERR) + return 0; /* handling overflow */ + else { + int diff = limit - g->Cstacklimit; + if (ccalls + diff <= CSTACKERR) + return 0; /* new limit would cause an overflow */ + g->Cstacklimit = limit; /* set new limit */ + L->nCcalls += diff; /* correct 'nCcalls' */ + return limit - diff - CSTACKERR; /* success; return previous limit */ + } +} + + +/* +** Decrement count of "C calls" and check for overflows. In case of +** a stack overflow, check appropriate error ("regular" overflow or +** overflow while handling stack overflow). If 'nCcalls' is smaller +** than CSTACKERR but larger than CSTACKMARK, it means it has just +** entered the "overflow zone", so the function raises an overflow +** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is +** already handling an overflow) but larger than CSTACKERRMARK, does +** not report an error (to allow message handling to work). Otherwise, +** report a stack overflow while handling a stack overflow (probably +** caused by a repeating error in the message handling function). +*/ + +void luaE_enterCcall (lua_State *L) { + int ncalls = getCcalls(L); + L->nCcalls--; + if (ncalls <= CSTACKERR) { /* possible overflow? */ + luaE_freeCI(L); /* release unused CIs */ + ncalls = getCcalls(L); /* update call count */ + if (ncalls <= CSTACKERR) { /* still overflow? */ + if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */ + luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ + else if (ncalls >= CSTACKMARK) { + /* not in error-handling zone; raise the error now */ + L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */ + luaG_runerror(L, "C stack overflow"); + } + /* else stack is in the error-handling zone; + allow message handler to work */ + } + } +} + + +CallInfo *luaE_extendCI (lua_State *L) { + CallInfo *ci; + lua_assert(L->ci->next == NULL); + luaE_enterCcall(L); + ci = luaM_new(L, CallInfo); + lua_assert(L->ci->next == NULL); + L->ci->next = ci; + ci->previous = L->ci; + ci->next = NULL; + ci->u.l.trap = 0; + L->nci++; + return ci; +} + + +/* +** free all CallInfo structures not in use by a thread +*/ +void luaE_freeCI (lua_State *L) { + CallInfo *ci = L->ci; + CallInfo *next = ci->next; + ci->next = NULL; + L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */ + while ((ci = next) != NULL) { + next = ci->next; + luaM_free(L, ci); + L->nci--; + } + L->nCcalls -= L->nci; /* adjust result */ +} + + +/* +** free half of the CallInfo structures not in use by a thread, +** keeping the first one. +*/ +void luaE_shrinkCI (lua_State *L) { + CallInfo *ci = L->ci->next; /* first free CallInfo */ + CallInfo *next; + if (ci == NULL) + return; /* no extra elements */ + L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */ + while ((next = ci->next) != NULL) { /* two extra elements? */ + CallInfo *next2 = next->next; /* next's next */ + ci->next = next2; /* remove next from the list */ + L->nci--; + luaM_free(L, next); /* free next */ + if (next2 == NULL) + break; /* no more elements */ + else { + next2->previous = ci; + ci = next2; /* continue */ + } + } + L->nCcalls -= L->nci; /* adjust result */ +} + + +static void stack_init (lua_State *L1, lua_State *L) { + int i; CallInfo *ci; + /* initialize stack array */ + L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, StackValue); + L1->stacksize = BASIC_STACK_SIZE; + for (i = 0; i < BASIC_STACK_SIZE; i++) + setnilvalue(s2v(L1->stack + i)); /* erase new stack */ + L1->top = L1->stack; + L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK; + /* initialize first ci */ + ci = &L1->base_ci; + ci->next = ci->previous = NULL; + ci->callstatus = CIST_C; + ci->func = L1->top; + ci->u.c.k = NULL; + ci->nresults = 0; + setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */ + L1->top++; + ci->top = L1->top + LUA_MINSTACK; + L1->ci = ci; +} + + +static void freestack (lua_State *L) { + if (L->stack == NULL) + return; /* stack not completely built yet */ + L->ci = &L->base_ci; /* free the entire 'ci' list */ + luaE_freeCI(L); + lua_assert(L->nci == 0); + luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ +} + + +/* +** Create registry table and its predefined values +*/ +static void init_registry (lua_State *L, global_State *g) { + TValue temp; + /* create registry */ + Table *registry = luaH_new(L); + sethvalue(L, &g->l_registry, registry); + luaH_resize(L, registry, LUA_RIDX_LAST, 0); + /* registry[LUA_RIDX_MAINTHREAD] = L */ + setthvalue(L, &temp, L); /* temp = L */ + luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp); + /* registry[LUA_RIDX_GLOBALS] = table of globals */ + sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */ + luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp); +} + + +/* +** open parts of the state that may cause memory-allocation errors. +** ('g->nilvalue' being a nil value flags that the state was completely +** build.) +*/ +static void f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + init_registry(L, g); + luaS_init(L); + luaT_init(L); + luaX_init(L); + g->gcrunning = 1; /* allow gc */ + setnilvalue(&g->nilvalue); + luai_userstateopen(L); +} + + +/* +** preinitialize a thread with consistent values without allocating +** any memory (to avoid errors) +*/ +static void preinit_thread (lua_State *L, global_State *g) { + G(L) = g; + L->stack = NULL; + L->ci = NULL; + L->nci = 0; + L->stacksize = 0; + L->twups = L; /* thread has no upvalues */ + L->errorJmp = NULL; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->status = LUA_OK; + L->errfunc = 0; + L->oldpc = 0; +} + + +static void close_state (lua_State *L) { + global_State *g = G(L); + luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */ + luaC_freeallobjects(L); /* collect all objects */ + if (ttisnil(&g->nilvalue)) /* closing a fully built state? */ + luai_userstateclose(L); + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); + freestack(L); + lua_assert(gettotalbytes(g) == sizeof(LG)); + (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ +} + + +LUA_API lua_State *lua_newthread (lua_State *L) { + global_State *g; + lua_State *L1; + lua_lock(L); + g = G(L); + luaC_checkGC(L); + /* create new thread */ + L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; + L1->marked = luaC_white(g); + L1->tt = LUA_VTHREAD; + /* link it on list 'allgc' */ + L1->next = g->allgc; + g->allgc = obj2gco(L1); + /* anchor it on L stack */ + setthvalue2s(L, L->top, L1); + api_incr_top(L); + preinit_thread(L1, g); + L1->nCcalls = getCcalls(L); + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + /* initialize L1 extra space */ + memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), + LUA_EXTRASPACE); + luai_userstatethread(L, L1); + stack_init(L1, L); /* init stack */ + lua_unlock(L); + return L1; +} + + +void luaE_freethread (lua_State *L, lua_State *L1) { + LX *l = fromstate(L1); + luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L, L1); + freestack(L1); + luaM_free(L, l); +} + + +int lua_resetthread (lua_State *L) { + CallInfo *ci; + int status; + lua_lock(L); + L->ci = ci = &L->base_ci; /* unwind CallInfo list */ + setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */ + ci->func = L->stack; + ci->callstatus = CIST_C; + status = luaF_close(L, L->stack, CLOSEPROTECT); + if (status != CLOSEPROTECT) /* real errors? */ + luaD_seterrorobj(L, status, L->stack + 1); + else { + status = LUA_OK; + L->top = L->stack + 1; + } + ci->top = L->top + LUA_MINSTACK; + L->status = status; + lua_unlock(L); + return status; +} + + +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { + int i; + lua_State *L; + global_State *g; + LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); + if (l == NULL) return NULL; + L = &l->l.l; + g = &l->g; + L->tt = LUA_VTHREAD; + g->currentwhite = bitmask(WHITE0BIT); + L->marked = luaC_white(g); + preinit_thread(L, g); + g->allgc = obj2gco(L); /* by now, only object is the main thread */ + L->next = NULL; + g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR; + incnny(L); /* main thread is always non yieldable */ + g->frealloc = f; + g->ud = ud; + g->warnf = NULL; + g->ud_warn = NULL; + g->mainthread = L; + g->seed = luai_makeseed(L); + g->gcrunning = 0; /* no GC while building state */ + g->strt.size = g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(&g->l_registry); + g->panic = NULL; + g->gcstate = GCSpause; + g->gckind = KGC_INC; + g->gcemergency = 0; + g->finobj = g->tobefnz = g->fixedgc = NULL; + g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; + g->finobjsur = g->finobjold1 = g->finobjrold = NULL; + g->sweepgc = NULL; + g->gray = g->grayagain = NULL; + g->weak = g->ephemeron = g->allweak = NULL; + g->twups = NULL; + g->totalbytes = sizeof(LG); + g->GCdebt = 0; + g->lastatomic = 0; + setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ + setgcparam(g->gcpause, LUAI_GCPAUSE); + setgcparam(g->gcstepmul, LUAI_GCMUL); + g->gcstepsize = LUAI_GCSTEPSIZE; + setgcparam(g->genmajormul, LUAI_GENMAJORMUL); + g->genminormul = LUAI_GENMINORMUL; + for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + return L; +} + + +LUA_API void lua_close (lua_State *L) { + lua_lock(L); + L = G(L)->mainthread; /* only the main thread can be closed */ + close_state(L); +} + + +void luaE_warning (lua_State *L, const char *msg, int tocont) { + lua_WarnFunction wf = G(L)->warnf; + if (wf != NULL) + wf(G(L)->ud_warn, msg, tocont); +} + + +/* +** Generate a warning from an error message +*/ +void luaE_warnerror (lua_State *L, const char *where) { + TValue *errobj = s2v(L->top - 1); /* error object */ + const char *msg = (ttisstring(errobj)) + ? svalue(errobj) + : "error object is not a string"; + /* produce warning "error in %s (%s)" (where, msg) */ + luaE_warning(L, "error in ", 1); + luaE_warning(L, where, 1); + luaE_warning(L, " (", 1); + luaE_warning(L, msg, 1); + luaE_warning(L, ")", 0); +} + diff --git a/Lua/lstate.h b/Lua/lstate.h new file mode 100644 index 00000000..c1c38204 --- /dev/null +++ b/Lua/lstate.h @@ -0,0 +1,400 @@ +/* +** $Id: lstate.h $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +#include "lua.h" + +#include "lobject.h" +#include "ltm.h" +#include "lzio.h" + + +/* +** Some notes about garbage-collected objects: All objects in Lua must +** be kept somehow accessible until being freed, so all objects always +** belong to one (and only one) of these lists, using field 'next' of +** the 'CommonHeader' for the link: +** +** 'allgc': all objects not marked for finalization; +** 'finobj': all objects marked for finalization; +** 'tobefnz': all objects ready to be finalized; +** 'fixedgc': all objects that are not to be collected (currently +** only small strings, such as reserved words). +** +** For the generational collector, some of these lists have marks for +** generations. Each mark points to the first element in the list for +** that particular generation; that generation goes until the next mark. +** +** 'allgc' -> 'survival': new objects; +** 'survival' -> 'old': objects that survived one collection; +** 'old1' -> 'reallyold': objects that became old in last collection; +** 'reallyold' -> NULL: objects old for more than one cycle. +** +** 'finobj' -> 'finobjsur': new objects marked for finalization; +** 'finobjsur' -> 'finobjold1': survived """"; +** 'finobjold1' -> 'finobjrold': just old """"; +** 'finobjrold' -> NULL: really old """". +** +** All lists can contain elements older than their main ages, due +** to 'luaC_checkfinalizer' and 'udata2finalize', which move +** objects between the normal lists and the "marked for finalization" +** lists. Moreover, barriers can age young objects in young lists as +** OLD0, which then become OLD1. However, a list never contains +** elements younger than their main ages. +** +** The generational collector also uses a pointer 'firstold1', which +** points to the first OLD1 object in the list. It is used to optimize +** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc' +** and 'reallyold', but often the list has no OLD1 objects or they are +** after 'old1'.) Note the difference between it and 'old1': +** 'firstold1': no OLD1 objects before this point; there can be all +** ages after it. +** 'old1': no objects younger than OLD1 after this point. +*/ + +/* +** Moreover, there is another set of lists that control gray objects. +** These lists are linked by fields 'gclist'. (All objects that +** can become gray have such a field. The field is not the same +** in all objects, but it always has this name.) Any gray object +** must belong to one of these lists, and all objects in these lists +** must be gray (with two exceptions explained below): +** +** 'gray': regular gray objects, still waiting to be visited. +** 'grayagain': objects that must be revisited at the atomic phase. +** That includes +** - black objects got in a write barrier; +** - all kinds of weak tables during propagation phase; +** - all threads. +** 'weak': tables with weak values to be cleared; +** 'ephemeron': ephemeron tables with white->white entries; +** 'allweak': tables with weak keys and/or weak values to be cleared. +** +** The exceptions to that "gray rule" are: +** - TOUCHED2 objects in generational mode stay in a gray list (because +** they must be visited again at the end of the cycle), but they are +** marked black because assignments to them must activate barriers (to +** move them back to TOUCHED1). +** - Open upvales are kept gray to avoid barriers, but they stay out +** of gray lists. (They don't even have a 'gclist' field.) +*/ + + + +/* +** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of +** how many "C calls" it still can do in the C stack, to avoid C-stack +** overflow. This count is very rough approximation; it considers only +** recursive functions inside the interpreter, as non-recursive calls +** can be considered using a fixed (although unknown) amount of stack +** space. +** +** The count has two parts: the lower part is the count itself; the +** higher part counts the number of non-yieldable calls in the stack. +** (They are together so that we can change both with one instruction.) +** +** Because calls to external C functions can use an unknown amount +** of space (e.g., functions using an auxiliary buffer), calls +** to these functions add more than one to the count (see CSTACKCF). +** +** The proper count excludes the number of CallInfo structures allocated +** by Lua, as a kind of "potential" calls. So, when Lua calls a function +** (and "consumes" one CallInfo), it needs neither to decrement nor to +** check 'nCcalls', as its use of C stack is already accounted for. +*/ + +/* number of "C stack slots" used by an external C function */ +#define CSTACKCF 10 + + +/* +** The C-stack size is sliced in the following zones: +** - larger than CSTACKERR: normal stack; +** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow; +** - [CSTACKCF, CSTACKERRMARK]: error-handling zone; +** - below CSTACKERRMARK: buffer zone to signal overflow during overflow; +** (Because the counter can be decremented CSTACKCF at once, we need +** the so called "buffer zones", with at least that size, to properly +** detect a change from one zone to the next.) +*/ +#define CSTACKERR (8 * CSTACKCF) +#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2)) +#define CSTACKERRMARK (CSTACKCF + 2) + + +/* initial limit for the C-stack of threads */ +#define CSTACKTHREAD (2 * CSTACKERR) + + +/* true if this thread does not have non-yieldable calls in the stack */ +#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) + +/* real number of C calls */ +#define getCcalls(L) ((L)->nCcalls & 0xffff) + + +/* Increment the number of non-yieldable calls */ +#define incnny(L) ((L)->nCcalls += 0x10000) + +/* Decrement the number of non-yieldable calls */ +#define decnny(L) ((L)->nCcalls -= 0x10000) + +/* Increment the number of non-yieldable calls and decrement nCcalls */ +#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF) + +/* Decrement the number of non-yieldable calls and increment nCcalls */ +#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF) + + + + + + +struct lua_longjmp; /* defined in ldo.c */ + + +/* +** Atomic type (relative to signals) to better ensure that 'lua_sethook' +** is thread safe +*/ +#if !defined(l_signalT) +#include +#define l_signalT sig_atomic_t +#endif + + +/* extra stack space to handle TM calls and some other extras */ +#define EXTRA_STACK 5 + + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + + +/* kinds of Garbage Collection */ +#define KGC_INC 0 /* incremental gc */ +#define KGC_GEN 1 /* generational gc */ + + +typedef struct stringtable { + TString **hash; + int nuse; /* number of elements */ + int size; +} stringtable; + + +/* +** Information about a call. +*/ +typedef struct CallInfo { + StkId func; /* function index in the stack */ + StkId top; /* top for this function */ + struct CallInfo *previous, *next; /* dynamic call link */ + union { + struct { /* only for Lua functions */ + const Instruction *savedpc; + volatile l_signalT trap; + int nextraargs; /* # of extra arguments in vararg functions */ + } l; + struct { /* only for C functions */ + lua_KFunction k; /* continuation in case of yields */ + ptrdiff_t old_errfunc; + lua_KContext ctx; /* context info. in case of yields */ + } c; + } u; + union { + int funcidx; /* called-function index */ + int nyield; /* number of values yielded */ + struct { /* info about transferred values (for call/return hooks) */ + unsigned short ftransfer; /* offset of first value transferred */ + unsigned short ntransfer; /* number of values transferred */ + } transferinfo; + } u2; + short nresults; /* expected number of results from this function */ + unsigned short callstatus; +} CallInfo; + + +/* +** Bits in CallInfo status +*/ +#define CIST_OAH (1<<0) /* original value of 'allowhook' */ +#define CIST_C (1<<1) /* call is running a C function */ +#define CIST_HOOKED (1<<2) /* call is running a debug hook */ +#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */ +#define CIST_TAIL (1<<4) /* call was tail called */ +#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */ +#define CIST_FIN (1<<6) /* call is running a finalizer */ +#define CIST_TRAN (1<<7) /* 'ci' has transfer information */ +#if defined(LUA_COMPAT_LT_LE) +#define CIST_LEQ (1<<8) /* using __lt for __le */ +#endif + +/* active function is a Lua function */ +#define isLua(ci) (!((ci)->callstatus & CIST_C)) + +/* call is running Lua code (not a hook) */ +#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED))) + +/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */ +#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v)) +#define getoah(st) ((st) & CIST_OAH) + + +/* +** 'global state', shared by all threads of this state +*/ +typedef struct global_State { + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to 'frealloc' */ + l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ + l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ + lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ + lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ + stringtable strt; /* hash table for strings */ + TValue l_registry; + TValue nilvalue; /* a nil value */ + unsigned int seed; /* randomized seed for hashes */ + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + lu_byte gckind; /* kind of GC running */ + lu_byte genminormul; /* control for minor generational collections */ + lu_byte genmajormul; /* control for major generational collections */ + lu_byte gcrunning; /* true if GC is running */ + lu_byte gcemergency; /* true if this is an emergency collection */ + lu_byte gcpause; /* size of pause between successive GCs */ + lu_byte gcstepmul; /* GC "speed" */ + lu_byte gcstepsize; /* (log2 of) GC granularity */ + GCObject *allgc; /* list of all collectable objects */ + GCObject **sweepgc; /* current position of sweep in list */ + GCObject *finobj; /* list of collectable objects with finalizers */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of tables with weak values */ + GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ + GCObject *allweak; /* list of all-weak tables */ + GCObject *tobefnz; /* list of userdata to be GC */ + GCObject *fixedgc; /* list of objects not to be collected */ + /* fields for generational collector */ + GCObject *survival; /* start of objects that survived one GC cycle */ + GCObject *old1; /* start of old1 objects */ + GCObject *reallyold; /* objects more than one cycle old ("really old") */ + GCObject *firstold1; /* first OLD1 object in the list (if any) */ + GCObject *finobjsur; /* list of survival objects with finalizers */ + GCObject *finobjold1; /* list of old1 objects with finalizers */ + GCObject *finobjrold; /* list of really old objects with finalizers */ + struct lua_State *twups; /* list of threads with open upvalues */ + lua_CFunction panic; /* to be called in unprotected errors */ + struct lua_State *mainthread; + TString *memerrmsg; /* message for memory-allocation errors */ + TString *tmname[TM_N]; /* array with tag-method names */ + struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ + TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ + lua_WarnFunction warnf; /* warning function */ + void *ud_warn; /* auxiliary data to 'warnf' */ + unsigned int Cstacklimit; /* current limit for the C stack */ +} global_State; + + +/* +** 'per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte status; + lu_byte allowhook; + unsigned short nci; /* number of items in 'ci' list */ + StkId top; /* first free slot in the stack */ + global_State *l_G; + CallInfo *ci; /* call info for current function */ + StkId stack_last; /* last free slot in the stack */ + StkId stack; /* stack base */ + UpVal *openupval; /* list of open upvalues in this stack */ + GCObject *gclist; + struct lua_State *twups; /* list of threads with open upvalues */ + struct lua_longjmp *errorJmp; /* current error recover point */ + CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ + volatile lua_Hook hook; + ptrdiff_t errfunc; /* current error handling function (stack index) */ + l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */ + int oldpc; /* last pc traced */ + int stacksize; + int basehookcount; + int hookcount; + volatile l_signalT hookmask; +}; + + +#define G(L) (L->l_G) + + +/* +** Union of all collectable objects (only for conversions) +** ISO C99, 6.5.2.3 p.5: +** "if a union contains several structures that share a common initial +** sequence [...], and if the union object currently contains one +** of these structures, it is permitted to inspect the common initial +** part of any of them anywhere that a declaration of the complete type +** of the union is visible." +*/ +union GCUnion { + GCObject gc; /* common header */ + struct TString ts; + struct Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct lua_State th; /* thread */ + struct UpVal upv; +}; + + +/* +** ISO C99, 6.7.2.1 p.14: +** "A pointer to a union object, suitably converted, points to each of +** its members [...], and vice versa." +*/ +#define cast_u(o) cast(union GCUnion *, (o)) + +/* macros to convert a GCObject into a specific value */ +#define gco2ts(o) \ + check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) +#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u)) +#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l)) +#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c)) +#define gco2cl(o) \ + check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) +#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h)) +#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p)) +#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th)) +#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv)) + + +/* +** macro to convert a Lua object into a GCObject +** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.) +*/ +#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) + + +/* actual number of total bytes allocated */ +#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) + +LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); +LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); +LUAI_FUNC void luaE_freeCI (lua_State *L); +LUAI_FUNC void luaE_shrinkCI (lua_State *L); +LUAI_FUNC void luaE_enterCcall (lua_State *L); +LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); +LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); + + +#define luaE_exitCcall(L) ((L)->nCcalls++) + +#endif + diff --git a/Lua/lstring.c b/Lua/lstring.c new file mode 100644 index 00000000..6f157473 --- /dev/null +++ b/Lua/lstring.c @@ -0,0 +1,285 @@ +/* +** $Id: lstring.c $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#define lstring_c +#define LUA_CORE + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" + + +/* +** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a long string to +** compute its hash +*/ +#if !defined(LUAI_HASHLIMIT) +#define LUAI_HASHLIMIT 5 +#endif + + + +/* +** Maximum size for string table. +*/ +#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*)) + + +/* +** equality for long strings +*/ +int luaS_eqlngstr (TString *a, TString *b) { + size_t len = a->u.lnglen; + lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); + return (a == b) || /* same instance or... */ + ((len == b->u.lnglen) && /* equal length and ... */ + (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ +} + + +unsigned int luaS_hash (const char *str, size_t l, unsigned int seed, + size_t step) { + unsigned int h = seed ^ cast_uint(l); + for (; l >= step; l -= step) + h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); + return h; +} + + +unsigned int luaS_hashlongstr (TString *ts) { + lua_assert(ts->tt == LUA_VLNGSTR); + if (ts->extra == 0) { /* no hash? */ + size_t len = ts->u.lnglen; + size_t step = (len >> LUAI_HASHLIMIT) + 1; + ts->hash = luaS_hash(getstr(ts), len, ts->hash, step); + ts->extra = 1; /* now it has its hash */ + } + return ts->hash; +} + + +static void tablerehash (TString **vect, int osize, int nsize) { + int i; + for (i = osize; i < nsize; i++) /* clear new elements */ + vect[i] = NULL; + for (i = 0; i < osize; i++) { /* rehash old part of the array */ + TString *p = vect[i]; + vect[i] = NULL; + while (p) { /* for each string in the list */ + TString *hnext = p->u.hnext; /* save next */ + unsigned int h = lmod(p->hash, nsize); /* new position */ + p->u.hnext = vect[h]; /* chain it into array */ + vect[h] = p; + p = hnext; + } + } +} + + +/* +** Resize the string table. If allocation fails, keep the current size. +** (This can degrade performance, but any non-zero size should work +** correctly.) +*/ +void luaS_resize (lua_State *L, int nsize) { + stringtable *tb = &G(L)->strt; + int osize = tb->size; + TString **newvect; + if (nsize < osize) /* shrinking table? */ + tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */ + newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*); + if (unlikely(newvect == NULL)) { /* reallocation failed? */ + if (nsize < osize) /* was it shrinking table? */ + tablerehash(tb->hash, nsize, osize); /* restore to original size */ + /* leave table as it was */ + } + else { /* allocation succeeded */ + tb->hash = newvect; + tb->size = nsize; + if (nsize > osize) + tablerehash(newvect, osize, nsize); /* rehash for new size */ + } +} + + +/* +** Clear API string cache. (Entries cannot be empty, so fill them with +** a non-collectable string.) +*/ +void luaS_clearcache (global_State *g) { + int i, j; + for (i = 0; i < STRCACHE_N; i++) + for (j = 0; j < STRCACHE_M; j++) { + if (iswhite(g->strcache[i][j])) /* will entry be collected? */ + g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ + } +} + + +/* +** Initialize the string table and the string cache +*/ +void luaS_init (lua_State *L) { + global_State *g = G(L); + int i, j; + stringtable *tb = &G(L)->strt; + tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*); + tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */ + tb->size = MINSTRTABSIZE; + /* pre-create memory-error message */ + g->memerrmsg = luaS_newliteral(L, MEMERRMSG); + luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ + for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ + for (j = 0; j < STRCACHE_M; j++) + g->strcache[i][j] = g->memerrmsg; +} + + + +/* +** creates a new string object +*/ +static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { + TString *ts; + GCObject *o; + size_t totalsize; /* total size of TString object */ + totalsize = sizelstring(l); + o = luaC_newobj(L, tag, totalsize); + ts = gco2ts(o); + ts->hash = h; + ts->extra = 0; + getstr(ts)[l] = '\0'; /* ending 0 */ + return ts; +} + + +TString *luaS_createlngstrobj (lua_State *L, size_t l) { + TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed); + ts->u.lnglen = l; + return ts; +} + + +void luaS_remove (lua_State *L, TString *ts) { + stringtable *tb = &G(L)->strt; + TString **p = &tb->hash[lmod(ts->hash, tb->size)]; + while (*p != ts) /* find previous element */ + p = &(*p)->u.hnext; + *p = (*p)->u.hnext; /* remove element from its list */ + tb->nuse--; +} + + +static void growstrtab (lua_State *L, stringtable *tb) { + if (unlikely(tb->nuse == MAX_INT)) { /* too many strings? */ + luaC_fullgc(L, 1); /* try to free some... */ + if (tb->nuse == MAX_INT) /* still too many? */ + luaM_error(L); /* cannot even create a message... */ + } + if (tb->size <= MAXSTRTB / 2) /* can grow string table? */ + luaS_resize(L, tb->size * 2); +} + + +/* +** Checks whether short string exists and reuses it or creates a new one. +*/ +static TString *internshrstr (lua_State *L, const char *str, size_t l) { + TString *ts; + global_State *g = G(L); + stringtable *tb = &g->strt; + unsigned int h = luaS_hash(str, l, g->seed, 1); + TString **list = &tb->hash[lmod(h, tb->size)]; + lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ + for (ts = *list; ts != NULL; ts = ts->u.hnext) { + if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { + /* found! */ + if (isdead(g, ts)) /* dead (but not collected yet)? */ + changewhite(ts); /* resurrect it */ + return ts; + } + } + /* else must create a new string */ + if (tb->nuse >= tb->size) { /* need to grow string table? */ + growstrtab(L, tb); + list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ + } + ts = createstrobj(L, l, LUA_VSHRSTR, h); + memcpy(getstr(ts), str, l * sizeof(char)); + ts->shrlen = cast_byte(l); + ts->u.hnext = *list; + *list = ts; + tb->nuse++; + return ts; +} + + +/* +** new string (with explicit length) +*/ +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + if (l <= LUAI_MAXSHORTLEN) /* short string? */ + return internshrstr(L, str, l); + else { + TString *ts; + if (unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char))) + luaM_toobig(L); + ts = luaS_createlngstrobj(L, l); + memcpy(getstr(ts), str, l * sizeof(char)); + return ts; + } +} + + +/* +** Create or reuse a zero-terminated string, first checking in the +** cache (using the string address as a key). The cache can contain +** only zero-terminated strings, so it is safe to use 'strcmp' to +** check hits. +*/ +TString *luaS_new (lua_State *L, const char *str) { + unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ + int j; + TString **p = G(L)->strcache[i]; + for (j = 0; j < STRCACHE_M; j++) { + if (strcmp(str, getstr(p[j])) == 0) /* hit? */ + return p[j]; /* that is it */ + } + /* normal route */ + for (j = STRCACHE_M - 1; j > 0; j--) + p[j] = p[j - 1]; /* move out last element */ + /* new element is first in the list */ + p[0] = luaS_newlstr(L, str, strlen(str)); + return p[0]; +} + + +Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) { + Udata *u; + int i; + GCObject *o; + if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue))) + luaM_toobig(L); + o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s)); + u = gco2u(o); + u->len = s; + u->nuvalue = nuvalue; + u->metatable = NULL; + for (i = 0; i < nuvalue; i++) + setnilvalue(&u->uv[i].uv); + return u; +} + diff --git a/Lua/lstring.h b/Lua/lstring.h new file mode 100644 index 00000000..a413a9d3 --- /dev/null +++ b/Lua/lstring.h @@ -0,0 +1,58 @@ +/* +** $Id: lstring.h $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" + + +/* +** Memory-allocation error message must be preallocated (it cannot +** be created after memory is exhausted) +*/ +#define MEMERRMSG "not enough memory" + + +/* +** Size of a TString: Size of the header plus space for the string +** itself (including final '\0'). +*/ +#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char)) + +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + + +/* +** test whether a string is a reserved word +*/ +#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0) + + +/* +** equality for short strings, which are always internalized +*/ +#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) + + +LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, + unsigned int seed, size_t step); +LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); +LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC void luaS_clearcache (global_State *g); +LUAI_FUNC void luaS_init (lua_State *L); +LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); +LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); +LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); + + +#endif diff --git a/Lua/lstrlib.c b/Lua/lstrlib.c new file mode 100644 index 00000000..2ba8bde4 --- /dev/null +++ b/Lua/lstrlib.c @@ -0,0 +1,1805 @@ +/* +** $Id: lstrlib.c $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + +#define lstrlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* +** maximum number of captures that a pattern can do during +** pattern-matching. This limit is arbitrary, but must fit in +** an unsigned char. +*/ +#if !defined(LUA_MAXCAPTURES) +#define LUA_MAXCAPTURES 32 +#endif + + +/* macro to 'unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + +/* +** Some sizes are better limited to fit in 'int', but must also fit in +** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) +*/ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +#define MAXSIZE \ + (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) + + + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, (lua_Integer)l); + return 1; +} + + +/* +** translate a relative initial string position +** (negative means back from end): clip result to [1, inf). +** The length of any string in Lua must fit in a lua_Integer, +** so there are no overflows in the casts. +** The inverted comparison avoids a possible overflow +** computing '-pos'. +*/ +static size_t posrelatI (lua_Integer pos, size_t len) { + if (pos > 0) + return (size_t)pos; + else if (pos == 0) + return 1; + else if (pos < -(lua_Integer)len) /* inverted comparison */ + return 1; /* clip to 1 */ + else return len + (size_t)pos + 1; +} + + +/* +** Gets an optional ending string position from argument 'arg', +** with default value 'def'. +** Negative means back from end: clip result to [0, len] +*/ +static size_t getendpos (lua_State *L, int arg, lua_Integer def, + size_t len) { + lua_Integer pos = luaL_optinteger(L, arg, def); + if (pos > (lua_Integer)len) + return len; + else if (pos >= 0) + return (size_t)pos; + else if (pos < -(lua_Integer)len) + return 0; + else return len + (size_t)pos + 1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t start = posrelatI(luaL_checkinteger(L, 2), l); + size_t end = getendpos(L, 3, -1, l); + if (start <= end) + lua_pushlstring(L, s + start - 1, (end - start) + 1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_reverse (lua_State *L) { + size_t l, i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i = 0; i < l; i++) + p[i] = s[l - i - 1]; + luaL_pushresultsize(&b, l); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i=0; i MAXSIZE / n) /* may overflow? */ + return luaL_error(L, "resulting string too large"); + else { + size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; + luaL_Buffer b; + char *p = luaL_buffinitsize(L, &b, totallen); + while (n-- > 1) { /* first n-1 copies (followed by separator) */ + memcpy(p, s, l * sizeof(char)); p += l; + if (lsep > 0) { /* empty 'memcpy' is not that cheap */ + memcpy(p, sep, lsep * sizeof(char)); + p += lsep; + } + } + memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ + luaL_pushresultsize(&b, totallen); + } + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + lua_Integer pi = luaL_optinteger(L, 2, 1); + size_t posi = posrelatI(pi, l); + size_t pose = getendpos(L, 3, pi, l); + int n, i; + if (posi > pose) return 0; /* empty interval; return no values */ + if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; + luaL_checkstack(L, n, "string slice too long"); + for (i=0; iinit) { + state->init = 1; + luaL_buffinit(L, &state->B); + } + luaL_addlstring(&state->B, (const char *)b, size); + return 0; +} + + +static int str_dump (lua_State *L) { + struct str_Writer state; + int strip = lua_toboolean(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 1); /* ensure function is on the top of the stack */ + state.init = 0; + if (lua_dump(L, writer, &state, strip) != 0) + return luaL_error(L, "unable to dump given function"); + luaL_pushresult(&state.B); + return 1; +} + + + +/* +** {====================================================== +** METAMETHODS +** ======================================================= +*/ + +#if defined(LUA_NOCVTS2N) /* { */ + +/* no coercion from strings to numbers */ + +static const luaL_Reg stringmetamethods[] = { + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#else /* }{ */ + +static int tonum (lua_State *L, int arg) { + if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */ + lua_pushvalue(L, arg); + return 1; + } + else { /* check whether it is a numerical string */ + size_t len; + const char *s = lua_tolstring(L, arg, &len); + return (s != NULL && lua_stringtonumber(L, s) == len + 1); + } +} + + +static void trymt (lua_State *L, const char *mtname) { + lua_settop(L, 2); /* back to the original arguments */ + if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname)) + luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, + luaL_typename(L, -2), luaL_typename(L, -1)); + lua_insert(L, -3); /* put metamethod before arguments */ + lua_call(L, 2, 1); /* call metamethod */ +} + + +static int arith (lua_State *L, int op, const char *mtname) { + if (tonum(L, 1) && tonum(L, 2)) + lua_arith(L, op); /* result will be on the top */ + else + trymt(L, mtname); + return 1; +} + + +static int arith_add (lua_State *L) { + return arith(L, LUA_OPADD, "__add"); +} + +static int arith_sub (lua_State *L) { + return arith(L, LUA_OPSUB, "__sub"); +} + +static int arith_mul (lua_State *L) { + return arith(L, LUA_OPMUL, "__mul"); +} + +static int arith_mod (lua_State *L) { + return arith(L, LUA_OPMOD, "__mod"); +} + +static int arith_pow (lua_State *L) { + return arith(L, LUA_OPPOW, "__pow"); +} + +static int arith_div (lua_State *L) { + return arith(L, LUA_OPDIV, "__div"); +} + +static int arith_idiv (lua_State *L) { + return arith(L, LUA_OPIDIV, "__idiv"); +} + +static int arith_unm (lua_State *L) { + return arith(L, LUA_OPUNM, "__unm"); +} + + +static const luaL_Reg stringmetamethods[] = { + {"__add", arith_add}, + {"__sub", arith_sub}, + {"__mul", arith_mul}, + {"__mod", arith_mod}, + {"__pow", arith_pow}, + {"__div", arith_div}, + {"__idiv", arith_idiv}, + {"__unm", arith_unm}, + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#endif /* } */ + +/* }====================================================== */ + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + + +typedef struct MatchState { + const char *src_init; /* init of source string */ + const char *src_end; /* end ('\0') of source string */ + const char *p_end; /* end ('\0') of pattern */ + lua_State *L; + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ + unsigned char level; /* total number of captures (finished or unfinished) */ + struct { + const char *init; + ptrdiff_t len; + } capture[LUA_MAXCAPTURES]; +} MatchState; + + +/* recursive function */ +static const char *match (MatchState *ms, const char *s, const char *p); + + +/* maximum recursion depth for 'match' */ +#if !defined(MAXCCALLS) +#define MAXCCALLS 200 +#endif + + +#define L_ESC '%' +#define SPECIALS "^$*+?.([%-" + + +static int check_capture (MatchState *ms, int l) { + l -= '1'; + if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index %%%d", l + 1); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (p == ms->p_end) + luaL_error(ms->L, "malformed pattern (ends with '%%')"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a ']' */ + if (p == ms->p_end) + luaL_error(ms->L, "malformed pattern (missing ']')"); + if (*(p++) == L_ESC && p < ms->p_end) + p++; /* skip escapes (e.g. '%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'g' : res = isgraph(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; /* deprecated option */ + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the '^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int singlematch (MatchState *ms, const char *s, const char *p, + const char *ep) { + if (s >= ms->src_end) + return 0; + else { + int c = uchar(*s); + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } + } +} + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (p >= ms->p_end - 1) + luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while (singlematch(ms, s + i, p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (singlematch(ms, s, p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + if (ms->matchdepth-- == 0) + luaL_error(ms->L, "pattern too complex"); + init: /* using goto's to optimize tail recursion */ + if (p != ms->p_end) { /* end of pattern? */ + switch (*p) { + case '(': { /* start capture */ + if (*(p + 1) == ')') /* position capture? */ + s = start_capture(ms, s, p + 2, CAP_POSITION); + else + s = start_capture(ms, s, p + 1, CAP_UNFINISHED); + break; + } + case ')': { /* end capture */ + s = end_capture(ms, s, p + 1); + break; + } + case '$': { + if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ + goto dflt; /* no; go to default */ + s = (s == ms->src_end) ? s : NULL; /* check end of string */ + break; + } + case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ + switch (*(p + 1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p + 2); + if (s != NULL) { + p += 4; goto init; /* return match(ms, s, p + 4); */ + } /* else fail (s == NULL) */ + break; + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing '[' after '%%f' in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s - 1); + if (!matchbracketclass(uchar(previous), p, ep - 1) && + matchbracketclass(uchar(*s), p, ep - 1)) { + p = ep; goto init; /* return match(ms, s, ep); */ + } + s = NULL; /* match failed */ + break; + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p + 1))); + if (s != NULL) { + p += 2; goto init; /* return match(ms, s, p + 2) */ + } + break; + } + default: goto dflt; + } + break; + } + default: dflt: { /* pattern class plus optional suffix */ + const char *ep = classend(ms, p); /* points to optional suffix */ + /* does not match at least once? */ + if (!singlematch(ms, s, p, ep)) { + if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ + p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ + } + else /* '+' or no suffix */ + s = NULL; /* fail */ + } + else { /* matched once */ + switch (*ep) { /* handle optional suffix */ + case '?': { /* optional */ + const char *res; + if ((res = match(ms, s + 1, ep + 1)) != NULL) + s = res; + else { + p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ + } + break; + } + case '+': /* 1 or more repetitions */ + s++; /* 1 match already done */ + /* FALLTHROUGH */ + case '*': /* 0 or more repetitions */ + s = max_expand(ms, s, p, ep); + break; + case '-': /* 0 or more repetitions (minimum) */ + s = min_expand(ms, s, p, ep); + break; + default: /* no suffix */ + s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ + } + } + break; + } + } + } + ms->matchdepth++; + return s; +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ + else { + const char *init; /* to search for a '*s2' inside 's1' */ + l2--; /* 1st char will be checked by 'memchr' */ + l1 = l1-l2; /* 's2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct 'l1' and 's1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +/* +** get information about the i-th capture. If there are no captures +** and 'i==0', return information about the whole match, which +** is the range 's'..'e'. If the capture is a string, return +** its length and put its address in '*cap'. If it is an integer +** (a position), push it on the stack and return CAP_POSITION. +*/ +static size_t get_onecapture (MatchState *ms, int i, const char *s, + const char *e, const char **cap) { + if (i >= ms->level) { + if (i != 0) + luaL_error(ms->L, "invalid capture index %%%d", i + 1); + *cap = s; + return e - s; + } + else { + ptrdiff_t capl = ms->capture[i].len; + *cap = ms->capture[i].init; + if (capl == CAP_UNFINISHED) + luaL_error(ms->L, "unfinished capture"); + else if (capl == CAP_POSITION) + lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); + return capl; + } +} + + +/* +** Push the i-th capture on the stack. +*/ +static void push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + const char *cap; + ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); + if (l != CAP_POSITION) + lua_pushlstring(ms->L, cap, l); + /* else position was already pushed */ +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +/* check whether pattern has no special characters */ +static int nospecials (const char *p, size_t l) { + size_t upto = 0; + do { + if (strpbrk(p + upto, SPECIALS)) + return 0; /* pattern has a special character */ + upto += strlen(p + upto) + 1; /* may have more after \0 */ + } while (upto <= l); + return 1; /* no special chars found */ +} + + +static void prepstate (MatchState *ms, lua_State *L, + const char *s, size_t ls, const char *p, size_t lp) { + ms->L = L; + ms->matchdepth = MAXCCALLS; + ms->src_init = s; + ms->src_end = s + ls; + ms->p_end = p + lp; +} + + +static void reprepstate (MatchState *ms) { + ms->level = 0; + lua_assert(ms->matchdepth == MAXCCALLS); +} + + +static int str_find_aux (lua_State *L, int find) { + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + if (init > ls) { /* start after string's end? */ + luaL_pushfail(L); /* cannot find anything */ + return 1; + } + /* explicit request or no special characters? */ + if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { + /* do a plain search */ + const char *s2 = lmemfind(s + init, ls - init, p, lp); + if (s2) { + lua_pushinteger(L, (s2 - s) + 1); + lua_pushinteger(L, (s2 - s) + lp); + return 2; + } + } + else { + MatchState ms; + const char *s1 = s + init; + int anchor = (*p == '^'); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + prepstate(&ms, L, s, ls, p, lp); + do { + const char *res; + reprepstate(&ms); + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, (s1 - s) + 1); /* start */ + lua_pushinteger(L, res - s); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + luaL_pushfail(L); /* not found */ + return 1; +} + + +static int str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +/* state for 'gmatch' */ +typedef struct GMatchState { + const char *src; /* current position */ + const char *p; /* pattern */ + const char *lastmatch; /* end of last match */ + MatchState ms; /* match state */ +} GMatchState; + + +static int gmatch_aux (lua_State *L) { + GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); + const char *src; + gm->ms.L = L; + for (src = gm->src; src <= gm->ms.src_end; src++) { + const char *e; + reprepstate(&gm->ms); + if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) { + gm->src = gm->lastmatch = e; + return push_captures(&gm->ms, src, e); + } + } + return 0; /* not found */ +} + + +static int gmatch (lua_State *L) { + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + GMatchState *gm; + lua_settop(L, 2); /* keep strings on closure to avoid being collected */ + gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0); + if (init > ls) /* start after string's end? */ + init = ls + 1; /* avoid overflows in 's + init' */ + prepstate(&gm->ms, L, s, ls, p, lp); + gm->src = s + init; gm->p = p; gm->lastmatch = NULL; + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + + +static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l; + lua_State *L = ms->L; + const char *news = lua_tolstring(L, 3, &l); + const char *p; + while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { + luaL_addlstring(b, news, p - news); + p++; /* skip ESC */ + if (*p == L_ESC) /* '%%' */ + luaL_addchar(b, *p); + else if (*p == '0') /* '%0' */ + luaL_addlstring(b, s, e - s); + else if (isdigit(uchar(*p))) { /* '%n' */ + const char *cap; + ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); + if (resl == CAP_POSITION) + luaL_addvalue(b); /* add position to accumulated result */ + else + luaL_addlstring(b, cap, resl); + } + else + luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); + l -= p + 1 - news; + news = p + 1; + } + luaL_addlstring(b, news, l); +} + + +/* +** Add the replacement value to the string buffer 'b'. +** Return true if the original string was changed. (Function calls and +** table indexing resulting in nil or false do not change the subject.) +*/ +static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e, int tr) { + lua_State *L = ms->L; + switch (tr) { + case LUA_TFUNCTION: { /* call the function */ + int n; + lua_pushvalue(L, 3); /* push the function */ + n = push_captures(ms, s, e); /* all captures as arguments */ + lua_call(L, n, 1); /* call it */ + break; + } + case LUA_TTABLE: { /* index the table */ + push_onecapture(ms, 0, s, e); /* first capture is the index */ + lua_gettable(L, 3); + break; + } + default: { /* LUA_TNUMBER or LUA_TSTRING */ + add_s(ms, b, s, e); /* add value to the buffer */ + return 1; /* something changed */ + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); /* remove value */ + luaL_addlstring(b, s, e - s); /* keep original text */ + return 0; /* no changes */ + } + else if (!lua_isstring(L, -1)) + return luaL_error(L, "invalid replacement value (a %s)", + luaL_typename(L, -1)); + else { + luaL_addvalue(b); /* add result to accumulator */ + return 1; /* something changed */ + } +} + + +static int str_gsub (lua_State *L) { + size_t srcl, lp; + const char *src = luaL_checklstring(L, 1, &srcl); /* subject */ + const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ + const char *lastmatch = NULL; /* end of last match */ + int tr = lua_type(L, 3); /* replacement type */ + lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ + int anchor = (*p == '^'); + lua_Integer n = 0; /* replacement count */ + int changed = 0; /* change flag */ + MatchState ms; + luaL_Buffer b; + luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table"); + luaL_buffinit(L, &b); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + prepstate(&ms, L, src, srcl, p, lp); + while (n < max_s) { + const char *e; + reprepstate(&ms); /* (re)prepare state for new match */ + if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ + n++; + changed = add_value(&ms, &b, src, e, tr) | changed; + src = lastmatch = e; + } + else if (src < ms.src_end) /* otherwise, skip one character */ + luaL_addchar(&b, *src++); + else break; /* end of subject */ + if (anchor) break; + } + if (!changed) /* no changes? */ + lua_pushvalue(L, 1); /* return original string */ + else { /* something changed */ + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); /* create and return new string */ + } + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** STRING FORMAT +** ======================================================= +*/ + +#if !defined(lua_number2strx) /* { */ + +/* +** Hexadecimal floating-point formatter +*/ + +#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) + + +/* +** Number of bits that goes into the first digit. It can be any value +** between 1 and 4; the following definition tries to align the number +** to nibble boundaries by making what is left after that first digit a +** multiple of 4. +*/ +#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1) + + +/* +** Add integer part of 'x' to buffer and return new 'x' +*/ +static lua_Number adddigit (char *buff, int n, lua_Number x) { + lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ + int d = (int)dd; + buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ + return x - dd; /* return what is left */ +} + + +static int num2straux (char *buff, int sz, lua_Number x) { + /* if 'inf' or 'NaN', format it like '%g' */ + if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL) + return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x); + else if (x == 0) { /* can be -0... */ + /* create "0" or "-0" followed by exponent */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x); + } + else { + int e; + lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ + int n = 0; /* character count */ + if (m < 0) { /* is number negative? */ + buff[n++] = '-'; /* add sign */ + m = -m; /* make it positive */ + } + buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */ + m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */ + e -= L_NBFD; /* this digit goes before the radix point */ + if (m > 0) { /* more digits? */ + buff[n++] = lua_getlocaledecpoint(); /* add radix point */ + do { /* add as many digits as needed */ + m = adddigit(buff, n++, m * 16); + } while (m > 0); + } + n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ + lua_assert(n < sz); + return n; + } +} + + +static int lua_number2strx (lua_State *L, char *buff, int sz, + const char *fmt, lua_Number x) { + int n = num2straux(buff, sz, x); + if (fmt[SIZELENMOD] == 'A') { + int i; + for (i = 0; i < n; i++) + buff[i] = toupper(uchar(buff[i])); + } + else if (fmt[SIZELENMOD] != 'a') + return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); + return n; +} + +#endif /* } */ + + +/* +** Maximum size for items formatted with '%f'. This size is produced +** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', +** and '\0') + number of decimal digits to represent maxfloat (which +** is maximum exponent + 1). (99+3+1, adding some extra, 110) +*/ +#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP)) + + +/* +** All formats except '%f' do not need that large limit. The other +** float formats use exponents, so that they fit in the 99 limit for +** significant digits; 's' for large strings and 'q' add items directly +** to the buffer; all integer formats also fit in the 99 limit. The +** worst case are floats: they may need 99 significant digits, plus +** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120. +*/ +#define MAX_ITEM 120 + + +/* valid flags in a format specification */ +#if !defined(L_FMTFLAGS) +#define L_FMTFLAGS "-+ #0" +#endif + + +/* +** maximum size of each format specification (such as "%-099.99d") +*/ +#define MAX_FORMAT 32 + + +static void addquoted (luaL_Buffer *b, const char *s, size_t len) { + luaL_addchar(b, '"'); + while (len--) { + if (*s == '"' || *s == '\\' || *s == '\n') { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + } + else if (iscntrl(uchar(*s))) { + char buff[10]; + if (!isdigit(uchar(*(s+1)))) + l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); + else + l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); + luaL_addstring(b, buff); + } + else + luaL_addchar(b, *s); + s++; + } + luaL_addchar(b, '"'); +} + + +/* +** Serialize a floating-point number in such a way that it can be +** scanned back by Lua. Use hexadecimal format for "common" numbers +** (to preserve precision); inf, -inf, and NaN are handled separately. +** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.) +*/ +static int quotefloat (lua_State *L, char *buff, lua_Number n) { + const char *s; /* for the fixed representations */ + if (n == (lua_Number)HUGE_VAL) /* inf? */ + s = "1e9999"; + else if (n == -(lua_Number)HUGE_VAL) /* -inf? */ + s = "-1e9999"; + else if (n != n) /* NaN? */ + s = "(0/0)"; + else { /* format number as hexadecimal */ + int nb = lua_number2strx(L, buff, MAX_ITEM, + "%" LUA_NUMBER_FRMLEN "a", n); + /* ensures that 'buff' string uses a dot as the radix character */ + if (memchr(buff, '.', nb) == NULL) { /* no dot? */ + char point = lua_getlocaledecpoint(); /* try locale point */ + char *ppoint = (char *)memchr(buff, point, nb); + if (ppoint) *ppoint = '.'; /* change it to a dot */ + } + return nb; + } + /* for the fixed representations */ + return l_sprintf(buff, MAX_ITEM, "%s", s); +} + + +static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { + switch (lua_type(L, arg)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(L, arg, &len); + addquoted(b, s, len); + break; + } + case LUA_TNUMBER: { + char *buff = luaL_prepbuffsize(b, MAX_ITEM); + int nb; + if (!lua_isinteger(L, arg)) /* float? */ + nb = quotefloat(L, buff, lua_tonumber(L, arg)); + else { /* integers */ + lua_Integer n = lua_tointeger(L, arg); + const char *format = (n == LUA_MININTEGER) /* corner case? */ + ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */ + : LUA_INTEGER_FMT; /* else use default format */ + nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n); + } + luaL_addsize(b, nb); + break; + } + case LUA_TNIL: case LUA_TBOOLEAN: { + luaL_tolstring(L, arg, NULL); + luaL_addvalue(b); + break; + } + default: { + luaL_argerror(L, arg, "value has no literal form"); + } + } +} + + +static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { + const char *p = strfrmt; + while (*p != '\0' && strchr(L_FMTFLAGS, *p) != NULL) p++; /* skip flags */ + if ((size_t)(p - strfrmt) >= sizeof(L_FMTFLAGS)/sizeof(char)) + luaL_error(L, "invalid format (repeated flags)"); + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + *(form++) = '%'; + memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char)); + form += (p - strfrmt) + 1; + *form = '\0'; + return p; +} + + +/* +** add length modifier into formats +*/ +static void addlenmod (char *form, const char *lenmod) { + size_t l = strlen(form); + size_t lm = strlen(lenmod); + char spec = form[l - 1]; + strcpy(form + l - 1, lenmod); + form[l + lm - 1] = spec; + form[l + lm] = '\0'; +} + + +static int str_format (lua_State *L) { + int top = lua_gettop(L); + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format ('%...') */ + int maxitem = MAX_ITEM; + char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */ + int nb = 0; /* number of bytes in added item */ + if (++arg > top) + return luaL_argerror(L, arg, "no value"); + strfrmt = scanformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg)); + break; + } + case 'd': case 'i': + case 'o': case 'u': case 'x': case 'X': { + lua_Integer n = luaL_checkinteger(L, arg); + addlenmod(form, LUA_INTEGER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n); + break; + } + case 'a': case 'A': + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = lua_number2strx(L, buff, maxitem, form, + luaL_checknumber(L, arg)); + break; + case 'f': + maxitem = MAX_ITEMF; /* extra space for '%f' */ + buff = luaL_prepbuffsize(&b, maxitem); + /* FALLTHROUGH */ + case 'e': case 'E': case 'g': case 'G': { + lua_Number n = luaL_checknumber(L, arg); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n); + break; + } + case 'p': { + const void *p = lua_topointer(L, arg); + if (p == NULL) { /* avoid calling 'printf' with argument NULL */ + p = "(null)"; /* result */ + form[strlen(form) - 1] = 's'; /* format it as a string */ + } + nb = l_sprintf(buff, maxitem, form, p); + break; + } + case 'q': { + if (form[2] != '\0') /* modifiers? */ + return luaL_error(L, "specifier '%%q' cannot have modifiers"); + addliteral(L, &b, arg); + break; + } + case 's': { + size_t l; + const char *s = luaL_tolstring(L, arg, &l); + if (form[2] == '\0') /* no modifiers? */ + luaL_addvalue(&b); /* keep entire string */ + else { + luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted */ + luaL_addvalue(&b); /* keep entire string */ + } + else { /* format the string into 'buff' */ + nb = l_sprintf(buff, maxitem, form, s); + lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + } + } + break; + } + default: { /* also treat cases 'pnLlh' */ + return luaL_error(L, "invalid conversion '%s' to 'format'", form); + } + } + lua_assert(nb < maxitem); + luaL_addsize(&b, nb); + } + } + luaL_pushresult(&b); + return 1; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** PACK/UNPACK +** ======================================================= +*/ + + +/* value used for padding */ +#if !defined(LUAL_PACKPADBYTE) +#define LUAL_PACKPADBYTE 0x00 +#endif + +/* maximum size for the binary representation of an integer */ +#define MAXINTSIZE 16 + +/* number of bits in a character */ +#define NB CHAR_BIT + +/* mask for one character (NB 1's) */ +#define MC ((1 << NB) - 1) + +/* size of a lua_Integer */ +#define SZINT ((int)sizeof(lua_Integer)) + + +/* dummy union to get native endianness */ +static const union { + int dummy; + char little; /* true iff machine is little endian */ +} nativeendian = {1}; + + +/* dummy structure to get native alignment requirements */ +struct cD { + char c; + union { double d; void *p; lua_Integer i; lua_Number n; } u; +}; + +#define MAXALIGN (offsetof(struct cD, u)) + + +/* +** Union for serializing floats +*/ +typedef union Ftypes { + float f; + double d; + lua_Number n; + char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ +} Ftypes; + + +/* +** information to pack/unpack stuff +*/ +typedef struct Header { + lua_State *L; + int islittle; + int maxalign; +} Header; + + +/* +** options for pack/unpack +*/ +typedef enum KOption { + Kint, /* signed integers */ + Kuint, /* unsigned integers */ + Kfloat, /* floating-point numbers */ + Kchar, /* fixed-length strings */ + Kstring, /* strings with prefixed length */ + Kzstr, /* zero-terminated strings */ + Kpadding, /* padding */ + Kpaddalign, /* padding for alignment */ + Knop /* no-op (configuration or spaces) */ +} KOption; + + +/* +** Read an integer numeral from string 'fmt' or return 'df' if +** there is no numeral +*/ +static int digit (int c) { return '0' <= c && c <= '9'; } + +static int getnum (const char **fmt, int df) { + if (!digit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + int a = 0; + do { + a = a*10 + (*((*fmt)++) - '0'); + } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); + return a; + } +} + + +/* +** Read an integer numeral and raises an error if it is larger +** than the maximum size for integers. +*/ +static int getnumlimit (Header *h, const char **fmt, int df) { + int sz = getnum(fmt, df); + if (sz > MAXINTSIZE || sz <= 0) + return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", + sz, MAXINTSIZE); + return sz; +} + + +/* +** Initialize Header +*/ +static void initheader (lua_State *L, Header *h) { + h->L = L; + h->islittle = nativeendian.little; + h->maxalign = 1; +} + + +/* +** Read and classify next option. 'size' is filled with option's size. +*/ +static KOption getoption (Header *h, const char **fmt, int *size) { + int opt = *((*fmt)++); + *size = 0; /* default */ + switch (opt) { + case 'b': *size = sizeof(char); return Kint; + case 'B': *size = sizeof(char); return Kuint; + case 'h': *size = sizeof(short); return Kint; + case 'H': *size = sizeof(short); return Kuint; + case 'l': *size = sizeof(long); return Kint; + case 'L': *size = sizeof(long); return Kuint; + case 'j': *size = sizeof(lua_Integer); return Kint; + case 'J': *size = sizeof(lua_Integer); return Kuint; + case 'T': *size = sizeof(size_t); return Kuint; + case 'f': *size = sizeof(float); return Kfloat; + case 'd': *size = sizeof(double); return Kfloat; + case 'n': *size = sizeof(lua_Number); return Kfloat; + case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; + case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; + case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; + case 'c': + *size = getnum(fmt, -1); + if (*size == -1) + luaL_error(h->L, "missing size for format option 'c'"); + return Kchar; + case 'z': return Kzstr; + case 'x': *size = 1; return Kpadding; + case 'X': return Kpaddalign; + case ' ': break; + case '<': h->islittle = 1; break; + case '>': h->islittle = 0; break; + case '=': h->islittle = nativeendian.little; break; + case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; + default: luaL_error(h->L, "invalid format option '%c'", opt); + } + return Knop; +} + + +/* +** Read, classify, and fill other details about the next option. +** 'psize' is filled with option's size, 'notoalign' with its +** alignment requirements. +** Local variable 'size' gets the size to be aligned. (Kpadal option +** always gets its full alignment, other options are limited by +** the maximum alignment ('maxalign'). Kchar option needs no alignment +** despite its size. +*/ +static KOption getdetails (Header *h, size_t totalsize, + const char **fmt, int *psize, int *ntoalign) { + KOption opt = getoption(h, fmt, psize); + int align = *psize; /* usually, alignment follows size */ + if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ + if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) + luaL_argerror(h->L, 1, "invalid next option for option 'X'"); + } + if (align <= 1 || opt == Kchar) /* need no alignment? */ + *ntoalign = 0; + else { + if (align > h->maxalign) /* enforce maximum alignment */ + align = h->maxalign; + if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ + luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); + *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); + } + return opt; +} + + +/* +** Pack integer 'n' with 'size' bytes and 'islittle' endianness. +** The final 'if' handles the case when 'size' is larger than +** the size of a Lua integer, correcting the extra sign-extension +** bytes if necessary (by default they would be zeros). +*/ +static void packint (luaL_Buffer *b, lua_Unsigned n, + int islittle, int size, int neg) { + char *buff = luaL_prepbuffsize(b, size); + int i; + buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ + for (i = 1; i < size; i++) { + n >>= NB; + buff[islittle ? i : size - 1 - i] = (char)(n & MC); + } + if (neg && size > SZINT) { /* negative number need sign extension? */ + for (i = SZINT; i < size; i++) /* correct extra bytes */ + buff[islittle ? i : size - 1 - i] = (char)MC; + } + luaL_addsize(b, size); /* add result to buffer */ +} + + +/* +** Copy 'size' bytes from 'src' to 'dest', correcting endianness if +** given 'islittle' is different from native endianness. +*/ +static void copywithendian (volatile char *dest, volatile const char *src, + int size, int islittle) { + if (islittle == nativeendian.little) { + while (size-- != 0) + *(dest++) = *(src++); + } + else { + dest += size - 1; + while (size-- != 0) + *(dest--) = *(src++); + } +} + + +static int str_pack (lua_State *L) { + luaL_Buffer b; + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + int arg = 1; /* current argument to pack */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ + luaL_buffinit(L, &b); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + totalsize += ntoalign + size; + while (ntoalign-- > 0) + luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ + arg++; + switch (opt) { + case Kint: { /* signed integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) { /* need overflow check? */ + lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); + luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); + } + packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); + break; + } + case Kuint: { /* unsigned integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) /* need overflow check? */ + luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), + arg, "unsigned overflow"); + packint(&b, (lua_Unsigned)n, h.islittle, size, 0); + break; + } + case Kfloat: { /* floating-point options */ + volatile Ftypes u; + char *buff = luaL_prepbuffsize(&b, size); + lua_Number n = luaL_checknumber(L, arg); /* get argument */ + if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ + else if (size == sizeof(u.d)) u.d = (double)n; + else u.n = n; + /* move 'u' to final result, correcting endianness if needed */ + copywithendian(buff, u.buff, size, h.islittle); + luaL_addsize(&b, size); + break; + } + case Kchar: { /* fixed-size string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, len <= (size_t)size, arg, + "string longer than given size"); + luaL_addlstring(&b, s, len); /* add string */ + while (len++ < (size_t)size) /* pad extra space */ + luaL_addchar(&b, LUAL_PACKPADBYTE); + break; + } + case Kstring: { /* strings with length count */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, size >= (int)sizeof(size_t) || + len < ((size_t)1 << (size * NB)), + arg, "string length does not fit in given size"); + packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ + luaL_addlstring(&b, s, len); + totalsize += len; + break; + } + case Kzstr: { /* zero-terminated string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); + luaL_addlstring(&b, s, len); + luaL_addchar(&b, '\0'); /* add zero at the end */ + totalsize += len + 1; + break; + } + case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ + case Kpaddalign: case Knop: + arg--; /* undo increment */ + break; + } + } + luaL_pushresult(&b); + return 1; +} + + +static int str_packsize (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, + "variable-length format"); + size += ntoalign; /* total space used by option */ + luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, + "format result too large"); + totalsize += size; + } + lua_pushinteger(L, (lua_Integer)totalsize); + return 1; +} + + +/* +** Unpack an integer with 'size' bytes and 'islittle' endianness. +** If size is smaller than the size of a Lua integer and integer +** is signed, must do sign extension (propagating the sign to the +** higher bits); if size is larger than the size of a Lua integer, +** it must check the unread bytes to see whether they do not cause an +** overflow. +*/ +static lua_Integer unpackint (lua_State *L, const char *str, + int islittle, int size, int issigned) { + lua_Unsigned res = 0; + int i; + int limit = (size <= SZINT) ? size : SZINT; + for (i = limit - 1; i >= 0; i--) { + res <<= NB; + res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + } + if (size < SZINT) { /* real size smaller than lua_Integer? */ + if (issigned) { /* needs sign extension? */ + lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); + res = ((res ^ mask) - mask); /* do sign extension */ + } + } + else if (size > SZINT) { /* must check unread bytes */ + int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; + for (i = limit; i < size; i++) { + if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) + luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); + } + } + return (lua_Integer)res; +} + + +static int str_unpack (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); + size_t ld; + const char *data = luaL_checklstring(L, 2, &ld); + size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1; + int n = 0; /* number of results */ + luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); + luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, + "data string too short"); + pos += ntoalign; /* skip alignment */ + /* stack space for item + next position */ + luaL_checkstack(L, 2, "too many results"); + n++; + switch (opt) { + case Kint: + case Kuint: { + lua_Integer res = unpackint(L, data + pos, h.islittle, size, + (opt == Kint)); + lua_pushinteger(L, res); + break; + } + case Kfloat: { + volatile Ftypes u; + lua_Number num; + copywithendian(u.buff, data + pos, size, h.islittle); + if (size == sizeof(u.f)) num = (lua_Number)u.f; + else if (size == sizeof(u.d)) num = (lua_Number)u.d; + else num = u.n; + lua_pushnumber(L, num); + break; + } + case Kchar: { + lua_pushlstring(L, data + pos, size); + break; + } + case Kstring: { + size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); + luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); + lua_pushlstring(L, data + pos + size, len); + pos += len; /* skip string */ + break; + } + case Kzstr: { + size_t len = (int)strlen(data + pos); + luaL_argcheck(L, pos + len < ld, 2, + "unfinished string for format 'z'"); + lua_pushlstring(L, data + pos, len); + pos += len + 1; /* skip string plus final '\0' */ + break; + } + case Kpaddalign: case Kpadding: case Knop: + n--; /* undo increment */ + break; + } + pos += size; + } + lua_pushinteger(L, pos + 1); /* next position */ + return n + 1; +} + +/* }====================================================== */ + + +static const luaL_Reg strlib[] = { + {"byte", str_byte}, + {"char", str_char}, + {"dump", str_dump}, + {"find", str_find}, + {"format", str_format}, + {"gmatch", gmatch}, + {"gsub", str_gsub}, + {"len", str_len}, + {"lower", str_lower}, + {"match", str_match}, + {"rep", str_rep}, + {"reverse", str_reverse}, + {"sub", str_sub}, + {"upper", str_upper}, + {"pack", str_pack}, + {"packsize", str_packsize}, + {"unpack", str_unpack}, + {NULL, NULL} +}; + + +static void createmetatable (lua_State *L) { + /* table to be metatable for strings */ + luaL_newlibtable(L, stringmetamethods); + luaL_setfuncs(L, stringmetamethods, 0); + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); /* copy table */ + lua_setmetatable(L, -2); /* set table as metatable for strings */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* get string library */ + lua_setfield(L, -2, "__index"); /* metatable.__index = string */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** Open string library +*/ +LUAMOD_API int luaopen_string (lua_State *L) { + luaL_newlib(L, strlib); + createmetatable(L); + return 1; +} + diff --git a/Lua/ltable.c b/Lua/ltable.c new file mode 100644 index 00000000..5a0d066f --- /dev/null +++ b/Lua/ltable.c @@ -0,0 +1,924 @@ +/* +** $Id: ltable.c $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#define ltable_c +#define LUA_CORE + +#include "lprefix.h" + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest 'n' such that +** more than half the slots between 1 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the 'original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include +#include + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lvm.h" + + +/* +** MAXABITS is the largest integer such that MAXASIZE fits in an +** unsigned int. +*/ +#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) + + +/* +** MAXASIZE is the maximum size of the array part. It is the minimum +** between 2^MAXABITS and the maximum size that, measured in bytes, +** fits in a 'size_t'. +*/ +#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue) + +/* +** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a +** signed int. +*/ +#define MAXHBITS (MAXABITS - 1) + + +/* +** MAXHSIZE is the maximum size of the hash part. It is the minimum +** between 2^MAXHBITS and the maximum size such that, measured in bytes, +** it fits in a 'size_t'. +*/ +#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node) + + +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +#define hashstr(t,str) hashpow2(t, (str)->hash) +#define hashboolean(t,p) hashpow2(t, p) +#define hashint(t,i) hashpow2(t, i) + + +/* +** for some types, it is better to avoid modulus by power of 2, as +** they tend to have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashpointer(t,p) hashmod(t, point2uint(p)) + + +#define dummynode (&dummynode_) + +static const Node dummynode_ = { + {{NULL}, LUA_VEMPTY, /* value's value and type */ + LUA_VNIL, 0, {NULL}} /* key type, next, and key value */ +}; + + +static const TValue absentkey = {ABSTKEYCONSTANT}; + + + +/* +** Hash for floating-point numbers. +** The main computation should be just +** n = frexp(n, &i); return (n * INT_MAX) + i +** but there are some numerical subtleties. +** In a two-complement representation, INT_MAX does not has an exact +** representation as a float, but INT_MIN does; because the absolute +** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the +** absolute value of the product 'frexp * -INT_MIN' is smaller or equal +** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when +** adding 'i'; the use of '~u' (instead of '-u') avoids problems with +** INT_MIN. +*/ +#if !defined(l_hashfloat) +static int l_hashfloat (lua_Number n) { + int i; + lua_Integer ni; + n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); + if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ + lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); + return 0; + } + else { /* normal case */ + unsigned int u = cast_uint(i) + cast_uint(ni); + return cast_int(u <= cast_uint(INT_MAX) ? u : ~u); + } +} +#endif + + +/* +** returns the 'main' position of an element in a table (that is, +** the index of its hash value). The key comes broken (tag in 'ktt' +** and value in 'vkl') so that we can call it on keys inserted into +** nodes. +*/ +static Node *mainposition (const Table *t, int ktt, const Value *kvl) { + switch (withvariant(ktt)) { + case LUA_VNUMINT: + return hashint(t, ivalueraw(*kvl)); + case LUA_VNUMFLT: + return hashmod(t, l_hashfloat(fltvalueraw(*kvl))); + case LUA_VSHRSTR: + return hashstr(t, tsvalueraw(*kvl)); + case LUA_VLNGSTR: + return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl))); + case LUA_VFALSE: + return hashboolean(t, 0); + case LUA_VTRUE: + return hashboolean(t, 1); + case LUA_VLIGHTUSERDATA: + return hashpointer(t, pvalueraw(*kvl)); + case LUA_VLCF: + return hashpointer(t, fvalueraw(*kvl)); + default: + return hashpointer(t, gcvalueraw(*kvl)); + } +} + + +/* +** Returns the main position of an element given as a 'TValue' +*/ +static Node *mainpositionTV (const Table *t, const TValue *key) { + return mainposition(t, rawtt(key), valraw(key)); +} + + +/* +** Check whether key 'k1' is equal to the key in node 'n2'. +** This equality is raw, so there are no metamethods. Floats +** with integer values have been normalized, so integers cannot +** be equal to floats. It is assumed that 'eqshrstr' is simply +** pointer equality, so that short strings are handled in the +** default case. +*/ +static int equalkey (const TValue *k1, const Node *n2) { + if (rawtt(k1) != keytt(n2)) /* not the same variants? */ + return 0; /* cannot be same key */ + switch (ttypetag(k1)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(k1) == keyival(n2)); + case LUA_VNUMFLT: + return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); + case LUA_VLIGHTUSERDATA: + return pvalue(k1) == pvalueraw(keyval(n2)); + case LUA_VLCF: + return fvalue(k1) == fvalueraw(keyval(n2)); + case LUA_VLNGSTR: + return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); + default: + return gcvalue(k1) == gcvalueraw(keyval(n2)); + } +} + + +/* +** True if value of 'alimit' is equal to the real size of the array +** part of table 't'. (Otherwise, the array part must be larger than +** 'alimit'.) +*/ +#define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit)) + + +/* +** Returns the real size of the 'array' array +*/ +LUAI_FUNC unsigned int luaH_realasize (const Table *t) { + if (limitequalsasize(t)) + return t->alimit; /* this is the size */ + else { + unsigned int size = t->alimit; + /* compute the smallest power of 2 not smaller than 'n' */ + size |= (size >> 1); + size |= (size >> 2); + size |= (size >> 4); + size |= (size >> 8); + size |= (size >> 16); +#if (UINT_MAX >> 30) > 3 + size |= (size >> 32); /* unsigned int has more than 32 bits */ +#endif + size++; + lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); + return size; + } +} + + +/* +** Check whether real size of the array is a power of 2. +** (If it is not, 'alimit' cannot be changed to any other value +** without changing the real size.) +*/ +static int ispow2realasize (const Table *t) { + return (!isrealasize(t) || ispow2(t->alimit)); +} + + +static unsigned int setlimittosize (Table *t) { + t->alimit = luaH_realasize(t); + setrealasize(t); + return t->alimit; +} + + +#define limitasasize(t) check_exp(isrealasize(t), t->alimit) + + + +/* +** "Generic" get version. (Not that generic: not valid for integers, +** which may be in array part, nor for floats with integral values.) +*/ +static const TValue *getgeneric (Table *t, const TValue *key) { + Node *n = mainpositionTV(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (equalkey(key, n)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; + } + } +} + + +/* +** returns the index for 'k' if 'k' is an appropriate key to live in +** the array part of a table, 0 otherwise. +*/ +static unsigned int arrayindex (lua_Integer k) { + if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */ + return cast_uint(k); /* 'key' is an appropriate array index */ + else + return 0; +} + + +/* +** returns the index of a 'key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signaled by 0. +*/ +static unsigned int findindex (lua_State *L, Table *t, TValue *key, + unsigned int asize) { + unsigned int i; + if (ttisnil(key)) return 0; /* first iteration */ + i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0; + if (i - 1u < asize) /* is 'key' inside array part? */ + return i; /* yes; that's the index */ + else { + const TValue *n = getgeneric(t, key); + if (unlikely(isabstkey(n))) + luaG_runerror(L, "invalid key to 'next'"); /* key not found */ + i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return (i + 1) + asize; + } +} + + +int luaH_next (lua_State *L, Table *t, StkId key) { + unsigned int asize = luaH_realasize(t); + unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */ + for (; i < asize; i++) { /* try first array part */ + if (!isempty(&t->array[i])) { /* a non-empty entry? */ + setivalue(s2v(key), i + 1); + setobj2s(L, key + 1, &t->array[i]); + return 1; + } + } + for (i -= asize; cast_int(i) < sizenode(t); i++) { /* hash part */ + if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */ + Node *n = gnode(t, i); + getnodekey(L, s2v(key), n); + setobj2s(L, key + 1, gval(n)); + return 1; + } + } + return 0; /* no more elements */ +} + + +static void freehash (lua_State *L, Table *t) { + if (!isdummy(t)) + luaM_freearray(L, t->node, cast_sizet(sizenode(t))); +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + +/* +** Compute the optimal size for the array part of table 't'. 'nums' is a +** "count array" where 'nums[i]' is the number of integers in the table +** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of +** integer keys in the table and leaves with the number of keys that +** will go to the array part; return the optimal size. (The condition +** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.) +*/ +static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { + int i; + unsigned int twotoi; /* 2^i (candidate for optimal size) */ + unsigned int a = 0; /* number of elements smaller than 2^i */ + unsigned int na = 0; /* number of elements to go to array part */ + unsigned int optimal = 0; /* optimal size for array part */ + /* loop while keys can fill more than half of total size */ + for (i = 0, twotoi = 1; + twotoi > 0 && *pna > twotoi / 2; + i++, twotoi *= 2) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + optimal = twotoi; /* optimal size (till now) */ + na = a; /* all elements up to 'optimal' will go to array part */ + } + } + lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); + *pna = na; + return optimal; +} + + +static int countint (lua_Integer key, unsigned int *nums) { + unsigned int k = arrayindex(key); + if (k != 0) { /* is 'key' an appropriate array index? */ + nums[luaO_ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +/* +** Count keys in array part of table 't': Fill 'nums[i]' with +** number of keys that will go into corresponding slice and return +** total number of non-nil keys. +*/ +static unsigned int numusearray (const Table *t, unsigned int *nums) { + int lg; + unsigned int ttlg; /* 2^lg */ + unsigned int ause = 0; /* summation of 'nums' */ + unsigned int i = 1; /* count to traverse all array keys */ + unsigned int asize = limitasasize(t); /* real array size */ + /* traverse each slice */ + for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { + unsigned int lc = 0; /* counter */ + unsigned int lim = ttlg; + if (lim > asize) { + lim = asize; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg - 1), 2^lg] */ + for (; i <= lim; i++) { + if (!isempty(&t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { + int totaluse = 0; /* total number of elements */ + int ause = 0; /* elements added to 'nums' (can go to array part) */ + int i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!isempty(gval(n))) { + if (keyisinteger(n)) + ause += countint(keyival(n), nums); + totaluse++; + } + } + *pna += ause; + return totaluse; +} + + +/* +** Creates an array for the hash part of a table with the given +** size, or reuses the dummy node if size is zero. +** The computation for size overflow is in two steps: the first +** comparison ensures that the shift in the second one does not +** overflow. +*/ +static void setnodevector (lua_State *L, Table *t, unsigned int size) { + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common 'dummynode' */ + t->lsizenode = 0; + t->lastfree = NULL; /* signal that it is using dummy node */ + } + else { + int i; + int lsize = luaO_ceillog2(size); + if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) + luaG_runerror(L, "table overflow"); + size = twoto(lsize); + t->node = luaM_newvector(L, size, Node); + for (i = 0; i < (int)size; i++) { + Node *n = gnode(t, i); + gnext(n) = 0; + setnilkey(n); + setempty(gval(n)); + } + t->lsizenode = cast_byte(lsize); + t->lastfree = gnode(t, size); /* all positions are free */ + } +} + + +/* +** (Re)insert all elements from the hash part of 'ot' into table 't'. +*/ +static void reinsert (lua_State *L, Table *ot, Table *t) { + int j; + int size = sizenode(ot); + for (j = 0; j < size; j++) { + Node *old = gnode(ot, j); + if (!isempty(gval(old))) { + /* doesn't need barrier/invalidate cache, as entry was + already present in the table */ + TValue k; + getnodekey(L, &k, old); + setobjt2t(L, luaH_set(L, t, &k), gval(old)); + } + } +} + + +/* +** Exchange the hash part of 't1' and 't2'. +*/ +static void exchangehashpart (Table *t1, Table *t2) { + lu_byte lsizenode = t1->lsizenode; + Node *node = t1->node; + Node *lastfree = t1->lastfree; + t1->lsizenode = t2->lsizenode; + t1->node = t2->node; + t1->lastfree = t2->lastfree; + t2->lsizenode = lsizenode; + t2->node = node; + t2->lastfree = lastfree; +} + + +/* +** Resize table 't' for the new given sizes. Both allocations (for +** the hash part and for the array part) can fail, which creates some +** subtleties. If the first allocation, for the hash part, fails, an +** error is raised and that is it. Otherwise, it copies the elements from +** the shrinking part of the array (if it is shrinking) into the new +** hash. Then it reallocates the array part. If that fails, the table +** is in its original state; the function frees the new hash part and then +** raises the allocation error. Otherwise, it sets the new hash part +** into the table, initializes the new part of the array (if any) with +** nils and reinserts the elements of the old hash back into the new +** parts of the table. +*/ +void luaH_resize (lua_State *L, Table *t, unsigned int newasize, + unsigned int nhsize) { + unsigned int i; + Table newt; /* to keep the new hash part */ + unsigned int oldasize = setlimittosize(t); + TValue *newarray; + /* create new hash part with appropriate size into 'newt' */ + setnodevector(L, &newt, nhsize); + if (newasize < oldasize) { /* will array shrink? */ + t->alimit = newasize; /* pretend array has new size... */ + exchangehashpart(t, &newt); /* and new hash */ + /* re-insert into the new hash the elements from vanishing slice */ + for (i = newasize; i < oldasize; i++) { + if (!isempty(&t->array[i])) + luaH_setint(L, t, i + 1, &t->array[i]); + } + t->alimit = oldasize; /* restore current size... */ + exchangehashpart(t, &newt); /* and hash (in case of errors) */ + } + /* allocate new array */ + newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); + if (unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ + freehash(L, &newt); /* release new hash part */ + luaM_error(L); /* raise error (with array unchanged) */ + } + /* allocation ok; initialize new part of the array */ + exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */ + t->array = newarray; /* set new array part */ + t->alimit = newasize; + for (i = oldasize; i < newasize; i++) /* clear new slice of the array */ + setempty(&t->array[i]); + /* re-insert elements from old hash part into new parts */ + reinsert(L, &newt, t); /* 'newt' now has the old hash */ + freehash(L, &newt); /* free old hash part */ +} + + +void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) { + int nsize = allocsizenode(t); + luaH_resize(L, t, nasize, nsize); +} + +/* +** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i +*/ +static void rehash (lua_State *L, Table *t, const TValue *ek) { + unsigned int asize; /* optimal size for array part */ + unsigned int na; /* number of keys in the array part */ + unsigned int nums[MAXABITS + 1]; + int i; + int totaluse; + for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */ + setlimittosize(t); + na = numusearray(t, nums); /* count keys in array part */ + totaluse = na; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &na); /* count keys in hash part */ + /* count extra key */ + if (ttisinteger(ek)) + na += countint(ivalue(ek), nums); + totaluse++; + /* compute new size for array part */ + asize = computesizes(nums, &na); + /* resize the table to new computed sizes */ + luaH_resize(L, t, asize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + + +Table *luaH_new (lua_State *L) { + GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); + Table *t = gco2t(o); + t->metatable = NULL; + t->flags = cast_byte(maskflags); /* table has no metamethod fields */ + t->array = NULL; + t->alimit = 0; + setnodevector(L, t, 0); + return t; +} + + +void luaH_free (lua_State *L, Table *t) { + freehash(L, t); + luaM_freearray(L, t->array, luaH_realasize(t)); + luaM_free(L, t); +} + + +static Node *getfreepos (Table *t) { + if (!isdummy(t)) { + while (t->lastfree > t->node) { + t->lastfree--; + if (keyisnil(t->lastfree)) + return t->lastfree; + } + } + return NULL; /* could not find a free place */ +} + + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { + Node *mp; + TValue aux; + if (unlikely(ttisnil(key))) + luaG_runerror(L, "table index is nil"); + else if (ttisfloat(key)) { + lua_Number f = fltvalue(key); + lua_Integer k; + if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */ + setivalue(&aux, k); + key = &aux; /* insert it as an integer */ + } + else if (unlikely(luai_numisnan(f))) + luaG_runerror(L, "table index is NaN"); + } + mp = mainpositionTV(t, key); + if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */ + Node *othern; + Node *f = getfreepos(t); /* get a free place */ + if (f == NULL) { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ + /* whatever called 'newkey' takes care of TM cache */ + return luaH_set(L, t, key); /* insert key into grown table */ + } + lua_assert(!isdummy(t)); + othern = mainposition(t, keytt(mp), &keyval(mp)); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (othern + gnext(othern) != mp) /* find previous */ + othern += gnext(othern); + gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */ + *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + if (gnext(mp) != 0) { + gnext(f) += cast_int(mp - f); /* correct 'next' */ + gnext(mp) = 0; /* now 'mp' is free */ + } + setempty(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + if (gnext(mp) != 0) + gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */ + else lua_assert(gnext(f) == 0); + gnext(mp) = cast_int(f - mp); + mp = f; + } + } + setnodekey(L, mp, key); + luaC_barrierback(L, obj2gco(t), key); + lua_assert(isempty(gval(mp))); + return gval(mp); +} + + +/* +** Search function for integers. If integer is inside 'alimit', get it +** directly from the array part. Otherwise, if 'alimit' is not equal to +** the real size of the array, key still can be in the array part. In +** this case, try to avoid a call to 'luaH_realasize' when key is just +** one more than the limit (so that it can be incremented without +** changing the real size of the array). +*/ +const TValue *luaH_getint (Table *t, lua_Integer key) { + if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */ + return &t->array[key - 1]; + else if (!limitequalsasize(t) && /* key still may be in the array part? */ + (l_castS2U(key) == t->alimit + 1 || + l_castS2U(key) - 1u < luaH_realasize(t))) { + t->alimit = cast_uint(key); /* probably '#t' is here now */ + return &t->array[key - 1]; + } + else { + Node *n = hashint(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisinteger(n) && keyival(n) == key) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) break; + n += nx; + } + } + return &absentkey; + } +} + + +/* +** search function for short strings +*/ +const TValue *luaH_getshortstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + lua_assert(key->tt == LUA_VSHRSTR); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisshrstr(n) && eqshrstr(keystrval(n), key)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; + } + } +} + + +const TValue *luaH_getstr (Table *t, TString *key) { + if (key->tt == LUA_VSHRSTR) + return luaH_getshortstr(t, key); + else { /* for long strings, use generic case */ + TValue ko; + setsvalue(cast(lua_State *, NULL), &ko, key); + return getgeneric(t, &ko); + } +} + + +/* +** main search function +*/ +const TValue *luaH_get (Table *t, const TValue *key) { + switch (ttypetag(key)) { + case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key)); + case LUA_VNUMINT: return luaH_getint(t, ivalue(key)); + case LUA_VNIL: return &absentkey; + case LUA_VNUMFLT: { + lua_Integer k; + if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ + return luaH_getint(t, k); /* use specialized version */ + /* else... */ + } /* FALLTHROUGH */ + default: + return getgeneric(t, key); + } +} + + +/* +** beware: when using this function you probably need to check a GC +** barrier and invalidate the TM cache. +*/ +TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { + const TValue *p = luaH_get(t, key); + if (!isabstkey(p)) + return cast(TValue *, p); + else return luaH_newkey(L, t, key); +} + + +void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { + const TValue *p = luaH_getint(t, key); + TValue *cell; + if (!isabstkey(p)) + cell = cast(TValue *, p); + else { + TValue k; + setivalue(&k, key); + cell = luaH_newkey(L, t, &k); + } + setobj2t(L, cell, value); +} + + +/* +** Try to find a boundary in the hash part of table 't'. From the +** caller, we know that 'j' is zero or present and that 'j + 1' is +** present. We want to find a larger key that is absent from the +** table, so that we can do a binary search between the two keys to +** find a boundary. We keep doubling 'j' until we get an absent index. +** If the doubling would overflow, we try LUA_MAXINTEGER. If it is +** absent, we are ready for the binary search. ('j', being max integer, +** is larger or equal to 'i', but it cannot be equal because it is +** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a +** boundary. ('j + 1' cannot be a present integer key because it is +** not a valid integer in Lua.) +*/ +static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { + lua_Unsigned i; + if (j == 0) j++; /* the caller ensures 'j + 1' is present */ + do { + i = j; /* 'i' is a present index */ + if (j <= l_castS2U(LUA_MAXINTEGER) / 2) + j *= 2; + else { + j = LUA_MAXINTEGER; + if (isempty(luaH_getint(t, j))) /* t[j] not present? */ + break; /* 'j' now is an absent index */ + else /* weird case */ + return j; /* well, max integer is a boundary... */ + } + } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */ + /* i < j && t[i] present && t[j] absent */ + while (j - i > 1u) { /* do a binary search between them */ + lua_Unsigned m = (i + j) / 2; + if (isempty(luaH_getint(t, m))) j = m; + else i = m; + } + return i; +} + + +static unsigned int binsearch (const TValue *array, unsigned int i, + unsigned int j) { + while (j - i > 1u) { /* binary search */ + unsigned int m = (i + j) / 2; + if (isempty(&array[m - 1])) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table 't'. (A 'boundary' is an integer index +** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent +** and 'maxinteger' if t[maxinteger] is present.) +** (In the next explanation, we use Lua indices, that is, with base 1. +** The code itself uses base 0 when indexing the array part of the table.) +** The code starts with 'limit = t->alimit', a position in the array +** part that may be a boundary. +** +** (1) If 't[limit]' is empty, there must be a boundary before it. +** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1' +** is present. If so, it is a boundary. Otherwise, do a binary search +** between 0 and limit to find a boundary. In both cases, try to +** use this boundary as the new 'alimit', as a hint for the next call. +** +** (2) If 't[limit]' is not empty and the array has more elements +** after 'limit', try to find a boundary there. Again, try first +** the special case (which should be quite frequent) where 'limit+1' +** is empty, so that 'limit' is a boundary. Otherwise, check the +** last element of the array part. If it is empty, there must be a +** boundary between the old limit (present) and the last element +** (absent), which is found with a binary search. (This boundary always +** can be a new limit.) +** +** (3) The last case is when there are no elements in the array part +** (limit == 0) or its last element (the new limit) is present. +** In this case, must check the hash part. If there is no hash part +** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call +** 'hash_search' to find a boundary in the hash part of the table. +** (In those cases, the boundary is not inside the array part, and +** therefore cannot be used as a new limit.) +*/ +lua_Unsigned luaH_getn (Table *t) { + unsigned int limit = t->alimit; + if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */ + /* there must be a boundary before 'limit' */ + if (limit >= 2 && !isempty(&t->array[limit - 2])) { + /* 'limit - 1' is a boundary; can it be a new limit? */ + if (ispow2realasize(t) && !ispow2(limit - 1)) { + t->alimit = limit - 1; + setnorealasize(t); /* now 'alimit' is not the real size */ + } + return limit - 1; + } + else { /* must search for a boundary in [0, limit] */ + unsigned int boundary = binsearch(t->array, 0, limit); + /* can this boundary represent the real size of the array? */ + if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) { + t->alimit = boundary; /* use it as the new limit */ + setnorealasize(t); + } + return boundary; + } + } + /* 'limit' is zero or present in table */ + if (!limitequalsasize(t)) { /* (2)? */ + /* 'limit' > 0 and array has more elements after 'limit' */ + if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */ + return limit; /* this is the boundary */ + /* else, try last element in the array */ + limit = luaH_realasize(t); + if (isempty(&t->array[limit - 1])) { /* empty? */ + /* there must be a boundary in the array after old limit, + and it must be a valid new limit */ + unsigned int boundary = binsearch(t->array, t->alimit, limit); + t->alimit = boundary; + return boundary; + } + /* else, new limit is present in the table; check the hash part */ + } + /* (3) 'limit' is the last element and either is zero or present in table */ + lua_assert(limit == luaH_realasize(t) && + (limit == 0 || !isempty(&t->array[limit - 1]))); + if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1)))) + return limit; /* 'limit + 1' is absent */ + else /* 'limit + 1' is also present */ + return hash_search(t, limit); +} + + + +#if defined(LUA_DEBUG) + +/* export these functions for the test library */ + +Node *luaH_mainposition (const Table *t, const TValue *key) { + return mainpositionTV(t, key); +} + +int luaH_isdummy (const Table *t) { return isdummy(t); } + +#endif diff --git a/Lua/ltable.h b/Lua/ltable.h new file mode 100644 index 00000000..c0060f4b --- /dev/null +++ b/Lua/ltable.h @@ -0,0 +1,62 @@ +/* +** $Id: ltable.h $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +#include "lobject.h" + + +#define gnode(t,i) (&(t)->node[i]) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->u.next) + + +/* +** Clear all bits of fast-access metamethods, which means that the table +** may have any of these metamethods. (First access that fails after the +** clearing will set the bit again.) +*/ +#define invalidateTMcache(t) ((t)->flags &= ~maskflags) + + +/* true when 't' is using 'dummynode' as its hash part */ +#define isdummy(t) ((t)->lastfree == NULL) + + +/* allocated size for hash nodes */ +#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) + + +/* returns the Node, given the value of a table entry */ +#define nodefromval(v) cast(Node *, (v)) + + +LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); +LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, + TValue *value); +LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); +LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); +LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); +LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC Table *luaH_new (lua_State *L); +LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, + unsigned int nhsize); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC lua_Unsigned luaH_getn (Table *t); +LUAI_FUNC unsigned int luaH_realasize (const Table *t); + + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +LUAI_FUNC int luaH_isdummy (const Table *t); +#endif + + +#endif diff --git a/Lua/ltablib.c b/Lua/ltablib.c new file mode 100644 index 00000000..d344a47e --- /dev/null +++ b/Lua/ltablib.c @@ -0,0 +1,428 @@ +/* +** $Id: ltablib.c $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + +#define ltablib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* +** Operations that an object must define to mimic a table +** (some functions only need some of them) +*/ +#define TAB_R 1 /* read */ +#define TAB_W 2 /* write */ +#define TAB_L 4 /* length */ +#define TAB_RW (TAB_R | TAB_W) /* read/write */ + + +#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) + + +static int checkfield (lua_State *L, const char *key, int n) { + lua_pushstring(L, key); + return (lua_rawget(L, -n) != LUA_TNIL); +} + + +/* +** Check that 'arg' either is a table or can behave like one (that is, +** has a metatable with the required metamethods) +*/ +static void checktab (lua_State *L, int arg, int what) { + if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ + int n = 1; /* number of elements to pop */ + if (lua_getmetatable(L, arg) && /* must have metatable */ + (!(what & TAB_R) || checkfield(L, "__index", ++n)) && + (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && + (!(what & TAB_L) || checkfield(L, "__len", ++n))) { + lua_pop(L, n); /* pop metatable and tested metamethods */ + } + else + luaL_checktype(L, arg, LUA_TTABLE); /* force an error */ + } +} + + +static int tinsert (lua_State *L) { + lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */ + lua_Integer pos; /* where to insert new element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + lua_Integer i; + pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ + /* check whether 'pos' is in [1, e] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2, + "position out of bounds"); + for (i = e; i > pos; i--) { /* move up elements */ + lua_geti(L, 1, i - 1); + lua_seti(L, 1, i); /* t[i] = t[i - 1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to 'insert'"); + } + } + lua_seti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + lua_Integer size = aux_getn(L, 1, TAB_RW); + lua_Integer pos = luaL_optinteger(L, 2, size); + if (pos != size) /* validate 'pos' if given */ + /* check whether 'pos' is in [1, size + 1] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1, + "position out of bounds"); + lua_geti(L, 1, pos); /* result = t[pos] */ + for ( ; pos < size; pos++) { + lua_geti(L, 1, pos + 1); + lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ + } + lua_pushnil(L); + lua_seti(L, 1, pos); /* remove entry t[pos] */ + return 1; +} + + +/* +** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever +** possible, copy in increasing order, which is better for rehashing. +** "possible" means destination after original range, or smaller +** than origin, or copying to another table. +*/ +static int tmove (lua_State *L) { + lua_Integer f = luaL_checkinteger(L, 2); + lua_Integer e = luaL_checkinteger(L, 3); + lua_Integer t = luaL_checkinteger(L, 4); + int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ + checktab(L, 1, TAB_R); + checktab(L, tt, TAB_W); + if (e >= f) { /* otherwise, nothing to move */ + lua_Integer n, i; + luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, + "too many elements to move"); + n = e - f + 1; /* number of elements to move */ + luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, + "destination wrap around"); + if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { + for (i = 0; i < n; i++) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + else { + for (i = n - 1; i >= 0; i--) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + } + lua_pushvalue(L, tt); /* return destination table */ + return 1; +} + + +static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { + lua_geti(L, 1, i); + if (!lua_isstring(L, -1)) + luaL_error(L, "invalid value (%s) at index %d in table for 'concat'", + luaL_typename(L, -1), i); + luaL_addvalue(b); +} + + +static int tconcat (lua_State *L) { + luaL_Buffer b; + lua_Integer last = aux_getn(L, 1, TAB_R); + size_t lsep; + const char *sep = luaL_optlstring(L, 2, "", &lsep); + lua_Integer i = luaL_optinteger(L, 3, 1); + last = luaL_optinteger(L, 4, last); + luaL_buffinit(L, &b); + for (; i < last; i++) { + addfield(L, &b, i); + luaL_addlstring(&b, sep, lsep); + } + if (i == last) /* add last value (if interval was not empty) */ + addfield(L, &b, i); + luaL_pushresult(&b); + return 1; +} + + +/* +** {====================================================== +** Pack/unpack +** ======================================================= +*/ + +static int tpack (lua_State *L) { + int i; + int n = lua_gettop(L); /* number of elements to pack */ + lua_createtable(L, n, 1); /* create result table */ + lua_insert(L, 1); /* put it at index 1 */ + for (i = n; i >= 1; i--) /* assign elements */ + lua_seti(L, 1, i); + lua_pushinteger(L, n); + lua_setfield(L, 1, "n"); /* t.n = number of elements */ + return 1; /* return table */ +} + + +static int tunpack (lua_State *L) { + lua_Unsigned n; + lua_Integer i = luaL_optinteger(L, 2, 1); + lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); + if (i > e) return 0; /* empty range */ + n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ + if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) + return luaL_error(L, "too many results to unpack"); + for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ + lua_geti(L, 1, i); + } + lua_geti(L, 1, e); /* push last element */ + return (int)n; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Quicksort +** (based on 'Algorithms in MODULA-3', Robert Sedgewick; +** Addison-Wesley, 1993.) +** ======================================================= +*/ + + +/* type for array indices */ +typedef unsigned int IdxT; + + +/* +** Produce a "random" 'unsigned int' to randomize pivot choice. This +** macro is used only when 'sort' detects a big imbalance in the result +** of a partition. (If you don't want/need this "randomness", ~0 is a +** good choice.) +*/ +#if !defined(l_randomizePivot) /* { */ + +#include + +/* size of 'e' measured in number of 'unsigned int's */ +#define sof(e) (sizeof(e) / sizeof(unsigned int)) + +/* +** Use 'time' and 'clock' as sources of "randomness". Because we don't +** know the types 'clock_t' and 'time_t', we cannot cast them to +** anything without risking overflows. A safe way to use their values +** is to copy them to an array of a known type and use the array values. +*/ +static unsigned int l_randomizePivot (void) { + clock_t c = clock(); + time_t t = time(NULL); + unsigned int buff[sof(c) + sof(t)]; + unsigned int i, rnd = 0; + memcpy(buff, &c, sof(c) * sizeof(unsigned int)); + memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); + for (i = 0; i < sof(buff); i++) + rnd += buff[i]; + return rnd; +} + +#endif /* } */ + + +/* arrays larger than 'RANLIMIT' may use randomized pivots */ +#define RANLIMIT 100u + + +static void set2 (lua_State *L, IdxT i, IdxT j) { + lua_seti(L, 1, i); + lua_seti(L, 1, j); +} + + +/* +** Return true iff value at stack index 'a' is less than the value at +** index 'b' (according to the order of the sort). +*/ +static int sort_comp (lua_State *L, int a, int b) { + if (lua_isnil(L, 2)) /* no function? */ + return lua_compare(L, a, b, LUA_OPLT); /* a < b */ + else { /* function */ + int res; + lua_pushvalue(L, 2); /* push function */ + lua_pushvalue(L, a-1); /* -1 to compensate function */ + lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ + lua_call(L, 2, 1); /* call function */ + res = lua_toboolean(L, -1); /* get result */ + lua_pop(L, 1); /* pop result */ + return res; + } +} + + +/* +** Does the partition: Pivot P is at the top of the stack. +** precondition: a[lo] <= P == a[up-1] <= a[up], +** so it only needs to do the partition from lo + 1 to up - 2. +** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] +** returns 'i'. +*/ +static IdxT partition (lua_State *L, IdxT lo, IdxT up) { + IdxT i = lo; /* will be incremented before first use */ + IdxT j = up - 1; /* will be decremented before first use */ + /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ + for (;;) { + /* next loop: repeat ++i while a[i] < P */ + while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ + /* next loop: repeat --j while P < a[j] */ + while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j < i) /* j < i but a[j] > P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ + if (j < i) { /* no elements out of place? */ + /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ + lua_pop(L, 1); /* pop a[j] */ + /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ + set2(L, up - 1, i); + return i; + } + /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ + set2(L, i, j); + } +} + + +/* +** Choose an element in the middle (2nd-3th quarters) of [lo,up] +** "randomized" by 'rnd' +*/ +static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { + IdxT r4 = (up - lo) / 4; /* range/4 */ + IdxT p = rnd % (r4 * 2) + (lo + r4); + lua_assert(lo + r4 <= p && p <= up - r4); + return p; +} + + +/* +** Quicksort algorithm (recursive function) +*/ +static void auxsort (lua_State *L, IdxT lo, IdxT up, + unsigned int rnd) { + while (lo < up) { /* loop for tail recursion */ + IdxT p; /* Pivot index */ + IdxT n; /* to be used later */ + /* sort elements 'lo', 'p', and 'up' */ + lua_geti(L, 1, lo); + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ + set2(L, lo, up); /* swap a[lo] - a[up] */ + else + lua_pop(L, 2); /* remove both values */ + if (up - lo == 1) /* only 2 elements? */ + return; /* already sorted */ + if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */ + p = (lo + up)/2; /* middle element is a good pivot */ + else /* for larger intervals, it is worth a random pivot */ + p = choosePivot(lo, up, rnd); + lua_geti(L, 1, p); + lua_geti(L, 1, lo); + if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */ + set2(L, p, lo); /* swap a[p] - a[lo] */ + else { + lua_pop(L, 1); /* remove a[lo] */ + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */ + set2(L, p, up); /* swap a[up] - a[p] */ + else + lua_pop(L, 2); + } + if (up - lo == 2) /* only 3 elements? */ + return; /* already sorted */ + lua_geti(L, 1, p); /* get middle element (Pivot) */ + lua_pushvalue(L, -1); /* push Pivot */ + lua_geti(L, 1, up - 1); /* push a[up - 1] */ + set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ + p = partition(L, lo, up); + /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ + if (p - lo < up - p) { /* lower interval is smaller? */ + auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */ + n = p - lo; /* size of smaller interval */ + lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */ + } + else { + auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */ + n = up - p; /* size of smaller interval */ + up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ + } + if ((up - lo) / 128 > n) /* partition too imbalanced? */ + rnd = l_randomizePivot(); /* try a new randomization */ + } /* tail call auxsort(L, lo, up, rnd) */ +} + + +static int sort (lua_State *L) { + lua_Integer n = aux_getn(L, 1, TAB_RW); + if (n > 1) { /* non-trivial interval? */ + luaL_argcheck(L, n < INT_MAX, 1, "array too big"); + if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ + lua_settop(L, 2); /* make sure there are two arguments */ + auxsort(L, 1, (IdxT)n, 0); + } + return 0; +} + +/* }====================================================== */ + + +static const luaL_Reg tab_funcs[] = { + {"concat", tconcat}, + {"insert", tinsert}, + {"pack", tpack}, + {"unpack", tunpack}, + {"remove", tremove}, + {"move", tmove}, + {"sort", sort}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_table (lua_State *L) { + luaL_newlib(L, tab_funcs); + return 1; +} + diff --git a/Lua/ltm.c b/Lua/ltm.c new file mode 100644 index 00000000..4770f96b --- /dev/null +++ b/Lua/ltm.c @@ -0,0 +1,270 @@ +/* +** $Id: ltm.c $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#define ltm_c +#define LUA_CORE + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + +static const char udatatypename[] = "userdata"; + +LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = { + "no value", + "nil", "boolean", udatatypename, "number", + "string", "table", "function", udatatypename, "thread", + "upvalue", "proto" /* these last cases are used for tests only */ +}; + + +void luaT_init (lua_State *L) { + static const char *const luaT_eventname[] = { /* ORDER TM */ + "__index", "__newindex", + "__gc", "__mode", "__len", "__eq", + "__add", "__sub", "__mul", "__mod", "__pow", + "__div", "__idiv", + "__band", "__bor", "__bxor", "__shl", "__shr", + "__unm", "__bnot", "__lt", "__le", + "__concat", "__call", "__close" + }; + int i; + for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); + luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaH_getshortstr(events, ename); + lua_assert(event <= TM_EQ); + if (notm(tm)) { /* no tag method? */ + events->flags |= cast_byte(1u<metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttype(o)]; + } + return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue); +} + + +/* +** Return the name of the type of an object. For tables and userdata +** with metatable, use their '__name' metafield, if present. +*/ +const char *luaT_objtypename (lua_State *L, const TValue *o) { + Table *mt; + if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || + (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { + const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); + if (ttisstring(name)) /* is '__name' a string? */ + return getstr(tsvalue(name)); /* use it as type name */ + } + return ttypename(ttype(o)); /* else use standard type name */ +} + + +void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + StkId func = L->top; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + setobj2s(L, func + 3, p3); /* 3rd argument */ + L->top = func + 4; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 0); + else + luaD_callnoyield(L, func, 0); +} + + +void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, StkId res) { + ptrdiff_t result = savestack(L, res); + StkId func = L->top; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + L->top += 3; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 1); + else + luaD_callnoyield(L, func, 1); + res = restorestack(L, result); + setobjs2s(L, res, --L->top); /* move result to its place */ +} + + +static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (notm(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (notm(tm)) return 0; + luaT_callTMres(L, tm, p1, p2, res); + return 1; +} + + +void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + if (!callbinTM(L, p1, p2, res, event)) { + switch (event) { + case TM_BAND: case TM_BOR: case TM_BXOR: + case TM_SHL: case TM_SHR: case TM_BNOT: { + if (ttisnumber(p1) && ttisnumber(p2)) + luaG_tointerror(L, p1, p2); + else + luaG_opinterror(L, p1, p2, "perform bitwise operation on"); + } + /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ + default: + luaG_opinterror(L, p1, p2, "perform arithmetic on"); + } + } +} + + +void luaT_tryconcatTM (lua_State *L) { + StkId top = L->top; + if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT)) + luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); +} + + +void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2, + int flip, StkId res, TMS event) { + if (flip) + luaT_trybinTM(L, p2, p1, res, event); + else + luaT_trybinTM(L, p1, p2, res, event); +} + + +void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, + int flip, StkId res, TMS event) { + TValue aux; + setivalue(&aux, i2); + luaT_trybinassocTM(L, p1, &aux, flip, res, event); +} + + +/* +** Calls an order tag method. +** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old +** behavior: if there is no '__le', try '__lt', based on l <= r iff +** !(r < l) (assuming a total order). If the metamethod yields during +** this substitution, the continuation has to know about it (to negate +** the result of rtop, event)) /* try original event */ + return !l_isfalse(s2v(L->top)); +#if defined(LUA_COMPAT_LT_LE) + else if (event == TM_LE) { + /* try '!(p2 < p1)' for '(p1 <= p2)' */ + L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ + if (callbinTM(L, p2, p1, L->top, TM_LT)) { + L->ci->callstatus ^= CIST_LEQ; /* clear mark */ + return l_isfalse(s2v(L->top)); + } + /* else error will remove this 'ci'; no need to clear mark */ + } +#endif + luaG_ordererror(L, p1, p2); /* no metamethod found */ + return 0; /* to avoid warnings */ +} + + +int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, + int flip, int isfloat, TMS event) { + TValue aux; const TValue *p2; + if (isfloat) { + setfltvalue(&aux, cast_num(v2)); + } + else + setivalue(&aux, v2); + if (flip) { /* arguments were exchanged? */ + p2 = p1; p1 = &aux; /* correct them */ + } + else + p2 = &aux; + return luaT_callorderTM(L, p1, p2, event); +} + + +void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, + const Proto *p) { + int i; + int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */ + int nextra = actual - nfixparams; /* number of extra arguments */ + ci->u.l.nextraargs = nextra; + luaD_checkstack(L, p->maxstacksize + 1); + /* copy function to the top of the stack */ + setobjs2s(L, L->top++, ci->func); + /* move fixed parameters to the top of the stack */ + for (i = 1; i <= nfixparams; i++) { + setobjs2s(L, L->top++, ci->func + i); + setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */ + } + ci->func += actual + 1; + ci->top += actual + 1; + lua_assert(L->top <= ci->top && ci->top <= L->stack_last); +} + + +void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { + int i; + int nextra = ci->u.l.nextraargs; + if (wanted < 0) { + wanted = nextra; /* get all extra arguments available */ + checkstackGCp(L, nextra, where); /* ensure stack space */ + L->top = where + nextra; /* next instruction will need top */ + } + for (i = 0; i < wanted && i < nextra; i++) + setobjs2s(L, where + i, ci->func - nextra + i); + for (; i < wanted; i++) /* complete required results with nil */ + setnilvalue(s2v(where + i)); +} + diff --git a/Lua/ltm.h b/Lua/ltm.h new file mode 100644 index 00000000..73b833c6 --- /dev/null +++ b/Lua/ltm.h @@ -0,0 +1,103 @@ +/* +** $Id: ltm.h $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +#include "lobject.h" + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" and "ORDER OP" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_LEN, + TM_EQ, /* last tag method with fast access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_MOD, + TM_POW, + TM_DIV, + TM_IDIV, + TM_BAND, + TM_BOR, + TM_BXOR, + TM_SHL, + TM_SHR, + TM_UNM, + TM_BNOT, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_CLOSE, + TM_N /* number of elements in the enum */ +} TMS; + + +/* +** Mask with 1 in all fast-access methods. A 1 in any of these bits +** in the flag of a (meta)table means the metatable does not have the +** corresponding metamethod field. (Bit 7 of the flag is used for +** 'isrealasize'.) +*/ +#define maskflags (~(~0u << (TM_EQ + 1))) + + +/* +** Test whether there is no tagmethod. +** (Because tagmethods use raw accesses, the result may be an "empty" nil.) +*/ +#define notm(tm) ttisnil(tm) + + +#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ + ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + +#define fasttm(l,et,e) gfasttm(G(l), et, e) + +#define ttypename(x) luaT_typenames_[(x) + 1] + +LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];) + + +LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3); +LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f, + const TValue *p1, const TValue *p2, StkId p3); +LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event); +LUAI_FUNC void luaT_tryconcatTM (lua_State *L); +LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1, + const TValue *p2, int inv, StkId res, TMS event); +LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, + int inv, StkId res, TMS event); +LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, + const TValue *p2, TMS event); +LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, + int inv, int isfloat, TMS event); + +LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, + struct CallInfo *ci, const Proto *p); +LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, + StkId where, int wanted); + + +#endif diff --git a/Lua/lua.h b/Lua/lua.h new file mode 100644 index 00000000..08c6a64a --- /dev/null +++ b/Lua/lua.h @@ -0,0 +1,517 @@ +/* +** $Id: lua.h $ +** Lua - A Scripting Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION_MAJOR "5" +#define LUA_VERSION_MINOR "4" +#define LUA_VERSION_RELEASE "1" + +#define LUA_VERSION_NUM 504 +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0) + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2020 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + + +/* mark for precompiled code ('Lua') */ +#define LUA_SIGNATURE "\x1bLua" + +/* option for multiple returns in 'lua_pcall' and 'lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** Pseudo-indices +** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty +** space after that to help overflow detection) +*/ +#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + +#define LUA_NUMTYPES 9 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* predefined values in the registry */ +#define LUA_RIDX_MAINTHREAD 1 +#define LUA_RIDX_GLOBALS 2 +#define LUA_RIDX_LAST LUA_RIDX_GLOBALS + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + +/* unsigned integer type */ +typedef LUA_UNSIGNED lua_Unsigned; + +/* type for continuation-function contexts */ +typedef LUA_KCONTEXT lua_KContext; + + +/* +** Type for C functions registered with Lua +*/ +typedef int (*lua_CFunction) (lua_State *L); + +/* +** Type for continuation functions +*/ +typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); + + +/* +** Type for functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); + + +/* +** Type for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** Type for warning functions +*/ +typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); + + + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* +** RCS ident string +*/ +extern const char lua_ident[]; + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); +LUA_API int (lua_resetthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +LUA_API lua_Number (lua_version) (lua_State *L); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_absindex) (lua_State *L, int idx); +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_rotate) (lua_State *L, int idx, int n); +LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); +LUA_API int (lua_checkstack) (lua_State *L, int n); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isinteger) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** Comparison and arithmetic functions +*/ + +#define LUA_OPADD 0 /* ORDER TM, ORDER OP */ +#define LUA_OPSUB 1 +#define LUA_OPMUL 2 +#define LUA_OPMOD 3 +#define LUA_OPPOW 4 +#define LUA_OPDIV 5 +#define LUA_OPIDIV 6 +#define LUA_OPBAND 7 +#define LUA_OPBOR 8 +#define LUA_OPBXOR 9 +#define LUA_OPSHL 10 +#define LUA_OPSHR 11 +#define LUA_OPUNM 12 +#define LUA_OPBNOT 13 + +LUA_API void (lua_arith) (lua_State *L, int op); + +#define LUA_OPEQ 0 +#define LUA_OPLT 1 +#define LUA_OPLE 2 + +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); +LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API int (lua_getglobal) (lua_State *L, const char *name); +LUA_API int (lua_gettable) (lua_State *L, int idx); +LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawget) (lua_State *L, int idx); +LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); + +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_setglobal) (lua_State *L, const char *name); +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n); + + +/* +** 'load' and 'call' functions (load and run Lua code) +*/ +LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k); +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) + +LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k); +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) + +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, const char *mode); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k); +LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg, + int *nres); +LUA_API int (lua_status) (lua_State *L); +LUA_API int (lua_isyieldable) (lua_State *L); + +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) + + +/* +** Warning-related functions +*/ +LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud); +LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont); + + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCISRUNNING 9 +#define LUA_GCGEN 10 +#define LUA_GCINC 11 + +LUA_API int (lua_gc) (lua_State *L, int what, ...); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); +LUA_API void (lua_len) (lua_State *L, int idx); + +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); + +LUA_API void (lua_toclose) (lua_State *L, int idx); + + +/* +** {============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) + +#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) lua_pushstring(L, "" s) + +#define lua_pushglobaltable(L) \ + ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + +#define lua_insert(L,idx) lua_rotate(L, (idx), 1) + +#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) + +#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) + +/* }============================================================== */ + + +/* +** {============================================================== +** compatibility macros +** =============================================================== +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) +#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) +#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) + +#endif + +#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) +#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) +#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1) + +#define LUA_NUMTAGS LUA_NUMTYPES + +/* }============================================================== */ + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILCALL 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debugger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); +LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); +LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); + +LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); +LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, + int fidx2, int n2); + +LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook (lua_gethook) (lua_State *L); +LUA_API int (lua_gethookmask) (lua_State *L); +LUA_API int (lua_gethookcount) (lua_State *L); + +LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit); + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ + const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ + const char *source; /* (S) */ + size_t srclen; /* (S) */ + int currentline; /* (l) */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + unsigned char nups; /* (u) number of upvalues */ + unsigned char nparams;/* (u) number of parameters */ + char isvararg; /* (u) */ + char istailcall; /* (t) */ + unsigned short ftransfer; /* (r) index of first value transferred */ + unsigned short ntransfer; /* (r) number of transferred values */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + struct CallInfo *i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2020 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/Lua/lua.hpp b/Lua/lua.hpp new file mode 100644 index 00000000..ec417f59 --- /dev/null +++ b/Lua/lua.hpp @@ -0,0 +1,9 @@ +// lua.hpp +// Lua header files for C++ +// <> not supplied automatically because Lua also compiles as C++ + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} diff --git a/Lua/luac.c b/Lua/luac.c new file mode 100644 index 00000000..56ddc414 --- /dev/null +++ b/Lua/luac.c @@ -0,0 +1,724 @@ +/* +** $Id: luac.c $ +** Lua compiler (saves bytecodes to files; also lists bytecodes) +** See Copyright Notice in lua.h +*/ + +#define luac_c +#define LUA_CORE + +#include "lprefix.h" + +#include +#include +#include +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "ldebug.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lopnames.h" +#include "lstate.h" +#include "lundump.h" + +static void PrintFunction(const Proto* f, int full); +#define luaU_print PrintFunction + +#define PROGNAME "luac" /* default program name */ +#define OUTPUT PROGNAME ".out" /* default output file */ + +static int listing=0; /* list bytecodes? */ +static int dumping=1; /* dump bytecodes? */ +static int stripping=0; /* strip debug information? */ +static char Output[]={ OUTPUT }; /* default output file name */ +static const char* output=Output; /* actual output file name */ +static const char* progname=PROGNAME; /* actual program name */ +static TString **tmname; + +static void fatal(const char* message) +{ + fprintf(stderr,"%s: %s\n",progname,message); + exit(EXIT_FAILURE); +} + +static void cannot(const char* what) +{ + fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno)); + exit(EXIT_FAILURE); +} + +static void usage(const char* message) +{ + if (*message=='-') + fprintf(stderr,"%s: unrecognized option '%s'\n",progname,message); + else + fprintf(stderr,"%s: %s\n",progname,message); + fprintf(stderr, + "usage: %s [options] [filenames]\n" + "Available options are:\n" + " -l list (use -l -l for full listing)\n" + " -o name output to file 'name' (default is \"%s\")\n" + " -p parse only\n" + " -s strip debug information\n" + " -v show version information\n" + " -- stop handling options\n" + " - stop handling options and process stdin\n" + ,progname,Output); + exit(EXIT_FAILURE); +} + +#define IS(s) (strcmp(argv[i],s)==0) + +static int doargs(int argc, char* argv[]) +{ + int i; + int version=0; + if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0]; + for (i=1; itop+(i))) + +static const Proto* combine(lua_State* L, int n) +{ + if (n==1) + return toproto(L,-1); + else + { + Proto* f; + int i=n; + if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1)); + f=toproto(L,-1); + for (i=0; ip[i]=toproto(L,i-n-1); + if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0; + } + f->sizelineinfo=0; + return f; + } +} + +static int writer(lua_State* L, const void* p, size_t size, void* u) +{ + UNUSED(L); + return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); +} + +static int pmain(lua_State* L) +{ + int argc=(int)lua_tointeger(L,1); + char** argv=(char**)lua_touserdata(L,2); + const Proto* f; + int i; + tmname=G(L)->tmname; + if (!lua_checkstack(L,argc)) fatal("too many input files"); + for (i=0; i1); + if (dumping) + { + FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); + if (D==NULL) cannot("open"); + lua_lock(L); + luaU_dump(L,f,writer,D,stripping); + lua_unlock(L); + if (ferror(D)) cannot("write"); + if (fclose(D)) cannot("close"); + } + return 0; +} + +int main(int argc, char* argv[]) +{ + lua_State* L; + int i=doargs(argc,argv); + argc-=i; argv+=i; + if (argc<=0) usage("no input files given"); + L=luaL_newstate(); + if (L==NULL) fatal("cannot create state: not enough memory"); + lua_pushcfunction(L,&pmain); + lua_pushinteger(L,argc); + lua_pushlightuserdata(L,argv); + if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1)); + lua_close(L); + return EXIT_SUCCESS; +} + +/* +** print bytecodes +*/ + +#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-") +#define VOID(p) ((const void*)(p)) +#define eventname(i) (getstr(tmname[i])) + +static void PrintString(const TString* ts) +{ + const char* s=getstr(ts); + size_t i,n=tsslen(ts); + printf("\""); + for (i=0; ik[i]; + switch (ttypetag(o)) + { + case LUA_VNIL: + printf("N"); + break; + case LUA_VFALSE: + case LUA_VTRUE: + printf("B"); + break; + case LUA_VNUMFLT: + printf("F"); + break; + case LUA_VNUMINT: + printf("I"); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + printf("S"); + break; + default: /* cannot happen */ + printf("?%d",ttypetag(o)); + break; + } + printf("\t"); +} + +static void PrintConstant(const Proto* f, int i) +{ + const TValue* o=&f->k[i]; + switch (ttypetag(o)) + { + case LUA_VNIL: + printf("nil"); + break; + case LUA_VFALSE: + printf("false"); + break; + case LUA_VTRUE: + printf("true"); + break; + case LUA_VNUMFLT: + { + char buff[100]; + sprintf(buff,LUA_NUMBER_FMT,fltvalue(o)); + printf("%s",buff); + if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0"); + break; + } + case LUA_VNUMINT: + printf(LUA_INTEGER_FMT,ivalue(o)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + PrintString(tsvalue(o)); + break; + default: /* cannot happen */ + printf("?%d",ttypetag(o)); + break; + } +} + +#define COMMENT "\t; " +#define EXTRAARG GETARG_Ax(code[pc+1]) +#define EXTRAARGC (EXTRAARG*(MAXARG_C+1)) +#define ISK (isk ? "k" : "") + +static void PrintCode(const Proto* f) +{ + const Instruction* code=f->code; + int pc,n=f->sizecode; + for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); + printf("%-9s\t",opnames[o]); + switch (o) + { + case OP_MOVE: + printf("%d %d",a,b); + break; + case OP_LOADI: + printf("%d %d",a,sbx); + break; + case OP_LOADF: + printf("%d %d",a,sbx); + break; + case OP_LOADK: + printf("%d %d",a,bx); + printf(COMMENT); PrintConstant(f,bx); + break; + case OP_LOADKX: + printf("%d",a); + printf(COMMENT); PrintConstant(f,EXTRAARG); + break; + case OP_LOADFALSE: + printf("%d",a); + break; + case OP_LFALSESKIP: + printf("%d",a); + break; + case OP_LOADTRUE: + printf("%d",a); + break; + case OP_LOADNIL: + printf("%d %d",a,b); + printf(COMMENT "%d out",b+1); + break; + case OP_GETUPVAL: + printf("%d %d",a,b); + printf(COMMENT "%s",UPVALNAME(b)); + break; + case OP_SETUPVAL: + printf("%d %d",a,b); + printf(COMMENT "%s",UPVALNAME(b)); + break; + case OP_GETTABUP: + printf("%d %d %d",a,b,c); + printf(COMMENT "%s",UPVALNAME(b)); + printf(" "); PrintConstant(f,c); + break; + case OP_GETTABLE: + printf("%d %d %d",a,b,c); + break; + case OP_GETI: + printf("%d %d %d",a,b,c); + break; + case OP_GETFIELD: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_SETTABUP: + printf("%d %d %d%s",a,b,c,ISK); + printf(COMMENT "%s",UPVALNAME(a)); + printf(" "); PrintConstant(f,b); + if (isk) { printf(" "); PrintConstant(f,c); } + break; + case OP_SETTABLE: + printf("%d %d %d%s",a,b,c,ISK); + if (isk) { printf(COMMENT); PrintConstant(f,c); } + break; + case OP_SETI: + printf("%d %d %d%s",a,b,c,ISK); + if (isk) { printf(COMMENT); PrintConstant(f,c); } + break; + case OP_SETFIELD: + printf("%d %d %d%s",a,b,c,ISK); + printf(COMMENT); PrintConstant(f,b); + if (isk) { printf(" "); PrintConstant(f,c); } + break; + case OP_NEWTABLE: + printf("%d %d %d",a,b,c); + printf(COMMENT "%d",c+EXTRAARGC); + break; + case OP_SELF: + printf("%d %d %d%s",a,b,c,ISK); + if (isk) { printf(COMMENT); PrintConstant(f,c); } + break; + case OP_ADDI: + printf("%d %d %d",a,b,sc); + break; + case OP_ADDK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_SUBK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_MULK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_MODK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_POWK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_DIVK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_IDIVK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_BANDK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_BORK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_BXORK: + printf("%d %d %d",a,b,c); + printf(COMMENT); PrintConstant(f,c); + break; + case OP_SHRI: + printf("%d %d %d",a,b,sc); + break; + case OP_SHLI: + printf("%d %d %d",a,b,sc); + break; + case OP_ADD: + printf("%d %d %d",a,b,c); + break; + case OP_SUB: + printf("%d %d %d",a,b,c); + break; + case OP_MUL: + printf("%d %d %d",a,b,c); + break; + case OP_MOD: + printf("%d %d %d",a,b,c); + break; + case OP_POW: + printf("%d %d %d",a,b,c); + break; + case OP_DIV: + printf("%d %d %d",a,b,c); + break; + case OP_IDIV: + printf("%d %d %d",a,b,c); + break; + case OP_BAND: + printf("%d %d %d",a,b,c); + break; + case OP_BOR: + printf("%d %d %d",a,b,c); + break; + case OP_BXOR: + printf("%d %d %d",a,b,c); + break; + case OP_SHL: + printf("%d %d %d",a,b,c); + break; + case OP_SHR: + printf("%d %d %d",a,b,c); + break; + case OP_MMBIN: + printf("%d %d %d",a,b,c); + printf(COMMENT "%s",eventname(c)); + break; + case OP_MMBINI: + printf("%d %d %d %d",a,sb,c,isk); + printf(COMMENT "%s",eventname(c)); + if (isk) printf(" flip"); + break; + case OP_MMBINK: + printf("%d %d %d %d",a,b,c,isk); + printf(COMMENT "%s ",eventname(c)); PrintConstant(f,b); + if (isk) printf(" flip"); + break; + case OP_UNM: + printf("%d %d",a,b); + break; + case OP_BNOT: + printf("%d %d",a,b); + break; + case OP_NOT: + printf("%d %d",a,b); + break; + case OP_LEN: + printf("%d %d",a,b); + break; + case OP_CONCAT: + printf("%d %d",a,b); + break; + case OP_CLOSE: + printf("%d",a); + break; + case OP_TBC: + printf("%d",a); + break; + case OP_JMP: + printf("%d",GETARG_sJ(i)); + printf(COMMENT "to %d",GETARG_sJ(i)+pc+2); + break; + case OP_EQ: + printf("%d %d %d",a,b,isk); + break; + case OP_LT: + printf("%d %d %d",a,b,isk); + break; + case OP_LE: + printf("%d %d %d",a,b,isk); + break; + case OP_EQK: + printf("%d %d %d",a,b,isk); + printf(COMMENT); PrintConstant(f,b); + break; + case OP_EQI: + printf("%d %d %d",a,sb,isk); + break; + case OP_LTI: + printf("%d %d %d",a,sb,isk); + break; + case OP_LEI: + printf("%d %d %d",a,sb,isk); + break; + case OP_GTI: + printf("%d %d %d",a,sb,isk); + break; + case OP_GEI: + printf("%d %d %d",a,sb,isk); + break; + case OP_TEST: + printf("%d %d",a,isk); + break; + case OP_TESTSET: + printf("%d %d %d",a,b,isk); + break; + case OP_CALL: + printf("%d %d %d",a,b,c); + printf(COMMENT); + if (b==0) printf("all in "); else printf("%d in ",b-1); + if (c==0) printf("all out"); else printf("%d out",c-1); + break; + case OP_TAILCALL: + printf("%d %d %d",a,b,c); + printf(COMMENT "%d in",b-1); + break; + case OP_RETURN: + printf("%d %d %d",a,b,c); + printf(COMMENT); + if (b==0) printf("all out"); else printf("%d out",b-1); + break; + case OP_RETURN0: + break; + case OP_RETURN1: + printf("%d",a); + break; + case OP_FORLOOP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc-bx+2); + break; + case OP_FORPREP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc+bx+2); + break; + case OP_TFORPREP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc+bx+2); + break; + case OP_TFORCALL: + printf("%d %d",a,c); + break; + case OP_TFORLOOP: + printf("%d %d",a,bx); + printf(COMMENT "to %d",pc-bx+2); + break; + case OP_SETLIST: + printf("%d %d %d",a,b,c); + if (isk) printf(COMMENT "%d",c+EXTRAARGC); + break; + case OP_CLOSURE: + printf("%d %d",a,bx); + printf(COMMENT "%p",VOID(f->p[bx])); + break; + case OP_VARARG: + printf("%d %d",a,c); + printf(COMMENT); + if (c==0) printf("all out"); else printf("%d out",c-1); + break; + case OP_VARARGPREP: + printf("%d",a); + break; + case OP_EXTRAARG: + printf("%d",ax); + break; +#if 0 + default: + printf("%d %d %d",a,b,c); + printf(COMMENT "not handled"); + break; +#endif + } + printf("\n"); + } +} + + +#define SS(x) ((x==1)?"":"s") +#define S(x) (int)(x),SS(x) + +static void PrintHeader(const Proto* f) +{ + const char* s=f->source ? getstr(f->source) : "=?"; + if (*s=='@' || *s=='=') + s++; + else if (*s==LUA_SIGNATURE[0]) + s="(bstring)"; + else + s="(string)"; + printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n", + (f->linedefined==0)?"main":"function",s, + f->linedefined,f->lastlinedefined, + S(f->sizecode),VOID(f)); + printf("%d%s param%s, %d slot%s, %d upvalue%s, ", + (int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams), + S(f->maxstacksize),S(f->sizeupvalues)); + printf("%d local%s, %d constant%s, %d function%s\n", + S(f->sizelocvars),S(f->sizek),S(f->sizep)); +} + +static void PrintDebug(const Proto* f) +{ + int i,n; + n=f->sizek; + printf("constants (%d) for %p:\n",n,VOID(f)); + for (i=0; isizelocvars; + printf("locals (%d) for %p:\n",n,VOID(f)); + for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); + } + n=f->sizeupvalues; + printf("upvalues (%d) for %p:\n",n,VOID(f)); + for (i=0; iupvalues[i].instack,f->upvalues[i].idx); + } +} + +static void PrintFunction(const Proto* f, int full) +{ + int i,n=f->sizep; + PrintHeader(f); + PrintCode(f); + if (full) PrintDebug(f); + for (i=0; ip[i],full); +} diff --git a/Lua/luaconf.h b/Lua/luaconf.h new file mode 100644 index 00000000..bdf927e7 --- /dev/null +++ b/Lua/luaconf.h @@ -0,0 +1,776 @@ +/* +** $Id: luaconf.h $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef luaconf_h +#define luaconf_h + +#include +#include + + +/* +** =================================================================== +** General Configuration File for Lua +** +** Some definitions here can be changed externally, through the +** compiler (e.g., with '-D' options). Those are protected by +** '#if !defined' guards. However, several other definitions should +** be changed directly here, either because they affect the Lua +** ABI (by making the changes here, you ensure that all software +** connected to Lua, such as C libraries, will be compiled with the +** same configuration); or because they are seldom changed. +** +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +** {==================================================================== +** System Configuration: macros to adapt (if needed) Lua to some +** particular platform, for instance restricting it to C89. +** ===================================================================== +*/ + +/* +@@ LUAI_MAXCSTACK defines the maximum depth for nested calls and +** also limits the maximum depth of other recursive algorithms in +** the implementation, such as syntactic analysis. A value too +** large may allow the interpreter to crash (C-stack overflow). +** The default value seems ok for regular machines, but may be +** too high for restricted hardware. +** The test file 'cstack.lua' may help finding a good limit. +** (It will crash with a limit too high.) +*/ +#if !defined(LUAI_MAXCSTACK) +#define LUAI_MAXCSTACK 2000 +#endif + + +/* +@@ LUA_USE_C89 controls the use of non-ISO-C89 features. +** Define it if you want Lua to avoid the use of a few C99 features +** or Windows-specific features on Windows. +*/ +/* #define LUA_USE_C89 */ + + +/* +** By default, Lua on Windows use (some) specific Windows features +*/ +#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_USE_WINDOWS /* enable goodies for regular Windows */ +#endif + + +#if defined(LUA_USE_WINDOWS) +#define LUA_DL_DLL /* enable support for DLL */ +#define LUA_USE_C89 /* broadly, Windows is C89 */ +#endif + + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#endif + + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ +#endif + + +/* +@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. +*/ +#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3) + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Number types. +** =================================================================== +*/ + +/* +@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. +*/ +/* #define LUA_32BITS */ + + +/* +@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for +** C89 ('long' and 'double'); Windows always has '__int64', so it does +** not need to use this case. +*/ +#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) +#define LUA_C89_NUMBERS +#endif + + +/* +@@ LUA_INT_TYPE defines the type for Lua integers. +@@ LUA_FLOAT_TYPE defines the type for Lua floats. +** Lua should work fine with any mix of these options supported +** by your C compiler. The usual configurations are 64-bit integers +** and 'double' (the default), 32-bit integers and 'float' (for +** restricted platforms), and 'long'/'double' (for C compilers not +** compliant with C99, which may not have support for 'long long'). +*/ + +/* predefined options for LUA_INT_TYPE */ +#define LUA_INT_INT 1 +#define LUA_INT_LONG 2 +#define LUA_INT_LONGLONG 3 + +/* predefined options for LUA_FLOAT_TYPE */ +#define LUA_FLOAT_FLOAT 1 +#define LUA_FLOAT_DOUBLE 2 +#define LUA_FLOAT_LONGDOUBLE 3 + +#if defined(LUA_32BITS) /* { */ +/* +** 32-bit integers and 'float' +*/ +#if LUAI_IS32INT /* use 'int' if big enough */ +#define LUA_INT_TYPE LUA_INT_INT +#else /* otherwise use 'long' */ +#define LUA_INT_TYPE LUA_INT_LONG +#endif +#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT + +#elif defined(LUA_C89_NUMBERS) /* }{ */ +/* +** largest types available for C89 ('long' and 'double') +*/ +#define LUA_INT_TYPE LUA_INT_LONG +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE + +#endif /* } */ + + +/* +** default configuration for 64-bit Lua ('long long' and 'double') +*/ +#if !defined(LUA_INT_TYPE) +#define LUA_INT_TYPE LUA_INT_LONGLONG +#endif + +#if !defined(LUA_FLOAT_TYPE) +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE +#endif + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Paths. +** =================================================================== +*/ + +/* +** LUA_PATH_SEP is the character that separates templates in a path. +** LUA_PATH_MARK is the string that marks the substitution points in a +** template. +** LUA_EXEC_DIR in a Windows path is replaced by the executable's +** directory. +*/ +#define LUA_PATH_SEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXEC_DIR "!" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +** Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +** C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ + +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#if defined(_WIN32) /* { */ +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll" +#endif + +#else /* }{ */ + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + "./?.lua;" "./?/init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" +#endif + +#endif /* } */ + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if !defined(LUA_DIRSEP) + +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Marks for exported symbols in the C code +** =================================================================== +*/ + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all auxiliary library functions. +@@ LUAMOD_API is a mark for all standard library opening functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) /* { */ + +#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ +#define LUA_API __declspec(dllexport) +#else /* }{ */ +#define LUA_API __declspec(dllimport) +#endif /* } */ + +#else /* }{ */ + +#define LUA_API extern + +#endif /* } */ + + +/* +** More often than not the libs go together with the core. +*/ +#define LUALIB_API LUA_API +#define LUAMOD_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +** exported to outside modules. +@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, +** none of which to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. Not all elf targets support +** this attribute. Unfortunately, gcc does not offer a way to check +** whether the target offers that support, and those without support +** give a warning about it. To avoid these warnings, change to the +** default definition. +*/ +#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) /* { */ +#define LUAI_FUNC __attribute__((visibility("internal"))) extern +#else /* }{ */ +#define LUAI_FUNC extern +#endif /* } */ + +#define LUAI_DDEC(dec) LUAI_FUNC dec +#define LUAI_DDEF /* empty */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Compatibility with previous versions +** =================================================================== +*/ + +/* +@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3. +** You can define it to get all options, or change specific options +** to fit your specific needs. +*/ +#if defined(LUA_COMPAT_5_3) /* { */ + +/* +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. +** (These functions were already officially removed in 5.3; +** nevertheless they are still available here.) +*/ +#define LUA_COMPAT_MATHLIB + +/* +@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for +** manipulating other integer types (lua_pushunsigned, lua_tounsigned, +** luaL_checkint, luaL_checklong, etc.) +** (These macros were also officially removed in 5.3, but they are still +** available here.) +*/ +#define LUA_COMPAT_APIINTCASTS + + +/* +@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod +** using '__lt'. +*/ +#define LUA_COMPAT_LT_LE + + +/* +@@ The following macros supply trivial compatibility for some +** changes in the API. The macros themselves document how to +** change your code to avoid using them. +** (Once more, these macros were officially removed in 5.3, but they are +** still available here.) +*/ +#define lua_strlen(L,i) lua_rawlen(L, (i)) + +#define lua_objlen(L,i) lua_rawlen(L, (i)) + +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) + +#endif /* } */ + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Numbers. +** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* +** satisfy your needs. +** =================================================================== +*/ + +/* +@@ LUA_NUMBER is the floating-point type used by Lua. +@@ LUAI_UACNUMBER is the result of a 'default argument promotion' +@@ over a floating number. +@@ l_floatatt(x) corrects float attribute 'x' to the proper float type +** by prefixing it with one of FLT/DBL/LDBL. +@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. +@@ LUA_NUMBER_FMT is the format for writing floats. +@@ lua_number2str converts a float to a string. +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. +@@ lua_str2number converts a decimal numeral to a number. +*/ + + +/* The following definitions are good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + +#define lua_number2str(s,sz,n) \ + l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n)) + +/* +@@ lua_numbertointeger converts a float number with an integral value +** to an integer, or returns 0 if float is not within the range of +** a lua_Integer. (The range comparisons are tricky because of +** rounding. The tests here assume a two-complement representation, +** where MININTEGER always has an exact representation as a float; +** MAXINTEGER may not have one, and therefore its conversion to float +** may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + +/* now the variable definitions */ + +#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ + +#define LUA_NUMBER float + +#define l_floatatt(n) (FLT_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.7g" + +#define l_mathop(op) op##f + +#define lua_str2number(s,p) strtof((s), (p)) + + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ + +#define LUA_NUMBER long double + +#define l_floatatt(n) (LDBL_##n) + +#define LUAI_UACNUMBER long double + +#define LUA_NUMBER_FRMLEN "L" +#define LUA_NUMBER_FMT "%.19Lg" + +#define l_mathop(op) op##l + +#define lua_str2number(s,p) strtold((s), (p)) + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ + +#define LUA_NUMBER double + +#define l_floatatt(n) (DBL_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.14g" + +#define l_mathop(op) op + +#define lua_str2number(s,p) strtod((s), (p)) + +#else /* }{ */ + +#error "numeric float type not defined" + +#endif /* } */ + + + +/* +@@ LUA_INTEGER is the integer type used by Lua. +** +@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. +** +@@ LUAI_UACINT is the result of a 'default argument promotion' +@@ over a LUA_INTEGER. +@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. +@@ LUA_INTEGER_FMT is the format for writing integers. +@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. +@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. +@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED. +@@ LUA_UNSIGNEDBITS is the number of bits in a LUA_UNSIGNED. +@@ lua_integer2str converts an integer to a string. +*/ + + +/* The following definitions are good for most cases here */ + +#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" + +#define LUAI_UACINT LUA_INTEGER + +#define lua_integer2str(s,sz,n) \ + l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n)) + +/* +** use LUAI_UACINT here to avoid problems with promotions (which +** can turn a comparison between unsigneds into a signed comparison) +*/ +#define LUA_UNSIGNED unsigned LUAI_UACINT + + +#define LUA_UNSIGNEDBITS (sizeof(LUA_UNSIGNED) * CHAR_BIT) + + +/* now the variable definitions */ + +#if LUA_INT_TYPE == LUA_INT_INT /* { int */ + +#define LUA_INTEGER int +#define LUA_INTEGER_FRMLEN "" + +#define LUA_MAXINTEGER INT_MAX +#define LUA_MININTEGER INT_MIN + +#define LUA_MAXUNSIGNED UINT_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ + +#define LUA_INTEGER long +#define LUA_INTEGER_FRMLEN "l" + +#define LUA_MAXINTEGER LONG_MAX +#define LUA_MININTEGER LONG_MIN + +#define LUA_MAXUNSIGNED ULONG_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ + +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ +#if defined(LLONG_MAX) /* { */ +/* use ISO C99 stuff */ + +#define LUA_INTEGER long long +#define LUA_INTEGER_FRMLEN "ll" + +#define LUA_MAXINTEGER LLONG_MAX +#define LUA_MININTEGER LLONG_MIN + +#define LUA_MAXUNSIGNED ULLONG_MAX + +#elif defined(LUA_USE_WINDOWS) /* }{ */ +/* in Windows, can use specific Windows types */ + +#define LUA_INTEGER __int64 +#define LUA_INTEGER_FRMLEN "I64" + +#define LUA_MAXINTEGER _I64_MAX +#define LUA_MININTEGER _I64_MIN + +#define LUA_MAXUNSIGNED _UI64_MAX + +#else /* }{ */ + +#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ + or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" + +#endif /* } */ + +#else /* }{ */ + +#error "numeric integer type not defined" + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Dependencies with C99 and other C details +** =================================================================== +*/ + +/* +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) +*/ +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif + + +/* +@@ lua_strx2number converts a hexadecimal numeral to a number. +** In C99, 'strtod' does that conversion. Otherwise, you can +** leave 'lua_strx2number' undefined and Lua will provide its own +** implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_strx2number(s,p) lua_str2number(s,p) +#endif + + +/* +@@ lua_pointer2str converts a pointer to a readable string in a +** non-specified way. +*/ +#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) + + +/* +@@ lua_number2strx converts a float to a hexadecimal numeral. +** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +** Otherwise, you can leave 'lua_number2strx' undefined and Lua will +** provide its own implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_number2strx(L,b,sz,f,n) \ + ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n))) +#endif + + +/* +** 'strtof' and 'opf' variants for math functions are not valid in +** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the +** availability of these variants. ('math.h' is already included in +** all files that use these macros.) +*/ +#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) +#undef l_mathop /* variants not available */ +#undef lua_str2number +#define l_mathop(op) (lua_Number)op /* no variant */ +#define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) +#endif + + +/* +@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation +** functions. It must be a numerical type; Lua will use 'intptr_t' if +** available, otherwise it will use 'ptrdiff_t' (the nearest thing to +** 'intptr_t' in C89) +*/ +#define LUA_KCONTEXT ptrdiff_t + +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(INTPTR_MAX) /* even in C99 this type is optional */ +#undef LUA_KCONTEXT +#define LUA_KCONTEXT intptr_t +#endif +#endif + + +/* +@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). +** Change that if you do not want to use C locales. (Code using this +** macro must include the header 'locale.h'.) +*/ +#if !defined(lua_getlocaledecpoint) +#define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Language Variations +** ===================================================================== +*/ + +/* +@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some +** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from +** numbers to strings. Define LUA_NOCVTS2N to turn off automatic +** coercion from strings to numbers. +*/ +/* #define LUA_NOCVTN2S */ +/* #define LUA_NOCVTS2N */ + + +/* +@@ LUA_USE_APICHECK turns on several consistency checks on the C API. +** Define it as a help when debugging C code. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(l,e) assert(e) +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Macros that affect the API and must be stable (that is, must be the +** same when you compile Lua and when you compile code that links to +** Lua). +** ===================================================================== +*/ + +/* +@@ LUAI_MAXSTACK limits the size of the Lua stack. +** CHANGE it if you need a different limit. This limit is arbitrary; +** its only purpose is to stop Lua from consuming unlimited stack +** space (and to reserve some numbers for pseudo-indices). +** (It must fit into max(size_t)/32.) +*/ +#if LUAI_IS32INT +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK 15000 +#endif + + +/* +@@ LUA_EXTRASPACE defines the size of a raw memory area associated with +** a Lua state with very fast access. +** CHANGE it if you need a different size. +*/ +#define LUA_EXTRASPACE (sizeof(void *)) + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@@ of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +*/ +#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) + + +/* +@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure +** maximum alignment for the other items in that union. +*/ +#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l + +/* }================================================================== */ + + + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + + + +#endif + diff --git a/Lua/lualib.h b/Lua/lualib.h new file mode 100644 index 00000000..eb08b530 --- /dev/null +++ b/Lua/lualib.h @@ -0,0 +1,58 @@ +/* +** $Id: lualib.h $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* version suffix for environment variable names */ +#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR + + +LUAMOD_API int (luaopen_base) (lua_State *L); + +#define LUA_COLIBNAME "coroutine" +LUAMOD_API int (luaopen_coroutine) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUAMOD_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUAMOD_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUAMOD_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUAMOD_API int (luaopen_string) (lua_State *L); + +#define LUA_UTF8LIBNAME "utf8" +LUAMOD_API int (luaopen_utf8) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUAMOD_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUAMOD_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUAMOD_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#if !defined(lua_assert) +#define lua_assert(x) ((void)0) +#endif + + +#endif diff --git a/Lua/lundump.c b/Lua/lundump.c new file mode 100644 index 00000000..5aa55c44 --- /dev/null +++ b/Lua/lundump.c @@ -0,0 +1,333 @@ +/* +** $Id: lundump.c $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#define lundump_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstring.h" +#include "lundump.h" +#include "lzio.h" + + +#if !defined(luai_verifycode) +#define luai_verifycode(L,f) /* empty */ +#endif + + +typedef struct { + lua_State *L; + ZIO *Z; + const char *name; +} LoadState; + + +static l_noret error (LoadState *S, const char *why) { + luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why); + luaD_throw(S->L, LUA_ERRSYNTAX); +} + + +/* +** All high-level loads go through loadVector; you can change it to +** adapt to the endianness of the input +*/ +#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0])) + +static void loadBlock (LoadState *S, void *b, size_t size) { + if (luaZ_read(S->Z, b, size) != 0) + error(S, "truncated chunk"); +} + + +#define loadVar(S,x) loadVector(S,&x,1) + + +static lu_byte loadByte (LoadState *S) { + int b = zgetc(S->Z); + if (b == EOZ) + error(S, "truncated chunk"); + return cast_byte(b); +} + + +static size_t loadUnsigned (LoadState *S, size_t limit) { + size_t x = 0; + int b; + limit >>= 7; + do { + b = loadByte(S); + if (x >= limit) + error(S, "integer overflow"); + x = (x << 7) | (b & 0x7f); + } while ((b & 0x80) == 0); + return x; +} + + +static size_t loadSize (LoadState *S) { + return loadUnsigned(S, ~(size_t)0); +} + + +static int loadInt (LoadState *S) { + return cast_int(loadUnsigned(S, INT_MAX)); +} + + +static lua_Number loadNumber (LoadState *S) { + lua_Number x; + loadVar(S, x); + return x; +} + + +static lua_Integer loadInteger (LoadState *S) { + lua_Integer x; + loadVar(S, x); + return x; +} + + +/* +** Load a nullable string into prototype 'p'. +*/ +static TString *loadStringN (LoadState *S, Proto *p) { + lua_State *L = S->L; + TString *ts; + size_t size = loadSize(S); + if (size == 0) /* no string? */ + return NULL; + else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ + char buff[LUAI_MAXSHORTLEN]; + loadVector(S, buff, size); /* load string into buffer */ + ts = luaS_newlstr(L, buff, size); /* create string */ + } + else { /* long string */ + ts = luaS_createlngstrobj(L, size); /* create string */ + setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */ + luaD_inctop(L); + loadVector(S, getstr(ts), size); /* load directly in final place */ + L->top--; /* pop string */ + } + luaC_objbarrier(L, p, ts); + return ts; +} + + +/* +** Load a non-nullable string into prototype 'p'. +*/ +static TString *loadString (LoadState *S, Proto *p) { + TString *st = loadStringN(S, p); + if (st == NULL) + error(S, "bad format for constant string"); + return st; +} + + +static void loadCode (LoadState *S, Proto *f) { + int n = loadInt(S); + f->code = luaM_newvectorchecked(S->L, n, Instruction); + f->sizecode = n; + loadVector(S, f->code, n); +} + + +static void loadFunction(LoadState *S, Proto *f, TString *psource); + + +static void loadConstants (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->k = luaM_newvectorchecked(S->L, n, TValue); + f->sizek = n; + for (i = 0; i < n; i++) + setnilvalue(&f->k[i]); + for (i = 0; i < n; i++) { + TValue *o = &f->k[i]; + int t = loadByte(S); + switch (t) { + case LUA_VNIL: + setnilvalue(o); + break; + case LUA_VFALSE: + setbfvalue(o); + break; + case LUA_VTRUE: + setbtvalue(o); + break; + case LUA_VNUMFLT: + setfltvalue(o, loadNumber(S)); + break; + case LUA_VNUMINT: + setivalue(o, loadInteger(S)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + setsvalue2n(S->L, o, loadString(S, f)); + break; + default: lua_assert(0); + } + } +} + + +static void loadProtos (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->p = luaM_newvectorchecked(S->L, n, Proto *); + f->sizep = n; + for (i = 0; i < n; i++) + f->p[i] = NULL; + for (i = 0; i < n; i++) { + f->p[i] = luaF_newproto(S->L); + luaC_objbarrier(S->L, f, f->p[i]); + loadFunction(S, f->p[i], f->source); + } +} + + +/* +** Load the upvalues for a function. The names must be filled first, +** because the filling of the other fields can raise read errors and +** the creation of the error message can call an emergency collection; +** in that case all prototypes must be consistent for the GC. +*/ +static void loadUpvalues (LoadState *S, Proto *f) { + int i, n; + n = loadInt(S); + f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); + f->sizeupvalues = n; + for (i = 0; i < n; i++) /* make array valid for GC */ + f->upvalues[i].name = NULL; + for (i = 0; i < n; i++) { /* following calls can raise errors */ + f->upvalues[i].instack = loadByte(S); + f->upvalues[i].idx = loadByte(S); + f->upvalues[i].kind = loadByte(S); + } +} + + +static void loadDebug (LoadState *S, Proto *f) { + int i, n; + n = loadInt(S); + f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); + f->sizelineinfo = n; + loadVector(S, f->lineinfo, n); + n = loadInt(S); + f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); + f->sizeabslineinfo = n; + for (i = 0; i < n; i++) { + f->abslineinfo[i].pc = loadInt(S); + f->abslineinfo[i].line = loadInt(S); + } + n = loadInt(S); + f->locvars = luaM_newvectorchecked(S->L, n, LocVar); + f->sizelocvars = n; + for (i = 0; i < n; i++) + f->locvars[i].varname = NULL; + for (i = 0; i < n; i++) { + f->locvars[i].varname = loadStringN(S, f); + f->locvars[i].startpc = loadInt(S); + f->locvars[i].endpc = loadInt(S); + } + n = loadInt(S); + for (i = 0; i < n; i++) + f->upvalues[i].name = loadStringN(S, f); +} + + +static void loadFunction (LoadState *S, Proto *f, TString *psource) { + f->source = loadStringN(S, f); + if (f->source == NULL) /* no source in dump? */ + f->source = psource; /* reuse parent's source */ + f->linedefined = loadInt(S); + f->lastlinedefined = loadInt(S); + f->numparams = loadByte(S); + f->is_vararg = loadByte(S); + f->maxstacksize = loadByte(S); + loadCode(S, f); + loadConstants(S, f); + loadUpvalues(S, f); + loadProtos(S, f); + loadDebug(S, f); +} + + +static void checkliteral (LoadState *S, const char *s, const char *msg) { + char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */ + size_t len = strlen(s); + loadVector(S, buff, len); + if (memcmp(s, buff, len) != 0) + error(S, msg); +} + + +static void fchecksize (LoadState *S, size_t size, const char *tname) { + if (loadByte(S) != size) + error(S, luaO_pushfstring(S->L, "%s size mismatch", tname)); +} + + +#define checksize(S,t) fchecksize(S,sizeof(t),#t) + +static void checkHeader (LoadState *S) { + /* skip 1st char (already read and checked) */ + checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk"); + if (loadByte(S) != LUAC_VERSION) + error(S, "version mismatch"); + if (loadByte(S) != LUAC_FORMAT) + error(S, "format mismatch"); + checkliteral(S, LUAC_DATA, "corrupted chunk"); + checksize(S, Instruction); + checksize(S, lua_Integer); + checksize(S, lua_Number); + if (loadInteger(S) != LUAC_INT) + error(S, "integer format mismatch"); + if (loadNumber(S) != LUAC_NUM) + error(S, "float format mismatch"); +} + + +/* +** Load precompiled chunk. +*/ +LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { + LoadState S; + LClosure *cl; + if (*name == '@' || *name == '=') + S.name = name + 1; + else if (*name == LUA_SIGNATURE[0]) + S.name = "binary string"; + else + S.name = name; + S.L = L; + S.Z = Z; + checkHeader(&S); + cl = luaF_newLclosure(L, loadByte(&S)); + setclLvalue2s(L, L->top, cl); + luaD_inctop(L); + cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); + loadFunction(&S, cl->p, NULL); + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luai_verifycode(L, cl->p); + return cl; +} + diff --git a/Lua/lundump.h b/Lua/lundump.h new file mode 100644 index 00000000..f3748a99 --- /dev/null +++ b/Lua/lundump.h @@ -0,0 +1,36 @@ +/* +** $Id: lundump.h $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* data to catch conversion errors */ +#define LUAC_DATA "\x19\x93\r\n\x1a\n" + +#define LUAC_INT 0x5678 +#define LUAC_NUM cast_num(370.5) + +/* +** Encode major-minor version in one byte, one nibble for each +*/ +#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */ +#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR)) + +#define LUAC_FORMAT 0 /* this is the official format */ + +/* load one chunk; from lundump.c */ +LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, + void* data, int strip); + +#endif diff --git a/Lua/lutf8lib.c b/Lua/lutf8lib.c new file mode 100644 index 00000000..901d985f --- /dev/null +++ b/Lua/lutf8lib.c @@ -0,0 +1,289 @@ +/* +** $Id: lutf8lib.c $ +** Standard library for UTF-8 manipulation +** See Copyright Notice in lua.h +*/ + +#define lutf8lib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define MAXUNICODE 0x10FFFFu + +#define MAXUTF 0x7FFFFFFFu + +/* +** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. +*/ +#if (UINT_MAX >> 30) >= 1 +typedef unsigned int utfint; +#else +typedef unsigned long utfint; +#endif + + +#define iscont(p) ((*(p) & 0xC0) == 0x80) + + +/* from strlib */ +/* translate a relative string position: negative means back from end */ +static lua_Integer u_posrelat (lua_Integer pos, size_t len) { + if (pos >= 0) return pos; + else if (0u - (size_t)pos > len) return 0; + else return (lua_Integer)len + pos + 1; +} + + +/* +** Decode one UTF-8 sequence, returning NULL if byte sequence is +** invalid. The array 'limits' stores the minimum value for each +** sequence length, to check for overlong representations. Its first +** entry forces an error for non-ascii bytes with no continuation +** bytes (count == 0). +*/ +static const char *utf8_decode (const char *s, utfint *val, int strict) { + static const utfint limits[] = + {~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; + unsigned int c = (unsigned char)s[0]; + utfint res = 0; /* final result */ + if (c < 0x80) /* ascii? */ + res = c; + else { + int count = 0; /* to count number of continuation bytes */ + for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ + unsigned int cc = (unsigned char)s[++count]; /* read next byte */ + if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ + return NULL; /* invalid byte sequence */ + res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ + } + res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */ + if (count > 5 || res > MAXUTF || res < limits[count]) + return NULL; /* invalid byte sequence */ + s += count; /* skip continuation bytes read */ + } + if (strict) { + /* check for invalid code points; too large or surrogates */ + if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu)) + return NULL; + } + if (val) *val = res; + return s + 1; /* +1 to include first byte */ +} + + +/* +** utf8len(s [, i [, j [, lax]]]) --> number of characters that +** start in the range [i,j], or nil + current position if 's' is not +** well formed in that interval +*/ +static int utflen (lua_State *L) { + lua_Integer n = 0; /* counter for the number of characters */ + size_t len; /* string length in bytes */ + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); + int lax = lua_toboolean(L, 4); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, + "initial position out of bounds"); + luaL_argcheck(L, --posj < (lua_Integer)len, 3, + "final position out of bounds"); + while (posi <= posj) { + const char *s1 = utf8_decode(s + posi, NULL, !lax); + if (s1 == NULL) { /* conversion error? */ + luaL_pushfail(L); /* return fail ... */ + lua_pushinteger(L, posi + 1); /* ... and current position */ + return 2; + } + posi = s1 - s; + n++; + } + lua_pushinteger(L, n); + return 1; +} + + +/* +** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all +** characters that start in the range [i,j] +*/ +static int codepoint (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); + int lax = lua_toboolean(L, 4); + int n; + const char *se; + luaL_argcheck(L, posi >= 1, 2, "out of bounds"); + luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds"); + if (posi > pose) return 0; /* empty interval; return no values */ + if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; /* upper bound for number of returns */ + luaL_checkstack(L, n, "string slice too long"); + n = 0; /* count the number of returns */ + se = s + pose; /* string end */ + for (s += posi - 1; s < se;) { + utfint code; + s = utf8_decode(s, &code, !lax); + if (s == NULL) + return luaL_error(L, "invalid UTF-8 code"); + lua_pushinteger(L, code); + n++; + } + return n; +} + + +static void pushutfchar (lua_State *L, int arg) { + lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); + luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); + lua_pushfstring(L, "%U", (long)code); +} + + +/* +** utfchar(n1, n2, ...) -> char(n1)..char(n2)... +*/ +static int utfchar (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + if (n == 1) /* optimize common case of single char */ + pushutfchar(L, 1); + else { + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i = 1; i <= n; i++) { + pushutfchar(L, i); + luaL_addvalue(&b); + } + luaL_pushresult(&b); + } + return 1; +} + + +/* +** offset(s, n, [i]) -> index where n-th character counting from +** position 'i' starts; 0 means character at 'i'. +*/ +static int byteoffset (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = luaL_checkinteger(L, 2); + lua_Integer posi = (n >= 0) ? 1 : len + 1; + posi = u_posrelat(luaL_optinteger(L, 3, posi), len); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, + "position out of bounds"); + if (n == 0) { + /* find beginning of current byte sequence */ + while (posi > 0 && iscont(s + posi)) posi--; + } + else { + if (iscont(s + posi)) + return luaL_error(L, "initial position is a continuation byte"); + if (n < 0) { + while (n < 0 && posi > 0) { /* move back */ + do { /* find beginning of previous character */ + posi--; + } while (posi > 0 && iscont(s + posi)); + n++; + } + } + else { + n--; /* do not move for 1st character */ + while (n > 0 && posi < (lua_Integer)len) { + do { /* find beginning of next character */ + posi++; + } while (iscont(s + posi)); /* (cannot pass final '\0') */ + n--; + } + } + } + if (n == 0) /* did it find given character? */ + lua_pushinteger(L, posi + 1); + else /* no such character */ + luaL_pushfail(L); + return 1; +} + + +static int iter_aux (lua_State *L, int strict) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = lua_tointeger(L, 2) - 1; + if (n < 0) /* first iteration? */ + n = 0; /* start from here */ + else if (n < (lua_Integer)len) { + n++; /* skip current byte */ + while (iscont(s + n)) n++; /* and its continuations */ + } + if (n >= (lua_Integer)len) + return 0; /* no more codepoints */ + else { + utfint code; + const char *next = utf8_decode(s + n, &code, strict); + if (next == NULL) + return luaL_error(L, "invalid UTF-8 code"); + lua_pushinteger(L, n + 1); + lua_pushinteger(L, code); + return 2; + } +} + + +static int iter_auxstrict (lua_State *L) { + return iter_aux(L, 1); +} + +static int iter_auxlax (lua_State *L) { + return iter_aux(L, 0); +} + + +static int iter_codes (lua_State *L) { + int lax = lua_toboolean(L, 2); + luaL_checkstring(L, 1); + lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); + return 3; +} + + +/* pattern to match a single UTF-8 character */ +#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" + + +static const luaL_Reg funcs[] = { + {"offset", byteoffset}, + {"codepoint", codepoint}, + {"char", utfchar}, + {"len", utflen}, + {"codes", iter_codes}, + /* placeholders */ + {"charpattern", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_utf8 (lua_State *L) { + luaL_newlib(L, funcs); + lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1); + lua_setfield(L, -2, "charpattern"); + return 1; +} + diff --git a/Lua/lvm.c b/Lua/lvm.c new file mode 100644 index 00000000..08681af1 --- /dev/null +++ b/Lua/lvm.c @@ -0,0 +1,1810 @@ +/* +** $Id: lvm.c $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#define lvm_c +#define LUA_CORE + +#include "lprefix.h" + +#include +#include +#include +#include +#include +#include + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + +/* +** By default, use jump tables in the main interpreter loop on gcc +** and compatible compilers. +*/ +#if !defined(LUA_USE_JUMPTABLE) +#if defined(__GNUC__) +#define LUA_USE_JUMPTABLE 1 +#else +#define LUA_USE_JUMPTABLE 0 +#endif +#endif + + + +/* limit for table tag-method chains (to avoid infinite loops) */ +#define MAXTAGLOOP 2000 + + +/* +** 'l_intfitsf' checks whether a given integer is in the range that +** can be converted to a float without rounding. Used in comparisons. +*/ + +/* number of bits in the mantissa of a float */ +#define NBM (l_floatatt(MANT_DIG)) + +/* +** Check whether some integers may not fit in a float, testing whether +** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.) +** (The shifts are done in parts, to avoid shifting by more than the size +** of an integer. In a worst case, NBM == 113 for long double and +** sizeof(long) == 32.) +*/ +#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \ + >> (NBM - (3 * (NBM / 4)))) > 0 + +/* limit for integers that fit in a float */ +#define MAXINTFITSF ((lua_Unsigned)1 << NBM) + +/* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */ +#define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF)) + +#else /* all integers fit in a float precisely */ + +#define l_intfitsf(i) 1 + +#endif + + +/* +** Try to convert a value from string to a number value. +** If the value is not a string or is a string not representing +** a valid numeral (or if coercions from strings to numbers +** are disabled via macro 'cvt2num'), do not modify 'result' +** and return 0. +*/ +static int l_strton (const TValue *obj, TValue *result) { + lua_assert(obj != result); + if (!cvt2num(obj)) /* is object not a string? */ + return 0; + else + return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1); +} + + +/* +** Try to convert a value to a float. The float case is already handled +** by the macro 'tonumber'. +*/ +int luaV_tonumber_ (const TValue *obj, lua_Number *n) { + TValue v; + if (ttisinteger(obj)) { + *n = cast_num(ivalue(obj)); + return 1; + } + else if (l_strton(obj, &v)) { /* string coercible to number? */ + *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */ + return 1; + } + else + return 0; /* conversion failed */ +} + + +/* +** try to convert a float to an integer, rounding according to 'mode'. +*/ +int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) { + lua_Number f = l_floor(n); + if (n != f) { /* not an integral value? */ + if (mode == F2Ieq) return 0; /* fails if mode demands integral value */ + else if (mode == F2Iceil) /* needs ceil? */ + f += 1; /* convert floor to ceil (remember: n != f) */ + } + return lua_numbertointeger(f, p); +} + + +/* +** try to convert a value to an integer, rounding according to 'mode', +** without string coercion. +** ("Fast track" handled by macro 'tointegerns'.) +*/ +int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode) { + if (ttisfloat(obj)) + return luaV_flttointeger(fltvalue(obj), p, mode); + else if (ttisinteger(obj)) { + *p = ivalue(obj); + return 1; + } + else + return 0; +} + + +/* +** try to convert a value to an integer. +*/ +int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode) { + TValue v; + if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */ + obj = &v; /* change it to point to its corresponding number */ + return luaV_tointegerns(obj, p, mode); +} + + +/* +** Try to convert a 'for' limit to an integer, preserving the semantics +** of the loop. Return true if the loop must not run; otherwise, '*p' +** gets the integer limit. +** (The following explanation assumes a positive step; it is valid for +** negative steps mutatis mutandis.) +** If the limit is an integer or can be converted to an integer, +** rounding down, that is the limit. +** Otherwise, check whether the limit can be converted to a float. If +** the float is too large, clip it to LUA_MAXINTEGER. If the float +** is too negative, the loop should not run, because any initial +** integer value is greater than such limit; so, the function returns +** true to signal that. (For this latter case, no integer limit would be +** correct; even a limit of LUA_MININTEGER would run the loop once for +** an initial value equal to LUA_MININTEGER.) +*/ +static int forlimit (lua_State *L, lua_Integer init, const TValue *lim, + lua_Integer *p, lua_Integer step) { + if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) { + /* not coercible to in integer */ + lua_Number flim; /* try to convert to float */ + if (!tonumber(lim, &flim)) /* cannot convert to float? */ + luaG_forerror(L, lim, "limit"); + /* else 'flim' is a float out of integer bounds */ + if (luai_numlt(0, flim)) { /* if it is positive, it is too large */ + if (step < 0) return 1; /* initial value must be less than it */ + *p = LUA_MAXINTEGER; /* truncate */ + } + else { /* it is less than min integer */ + if (step > 0) return 1; /* initial value must be greater than it */ + *p = LUA_MININTEGER; /* truncate */ + } + } + return (step > 0 ? init > *p : init < *p); /* not to run? */ +} + + +/* +** Prepare a numerical for loop (opcode OP_FORPREP). +** Return true to skip the loop. Otherwise, +** after preparation, stack will be as follows: +** ra : internal index (safe copy of the control variable) +** ra + 1 : loop counter (integer loops) or limit (float loops) +** ra + 2 : step +** ra + 3 : control variable +*/ +static int forprep (lua_State *L, StkId ra) { + TValue *pinit = s2v(ra); + TValue *plimit = s2v(ra + 1); + TValue *pstep = s2v(ra + 2); + if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */ + lua_Integer init = ivalue(pinit); + lua_Integer step = ivalue(pstep); + lua_Integer limit; + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + setivalue(s2v(ra + 3), init); /* control variable */ + if (forlimit(L, init, plimit, &limit, step)) + return 1; /* skip the loop */ + else { /* prepare loop counter */ + lua_Unsigned count; + if (step > 0) { /* ascending loop? */ + count = l_castS2U(limit) - l_castS2U(init); + if (step != 1) /* avoid division in the too common case */ + count /= l_castS2U(step); + } + else { /* step < 0; descending loop */ + count = l_castS2U(init) - l_castS2U(limit); + /* 'step+1' avoids negating 'mininteger' */ + count /= l_castS2U(-(step + 1)) + 1u; + } + /* store the counter in place of the limit (which won't be + needed anymore */ + setivalue(plimit, l_castU2S(count)); + } + } + else { /* try making all values floats */ + lua_Number init; lua_Number limit; lua_Number step; + if (unlikely(!tonumber(plimit, &limit))) + luaG_forerror(L, plimit, "limit"); + if (unlikely(!tonumber(pstep, &step))) + luaG_forerror(L, pstep, "step"); + if (unlikely(!tonumber(pinit, &init))) + luaG_forerror(L, pinit, "initial value"); + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + if (luai_numlt(0, step) ? luai_numlt(limit, init) + : luai_numlt(init, limit)) + return 1; /* skip the loop */ + else { + /* make sure internal values are all floats */ + setfltvalue(plimit, limit); + setfltvalue(pstep, step); + setfltvalue(s2v(ra), init); /* internal index */ + setfltvalue(s2v(ra + 3), init); /* control variable */ + } + } + return 0; +} + + +/* +** Execute a step of a float numerical for loop, returning +** true iff the loop must continue. (The integer case is +** written online with opcode OP_FORLOOP, for performance.) +*/ +static int floatforloop (StkId ra) { + lua_Number step = fltvalue(s2v(ra + 2)); + lua_Number limit = fltvalue(s2v(ra + 1)); + lua_Number idx = fltvalue(s2v(ra)); /* internal index */ + idx = luai_numadd(L, idx, step); /* increment index */ + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + chgfltvalue(s2v(ra), idx); /* update internal index */ + setfltvalue(s2v(ra + 3), idx); /* and control variable */ + return 1; /* jump back */ + } + else + return 0; /* finish the loop */ +} + + +/* +** Finish the table access 'val = t[key]'. +** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to +** t[k] entry (which must be empty). +*/ +void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, + const TValue *slot) { + int loop; /* counter to avoid infinite loops */ + const TValue *tm; /* metamethod */ + for (loop = 0; loop < MAXTAGLOOP; loop++) { + if (slot == NULL) { /* 't' is not a table? */ + lua_assert(!ttistable(t)); + tm = luaT_gettmbyobj(L, t, TM_INDEX); + if (unlikely(notm(tm))) + luaG_typeerror(L, t, "index"); /* no metamethod */ + /* else will try the metamethod */ + } + else { /* 't' is a table */ + lua_assert(isempty(slot)); + tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ + if (tm == NULL) { /* no metamethod? */ + setnilvalue(s2v(val)); /* result is nil */ + return; + } + /* else will try the metamethod */ + } + if (ttisfunction(tm)) { /* is metamethod a function? */ + luaT_callTMres(L, tm, t, key, val); /* call it */ + return; + } + t = tm; /* else try to access 'tm[key]' */ + if (luaV_fastget(L, t, key, slot, luaH_get)) { /* fast track? */ + setobj2s(L, val, slot); /* done */ + return; + } + /* else repeat (tail call 'luaV_finishget') */ + } + luaG_runerror(L, "'__index' chain too long; possible loop"); +} + + +/* +** Finish a table assignment 't[key] = val'. +** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points +** to the entry 't[key]', or to a value with an absent key if there +** is no such entry. (The value at 'slot' must be empty, otherwise +** 'luaV_fastget' would have done the job.) +*/ +void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + TValue *val, const TValue *slot) { + int loop; /* counter to avoid infinite loops */ + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; /* '__newindex' metamethod */ + if (slot != NULL) { /* is 't' a table? */ + Table *h = hvalue(t); /* save 't' table */ + lua_assert(isempty(slot)); /* slot must be empty */ + tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ + if (tm == NULL) { /* no metamethod? */ + if (isabstkey(slot)) /* no previous entry? */ + slot = luaH_newkey(L, h, key); /* create one */ + /* no metamethod and (now) there is an entry with given key */ + setobj2t(L, cast(TValue *, slot), val); /* set its new value */ + invalidateTMcache(h); + luaC_barrierback(L, obj2gco(h), val); + return; + } + /* else will try the metamethod */ + } + else { /* not a table; check metamethod */ + tm = luaT_gettmbyobj(L, t, TM_NEWINDEX); + if (unlikely(notm(tm))) + luaG_typeerror(L, t, "index"); + } + /* try the metamethod */ + if (ttisfunction(tm)) { + luaT_callTM(L, tm, t, key, val); + return; + } + t = tm; /* else repeat assignment over 'tm' */ + if (luaV_fastget(L, t, key, slot, luaH_get)) { + luaV_finishfastset(L, t, slot, val); + return; /* done */ + } + /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ + } + luaG_runerror(L, "'__newindex' chain too long; possible loop"); +} + + +/* +** Compare two strings 'ls' x 'rs', returning an integer less-equal- +** -greater than zero if 'ls' is less-equal-greater than 'rs'. +** The code is a little tricky because it allows '\0' in the strings +** and it uses 'strcoll' (to respect locales) for each segments +** of the strings. +*/ +static int l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = tsslen(ls); + const char *r = getstr(rs); + size_t lr = tsslen(rs); + for (;;) { /* for each segment */ + int temp = strcoll(l, r); + if (temp != 0) /* not equal? */ + return temp; /* done */ + else { /* strings are equal up to a '\0' */ + size_t len = strlen(l); /* index of first '\0' in both strings */ + if (len == lr) /* 'rs' is finished? */ + return (len == ll) ? 0 : 1; /* check 'ls' */ + else if (len == ll) /* 'ls' is finished? */ + return -1; /* 'ls' is less than 'rs' ('rs' is not finished) */ + /* both strings longer than 'len'; go on comparing after the '\0' */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } +} + + +/* +** Check whether integer 'i' is less than float 'f'. If 'i' has an +** exact representation as a float ('l_intfitsf'), compare numbers as +** floats. Otherwise, use the equivalence 'i < f <=> i < ceil(f)'. +** If 'ceil(f)' is out of integer range, either 'f' is greater than +** all integers or less than all integers. +** (The test with 'l_intfitsf' is only for performance; the else +** case is correct for all values, but it is slow due to the conversion +** from float to int.) +** When 'f' is NaN, comparisons must result in false. +*/ +static int LTintfloat (lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numlt(cast_num(i), f); /* compare them as floats */ + else { /* i < f <=> i < ceil(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return i < fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether integer 'i' is less than or equal to float 'f'. +** See comments on previous function. +*/ +static int LEintfloat (lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numle(cast_num(i), f); /* compare them as floats */ + else { /* i <= f <=> i <= floor(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return i <= fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether float 'f' is less than integer 'i'. +** See comments on previous function. +*/ +static int LTfloatint (lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numlt(f, cast_num(i)); /* compare them as floats */ + else { /* f < i <=> floor(f) < i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return fi < i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Check whether float 'f' is less than or equal to integer 'i'. +** See comments on previous function. +*/ +static int LEfloatint (lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numle(f, cast_num(i)); /* compare them as floats */ + else { /* f <= i <=> ceil(f) <= i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return fi <= i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Return 'l < r', for numbers. +*/ +static int LTnum (const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li < ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LTintfloat(li, fltvalue(r)); /* l < r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numlt(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LTfloatint(lf, ivalue(r)); + } +} + + +/* +** Return 'l <= r', for numbers. +*/ +static int LEnum (const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li <= ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LEintfloat(li, fltvalue(r)); /* l <= r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numle(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LEfloatint(lf, ivalue(r)); + } +} + + +/* +** return 'l < r' for non-numbers. +*/ +static int lessthanothers (lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) < 0; + else + return luaT_callorderTM(L, l, r, TM_LT); +} + + +/* +** Main operation less than; return 'l < r'. +*/ +int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LTnum(l, r); + else return lessthanothers(L, l, r); +} + + +/* +** return 'l <= r' for non-numbers. +*/ +static int lessequalothers (lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) <= 0; + else + return luaT_callorderTM(L, l, r, TM_LE); +} + + +/* +** Main operation less than or equal to; return 'l <= r'. +*/ +int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LEnum(l, r); + else return lessequalothers(L, l, r); +} + + +/* +** Main operation for equality of Lua values; return 't1 == t2'. +** L == NULL means raw equality (no metamethods) +*/ +int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */ + if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) + return 0; /* only numbers can be equal with different variants */ + else { /* two numbers with different variants */ + lua_Integer i1, i2; /* compare them as integers */ + return (tointegerns(t1, &i1) && tointegerns(t2, &i2) && i1 == i2); + } + } + /* values have same type and same variant */ + switch (ttypetag(t1)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; + case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2)); + case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); + case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_VLCF: return fvalue(t1) == fvalue(t2); + case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); + case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); + case LUA_VUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: + return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) /* no TM? */ + return 0; /* objects are different */ + else { + luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */ + return !l_isfalse(s2v(L->top)); + } +} + + +/* macro used by 'luaV_concat' to ensure that element at 'o' is a string */ +#define tostring(L,o) \ + (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) + +#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) + +/* copy strings in stack from top - n up to top - 1 to buffer */ +static void copy2buff (StkId top, int n, char *buff) { + size_t tl = 0; /* size already copied */ + do { + size_t l = vslen(s2v(top - n)); /* length of string being copied */ + memcpy(buff + tl, svalue(s2v(top - n)), l * sizeof(char)); + tl += l; + } while (--n > 0); +} + + +/* +** Main operation for concatenation: concat 'total' values in the stack, +** from 'L->top - total' up to 'L->top - 1'. +*/ +void luaV_concat (lua_State *L, int total) { + if (total == 1) + return; /* "all" values already concatenated */ + do { + StkId top = L->top; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || + !tostring(L, s2v(top - 1))) + luaT_tryconcatTM(L); + else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ + cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ + else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ + setobjs2s(L, top - 2, top - 1); /* result is second op. */ + } + else { + /* at least two non-empty string values; get as many as possible */ + size_t tl = vslen(s2v(top - 1)); + TString *ts; + /* collect total length and number of strings */ + for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { + size_t l = vslen(s2v(top - n - 1)); + if (unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) + luaG_runerror(L, "string length overflow"); + tl += l; + } + if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ + char buff[LUAI_MAXSHORTLEN]; + copy2buff(top, n, buff); /* copy strings to buffer */ + ts = luaS_newlstr(L, buff, tl); + } + else { /* long string; copy strings directly to final result */ + ts = luaS_createlngstrobj(L, tl); + copy2buff(top, n, getstr(ts)); + } + setsvalue2s(L, top - n, ts); /* create result */ + } + total -= n-1; /* got 'n' strings to create 1 new */ + L->top -= n-1; /* popped 'n' strings and pushed one */ + } while (total > 1); /* repeat until only 1 result left */ +} + + +/* +** Main operation 'ra = #rb'. +*/ +void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { + const TValue *tm; + switch (ttypetag(rb)) { + case LUA_VTABLE: { + Table *h = hvalue(rb); + tm = fasttm(L, h->metatable, TM_LEN); + if (tm) break; /* metamethod? break switch to call it */ + setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */ + return; + } + case LUA_VSHRSTR: { + setivalue(s2v(ra), tsvalue(rb)->shrlen); + return; + } + case LUA_VLNGSTR: { + setivalue(s2v(ra), tsvalue(rb)->u.lnglen); + return; + } + default: { /* try metamethod */ + tm = luaT_gettmbyobj(L, rb, TM_LEN); + if (unlikely(notm(tm))) /* no metamethod? */ + luaG_typeerror(L, rb, "get length of"); + break; + } + } + luaT_callTMres(L, tm, rb, rb, ra); +} + + +/* +** Integer division; return 'm // n', that is, floor(m/n). +** C division truncates its result (rounds towards zero). +** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, +** otherwise 'floor(q) == trunc(q) - 1'. +*/ +lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) { + if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to divide by zero"); + return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ + } + else { + lua_Integer q = m / n; /* perform C division */ + if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ + q -= 1; /* correct result for different rounding */ + return q; + } +} + + +/* +** Integer modulus; return 'm % n'. (Assume that C '%' with +** negative operands follows C99 behavior. See previous comment +** about luaV_idiv.) +*/ +lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { + if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to perform 'n%%0'"); + return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ + } + else { + lua_Integer r = m % n; + if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */ + r += n; /* correct result for different rounding */ + return r; + } +} + + +/* +** Float modulus +*/ +lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { + lua_Number r; + luai_nummod(L, m, n, r); + return r; +} + + +/* number of bits in an integer */ +#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) + +/* +** Shift left operation. (Shift right just negates 'y'.) +*/ +#define luaV_shiftr(x,y) luaV_shiftl(x,-(y)) + +lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { + if (y < 0) { /* shift right? */ + if (y <= -NBITS) return 0; + else return intop(>>, x, -y); + } + else { /* shift left */ + if (y >= NBITS) return 0; + else return intop(<<, x, y); + } +} + + +/* +** create a new Lua closure, push it in the stack, and initialize +** its upvalues. +*/ +static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, + StkId ra) { + int nup = p->sizeupvalues; + Upvaldesc *uv = p->upvalues; + int i; + LClosure *ncl = luaF_newLclosure(L, nup); + ncl->p = p; + setclLvalue2s(L, ra, ncl); /* anchor new closure in stack */ + for (i = 0; i < nup; i++) { /* fill in its upvalues */ + if (uv[i].instack) /* upvalue refers to local variable? */ + ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); + else /* get upvalue from enclosing function */ + ncl->upvals[i] = encup[uv[i].idx]; + luaC_objbarrier(L, ncl, ncl->upvals[i]); + } +} + + +/* +** finish execution of an opcode interrupted by a yield +*/ +void luaV_finishOp (lua_State *L) { + CallInfo *ci = L->ci; + StkId base = ci->func + 1; + Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ + OpCode op = GET_OPCODE(inst); + switch (op) { /* finish its execution */ + case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { + setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top); + break; + } + case OP_UNM: case OP_BNOT: case OP_LEN: + case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: + case OP_GETFIELD: case OP_SELF: { + setobjs2s(L, base + GETARG_A(inst), --L->top); + break; + } + case OP_LT: case OP_LE: + case OP_LTI: case OP_LEI: + case OP_GTI: case OP_GEI: + case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ + int res = !l_isfalse(s2v(L->top - 1)); + L->top--; +#if defined(LUA_COMPAT_LT_LE) + if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ + ci->callstatus ^= CIST_LEQ; /* clear mark */ + res = !res; /* negate result */ + } +#endif + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); + if (res != GETARG_k(inst)) /* condition failed? */ + ci->u.l.savedpc++; /* skip jump instruction */ + break; + } + case OP_CONCAT: { + StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */ + int a = GETARG_A(inst); /* first element to concatenate */ + int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ + setobjs2s(L, top - 2, top); /* put TM result in proper position */ + L->top = top - 1; /* top is one after last element (at top-2) */ + luaV_concat(L, total); /* concat them (may yield again) */ + break; + } + default: { + /* only these other opcodes can yield */ + lua_assert(op == OP_TFORCALL || op == OP_CALL || + op == OP_TAILCALL || op == OP_SETTABUP || op == OP_SETTABLE || + op == OP_SETI || op == OP_SETFIELD); + break; + } + } +} + + + + +/* +** {================================================================== +** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' +** =================================================================== +*/ + +#define l_addi(L,a,b) intop(+, a, b) +#define l_subi(L,a,b) intop(-, a, b) +#define l_muli(L,a,b) intop(*, a, b) +#define l_band(a,b) intop(&, a, b) +#define l_bor(a,b) intop(|, a, b) +#define l_bxor(a,b) intop(^, a, b) + +#define l_lti(a,b) (a < b) +#define l_lei(a,b) (a <= b) +#define l_gti(a,b) (a > b) +#define l_gei(a,b) (a >= b) + + +/* +** Arithmetic operations with immediate operands. 'iop' is the integer +** operation, 'fop' is the float operation. +*/ +#define op_arithI(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + int imm = GETARG_sC(i); \ + if (ttisinteger(v1)) { \ + lua_Integer iv1 = ivalue(v1); \ + pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \ + } \ + else if (ttisfloat(v1)) { \ + lua_Number nb = fltvalue(v1); \ + lua_Number fimm = cast_num(imm); \ + pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \ + }} + + +/* +** Auxiliary function for arithmetic operations over floats and others +** with two register operands. +*/ +#define op_arithf_aux(L,v1,v2,fop) { \ + lua_Number n1; lua_Number n2; \ + if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ + pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ + }} + + +/* +** Arithmetic operations over floats and others with register operands. +*/ +#define op_arithf(L,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with K operands for floats. +*/ +#define op_arithfK(L,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations over integers and floats. +*/ +#define op_arith_aux(L,v1,v2,iop,fop) { \ + if (ttisinteger(v1) && ttisinteger(v2)) { \ + lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ + pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ + } \ + else op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with register operands. +*/ +#define op_arith(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Arithmetic operations with K operands. +*/ +#define op_arithK(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Bitwise operations with constant operand. +*/ +#define op_bitwiseK(L,op) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); \ + lua_Integer i1; \ + lua_Integer i2 = ivalue(v2); \ + if (tointegerns(v1, &i1)) { \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Bitwise operations with register operands. +*/ +#define op_bitwise(L,op) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + lua_Integer i1; lua_Integer i2; \ + if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Order operations with register operands. 'opn' actually works +** for all numbers, but the fast track improves performance for +** integers. +*/ +#define op_order(L,opi,opn,other) { \ + int cond; \ + TValue *rb = vRB(i); \ + if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ + lua_Integer ia = ivalue(s2v(ra)); \ + lua_Integer ib = ivalue(rb); \ + cond = opi(ia, ib); \ + } \ + else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ + cond = opn(s2v(ra), rb); \ + else \ + Protect(cond = other(L, s2v(ra), rb)); \ + docondjump(); } + + +/* +** Order operations with immediate operand. (Immediate operand is +** always small enough to have an exact representation as a float.) +*/ +#define op_orderI(L,opi,opf,inv,tm) { \ + int cond; \ + int im = GETARG_sB(i); \ + if (ttisinteger(s2v(ra))) \ + cond = opi(ivalue(s2v(ra)), im); \ + else if (ttisfloat(s2v(ra))) { \ + lua_Number fa = fltvalue(s2v(ra)); \ + lua_Number fim = cast_num(im); \ + cond = opf(fa, fim); \ + } \ + else { \ + int isf = GETARG_C(i); \ + Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ + } \ + docondjump(); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Function 'luaV_execute': main interpreter loop +** =================================================================== +*/ + +/* +** some macros for common tasks in 'luaV_execute' +*/ + + +#define RA(i) (base+GETARG_A(i)) +#define RB(i) (base+GETARG_B(i)) +#define vRB(i) s2v(RB(i)) +#define KB(i) (k+GETARG_B(i)) +#define RC(i) (base+GETARG_C(i)) +#define vRC(i) s2v(RC(i)) +#define KC(i) (k+GETARG_C(i)) +#define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i))) + + + +#define updatetrap(ci) (trap = ci->u.l.trap) + +#define updatebase(ci) (base = ci->func + 1) + + +#define updatestack(ci) { if (trap) { updatebase(ci); ra = RA(i); } } + + +/* +** Execute a jump instruction. The 'updatetrap' allows signals to stop +** tight loops. (Without it, the local copy of 'trap' could never change.) +*/ +#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); } + + +/* for test instructions, execute the jump instruction that follows it */ +#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); } + +/* +** do a conditional jump: skip next instruction if 'cond' is not what +** was expected (parameter 'k'), else do next instruction, which must +** be a jump. +*/ +#define docondjump() if (cond != GETARG_k(i)) pc++; else donextjump(ci); + + +/* +** Correct global 'pc'. +*/ +#define savepc(L) (ci->u.l.savedpc = pc) + + +/* +** Whenever code can raise errors, the global 'pc' and the global +** 'top' must be correct to report occasional errors. +*/ +#define savestate(L,ci) (savepc(L), L->top = ci->top) + + +/* +** Protect code that, in general, can raise errors, reallocate the +** stack, and change the hooks. +*/ +#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) + +/* special version that does not change the top */ +#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) + +/* +** Protect code that will finish the loop (returns) or can only raise +** errors. (That is, it will not return to the interpreter main loop +** after changing the stack or hooks.) +*/ +#define halfProtect(exp) (savestate(L,ci), (exp)) + +/* idem, but without changing the stack */ +#define halfProtectNT(exp) (savepc(L), (exp)) + +/* 'c' is the limit of live values in the stack */ +#define checkGC(L,c) \ + { luaC_condGC(L, (savepc(L), L->top = (c)), \ + updatetrap(ci)); \ + luai_threadyield(L); } + + +/* fetch an instruction and prepare its execution */ +#define vmfetch() { \ + if (trap) { /* stack reallocation or hooks? */ \ + trap = luaG_traceexec(L, pc); /* handle hooks */ \ + updatebase(ci); /* correct stack */ \ + } \ + i = *(pc++); \ + ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \ +} + +#define vmdispatch(o) switch(o) +#define vmcase(l) case l: +#define vmbreak break + + +void luaV_execute (lua_State *L, CallInfo *ci) { + LClosure *cl; + TValue *k; + StkId base; + const Instruction *pc; + int trap; +#if LUA_USE_JUMPTABLE +#include "ljumptab.h" +#endif + tailcall: + trap = L->hookmask; + cl = clLvalue(s2v(ci->func)); + k = cl->p->k; + pc = ci->u.l.savedpc; + if (trap) { + if (cl->p->is_vararg) + trap = 0; /* hooks will start after VARARGPREP instruction */ + else if (pc == cl->p->code) /* first instruction (not resuming)? */ + luaD_hookcall(L, ci); + ci->u.l.trap = 1; /* there may be other hooks */ + } + base = ci->func + 1; + /* main loop of interpreter */ + for (;;) { + Instruction i; /* instruction being executed */ + StkId ra; /* instruction's A register */ + vmfetch(); + lua_assert(base == ci->func + 1); + lua_assert(base <= L->top && L->top < L->stack + L->stacksize); + /* invalidate top for instructions not expecting it */ + lua_assert(isIT(i) || (cast_void(L->top = base), 1)); + vmdispatch (GET_OPCODE(i)) { + vmcase(OP_MOVE) { + setobjs2s(L, ra, RB(i)); + vmbreak; + } + vmcase(OP_LOADI) { + lua_Integer b = GETARG_sBx(i); + setivalue(s2v(ra), b); + vmbreak; + } + vmcase(OP_LOADF) { + int b = GETARG_sBx(i); + setfltvalue(s2v(ra), cast_num(b)); + vmbreak; + } + vmcase(OP_LOADK) { + TValue *rb = k + GETARG_Bx(i); + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADKX) { + TValue *rb; + rb = k + GETARG_Ax(*pc); pc++; + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADFALSE) { + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LFALSESKIP) { + setbfvalue(s2v(ra)); + pc++; /* skip next instruction */ + vmbreak; + } + vmcase(OP_LOADTRUE) { + setbtvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LOADNIL) { + int b = GETARG_B(i); + do { + setnilvalue(s2v(ra++)); + } while (b--); + vmbreak; + } + vmcase(OP_GETUPVAL) { + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v); + vmbreak; + } + vmcase(OP_SETUPVAL) { + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v, s2v(ra)); + luaC_barrier(L, uv, s2v(ra)); + vmbreak; + } + vmcase(OP_GETTABUP) { + const TValue *slot; + TValue *upval = cl->upvals[GETARG_B(i)]->v; + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a string */ + if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, upval, rc, ra, slot)); + vmbreak; + } + vmcase(OP_GETTABLE) { + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = vRC(i); + lua_Unsigned n; + if (ttisinteger(rc) /* fast track for integers? */ + ? (cast_void(n = ivalue(rc)), luaV_fastgeti(L, rb, n, slot)) + : luaV_fastget(L, rb, rc, slot, luaH_get)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_GETI) { + const TValue *slot; + TValue *rb = vRB(i); + int c = GETARG_C(i); + if (luaV_fastgeti(L, rb, c, slot)) { + setobj2s(L, ra, slot); + } + else { + TValue key; + setivalue(&key, c); + Protect(luaV_finishget(L, rb, &key, ra, slot)); + } + vmbreak; + } + vmcase(OP_GETFIELD) { + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a string */ + if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_SETTABUP) { + const TValue *slot; + TValue *upval = cl->upvals[GETARG_A(i)]->v; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a string */ + if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { + luaV_finishfastset(L, upval, slot, rc); + } + else + Protect(luaV_finishset(L, upval, rb, rc, slot)); + vmbreak; + } + vmcase(OP_SETTABLE) { + const TValue *slot; + TValue *rb = vRB(i); /* key (table is in 'ra') */ + TValue *rc = RKC(i); /* value */ + lua_Unsigned n; + if (ttisinteger(rb) /* fast track for integers? */ + ? (cast_void(n = ivalue(rb)), luaV_fastgeti(L, s2v(ra), n, slot)) + : luaV_fastget(L, s2v(ra), rb, slot, luaH_get)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } + else + Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + vmbreak; + } + vmcase(OP_SETI) { + const TValue *slot; + int c = GETARG_B(i); + TValue *rc = RKC(i); + if (luaV_fastgeti(L, s2v(ra), c, slot)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } + else { + TValue key; + setivalue(&key, c); + Protect(luaV_finishset(L, s2v(ra), &key, rc, slot)); + } + vmbreak; + } + vmcase(OP_SETFIELD) { + const TValue *slot; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a string */ + if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } + else + Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + vmbreak; + } + vmcase(OP_NEWTABLE) { + int b = GETARG_B(i); /* log2(hash size) + 1 */ + int c = GETARG_C(i); /* array size */ + Table *t; + if (b > 0) + b = 1 << (b - 1); /* size is 2^(b - 1) */ + lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0)); + if (TESTARG_k(i)) /* non-zero extra argument? */ + c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ + pc++; /* skip extra argument */ + L->top = ra + 1; /* correct top in case of emergency GC */ + t = luaH_new(L); /* memory allocation */ + sethvalue2s(L, ra, t); + if (b != 0 || c != 0) + luaH_resize(L, t, c, b); /* idem */ + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_SELF) { + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rc); /* key must be a string */ + setobj2s(L, ra + 1, rb); + if (luaV_fastget(L, rb, key, slot, luaH_getstr)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_ADDI) { + op_arithI(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_ADDK) { + op_arithK(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUBK) { + op_arithK(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MULK) { + op_arithK(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MODK) { + op_arithK(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POWK) { + op_arithfK(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIVK) { + op_arithfK(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIVK) { + op_arithK(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BANDK) { + op_bitwiseK(L, l_band); + vmbreak; + } + vmcase(OP_BORK) { + op_bitwiseK(L, l_bor); + vmbreak; + } + vmcase(OP_BXORK) { + op_bitwiseK(L, l_bxor); + vmbreak; + } + vmcase(OP_SHRI) { + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); + } + vmbreak; + } + vmcase(OP_SHLI) { + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); + } + vmbreak; + } + vmcase(OP_ADD) { + op_arith(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUB) { + op_arith(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MUL) { + op_arith(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MOD) { + op_arith(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POW) { + op_arithf(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIV) { /* float division (always with floats) */ + op_arithf(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIV) { /* floor division */ + op_arith(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BAND) { + op_bitwise(L, l_band); + vmbreak; + } + vmcase(OP_BOR) { + op_bitwise(L, l_bor); + vmbreak; + } + vmcase(OP_BXOR) { + op_bitwise(L, l_bxor); + vmbreak; + } + vmcase(OP_SHR) { + op_bitwise(L, luaV_shiftr); + vmbreak; + } + vmcase(OP_SHL) { + op_bitwise(L, luaV_shiftl); + vmbreak; + } + vmcase(OP_MMBIN) { + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *rb = vRB(i); + TMS tm = (TMS)GETARG_C(i); + StkId result = RA(pi); + lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR); + Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm)); + vmbreak; + } + vmcase(OP_MMBINI) { + Instruction pi = *(pc - 2); /* original arith. expression */ + int imm = GETARG_sB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_MMBINK) { + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *imm = KB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_UNM) { + TValue *rb = vRB(i); + lua_Number nb; + if (ttisinteger(rb)) { + lua_Integer ib = ivalue(rb); + setivalue(s2v(ra), intop(-, 0, ib)); + } + else if (tonumberns(rb, nb)) { + setfltvalue(s2v(ra), luai_numunm(L, nb)); + } + else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); + vmbreak; + } + vmcase(OP_BNOT) { + TValue *rb = vRB(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib)); + } + else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT)); + vmbreak; + } + vmcase(OP_NOT) { + TValue *rb = vRB(i); + if (l_isfalse(rb)) + setbtvalue(s2v(ra)); + else + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LEN) { + Protect(luaV_objlen(L, ra, vRB(i))); + vmbreak; + } + vmcase(OP_CONCAT) { + int n = GETARG_B(i); /* number of elements to concatenate */ + L->top = ra + n; /* mark the end of concat operands */ + ProtectNT(luaV_concat(L, n)); + checkGC(L, L->top); /* 'luaV_concat' ensures correct top */ + vmbreak; + } + vmcase(OP_CLOSE) { + Protect(luaF_close(L, ra, LUA_OK)); + vmbreak; + } + vmcase(OP_TBC) { + /* create new to-be-closed upvalue */ + halfProtect(luaF_newtbcupval(L, ra)); + vmbreak; + } + vmcase(OP_JMP) { + dojump(ci, i, 0); + vmbreak; + } + vmcase(OP_EQ) { + int cond; + TValue *rb = vRB(i); + Protect(cond = luaV_equalobj(L, s2v(ra), rb)); + docondjump(); + vmbreak; + } + vmcase(OP_LT) { + op_order(L, l_lti, LTnum, lessthanothers); + vmbreak; + } + vmcase(OP_LE) { + op_order(L, l_lei, LEnum, lessequalothers); + vmbreak; + } + vmcase(OP_EQK) { + TValue *rb = KB(i); + /* basic types do not use '__eq'; we can use raw equality */ + int cond = luaV_rawequalobj(s2v(ra), rb); + docondjump(); + vmbreak; + } + vmcase(OP_EQI) { + int cond; + int im = GETARG_sB(i); + if (ttisinteger(s2v(ra))) + cond = (ivalue(s2v(ra)) == im); + else if (ttisfloat(s2v(ra))) + cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im)); + else + cond = 0; /* other types cannot be equal to a number */ + docondjump(); + vmbreak; + } + vmcase(OP_LTI) { + op_orderI(L, l_lti, luai_numlt, 0, TM_LT); + vmbreak; + } + vmcase(OP_LEI) { + op_orderI(L, l_lei, luai_numle, 0, TM_LE); + vmbreak; + } + vmcase(OP_GTI) { + op_orderI(L, l_gti, luai_numgt, 1, TM_LT); + vmbreak; + } + vmcase(OP_GEI) { + op_orderI(L, l_gei, luai_numge, 1, TM_LE); + vmbreak; + } + vmcase(OP_TEST) { + int cond = !l_isfalse(s2v(ra)); + docondjump(); + vmbreak; + } + vmcase(OP_TESTSET) { + TValue *rb = vRB(i); + if (l_isfalse(rb) == GETARG_k(i)) + pc++; + else { + setobj2s(L, ra, rb); + donextjump(ci); + } + vmbreak; + } + vmcase(OP_CALL) { + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) /* fixed number of arguments? */ + L->top = ra + b; /* top signals number of arguments */ + /* else previous instruction set top */ + ProtectNT(luaD_call(L, ra, nresults)); + vmbreak; + } + vmcase(OP_TAILCALL) { + int b = GETARG_B(i); /* number of arguments + 1 (function) */ + int nparams1 = GETARG_C(i); + /* delat is virtual 'func' - real 'func' (vararg functions) */ + int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; + if (b != 0) + L->top = ra + b; + else /* previous instruction set top */ + b = cast_int(L->top - ra); + savepc(ci); /* some calls here can raise errors */ + if (TESTARG_k(i)) { + /* close upvalues from current call; the compiler ensures + that there are no to-be-closed variables here, so this + call cannot change the stack */ + luaF_close(L, base, NOCLOSINGMETH); + lua_assert(base == ci->func + 1); + } + while (!ttisfunction(s2v(ra))) { /* not a function? */ + luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ + b++; /* there is now one extra argument */ + checkstackGCp(L, 1, ra); + } + if (!ttisLclosure(s2v(ra))) { /* C function? */ + luaD_call(L, ra, LUA_MULTRET); /* call it */ + updatetrap(ci); + updatestack(ci); /* stack may have been relocated */ + ci->func -= delta; + luaD_poscall(L, ci, cast_int(L->top - ra)); + return; + } + ci->func -= delta; + luaD_pretailcall(L, ci, ra, b); /* prepare call frame */ + goto tailcall; + } + vmcase(OP_RETURN) { + int n = GETARG_B(i) - 1; /* number of results */ + int nparams1 = GETARG_C(i); + if (n < 0) /* not fixed? */ + n = cast_int(L->top - ra); /* get what is available */ + savepc(ci); + if (TESTARG_k(i)) { /* may there be open upvalues? */ + if (L->top < ci->top) + L->top = ci->top; + luaF_close(L, base, LUA_OK); + updatetrap(ci); + updatestack(ci); + } + if (nparams1) /* vararg function? */ + ci->func -= ci->u.l.nextraargs + nparams1; + L->top = ra + n; /* set call for 'luaD_poscall' */ + luaD_poscall(L, ci, n); + return; + } + vmcase(OP_RETURN0) { + if (L->hookmask) { + L->top = ra; + halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */ + } + else { /* do the 'poscall' here */ + int nres = ci->nresults; + L->ci = ci->previous; /* back to caller */ + L->top = base - 1; + while (nres-- > 0) + setnilvalue(s2v(L->top++)); /* all results are nil */ + } + return; + } + vmcase(OP_RETURN1) { + if (L->hookmask) { + L->top = ra + 1; + halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */ + } + else { /* do the 'poscall' here */ + int nres = ci->nresults; + L->ci = ci->previous; /* back to caller */ + if (nres == 0) + L->top = base - 1; /* asked for no results */ + else { + setobjs2s(L, base - 1, ra); /* at least this result */ + L->top = base; + while (--nres > 0) /* complete missing results */ + setnilvalue(s2v(L->top++)); + } + } + return; + } + vmcase(OP_FORLOOP) { + if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ + lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); + if (count > 0) { /* still more iterations? */ + lua_Integer step = ivalue(s2v(ra + 2)); + lua_Integer idx = ivalue(s2v(ra)); /* internal index */ + chgivalue(s2v(ra + 1), count - 1); /* update counter */ + idx = intop(+, idx, step); /* add step to index */ + chgivalue(s2v(ra), idx); /* update internal index */ + setivalue(s2v(ra + 3), idx); /* and control variable */ + pc -= GETARG_Bx(i); /* jump back */ + } + } + else if (floatforloop(ra)) /* float loop */ + pc -= GETARG_Bx(i); /* jump back */ + updatetrap(ci); /* allows a signal to break the loop */ + vmbreak; + } + vmcase(OP_FORPREP) { + savestate(L, ci); /* in case of errors */ + if (forprep(L, ra)) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + vmbreak; + } + vmcase(OP_TFORPREP) { + /* create to-be-closed upvalue (if needed) */ + halfProtect(luaF_newtbcupval(L, ra + 3)); + pc += GETARG_Bx(i); + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i)); + goto l_tforcall; + } + vmcase(OP_TFORCALL) { + l_tforcall: + /* 'ra' has the iterator function, 'ra + 1' has the state, + 'ra + 2' has the control variable, and 'ra + 3' has the + to-be-closed variable. The call will use the stack after + these values (starting at 'ra + 4') + */ + /* push function, state, and control variable */ + memcpy(ra + 4, ra, 3 * sizeof(*ra)); + L->top = ra + 4 + 3; + ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ + updatestack(ci); /* stack may have changed */ + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); + goto l_tforloop; + } + vmcase(OP_TFORLOOP) { + l_tforloop: + if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ + setobjs2s(L, ra + 2, ra + 4); /* save control variable */ + pc -= GETARG_Bx(i); /* jump back */ + } + vmbreak; + } + vmcase(OP_SETLIST) { + int n = GETARG_B(i); + unsigned int last = GETARG_C(i); + Table *h = hvalue(s2v(ra)); + if (n == 0) + n = cast_int(L->top - ra) - 1; /* get up to the top */ + else + L->top = ci->top; /* correct top in case of emergency GC */ + last += n; + if (TESTARG_k(i)) { + last += GETARG_Ax(*pc) * (MAXARG_C + 1); + pc++; + } + if (last > luaH_realasize(h)) /* needs more space? */ + luaH_resizearray(L, h, last); /* preallocate it at once */ + for (; n > 0; n--) { + TValue *val = s2v(ra + n); + setobj2t(L, &h->array[last - 1], val); + last--; + luaC_barrierback(L, obj2gco(h), val); + } + vmbreak; + } + vmcase(OP_CLOSURE) { + Proto *p = cl->p->p[GETARG_Bx(i)]; + halfProtect(pushclosure(L, p, cl->upvals, base, ra)); + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_VARARG) { + int n = GETARG_C(i) - 1; /* required results */ + Protect(luaT_getvarargs(L, ci, ra, n)); + vmbreak; + } + vmcase(OP_VARARGPREP) { + ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); + if (trap) { + luaD_hookcall(L, ci); + L->oldpc = 1; /* next opcode will be seen as a "new" line */ + } + updatebase(ci); /* function has new base after adjustment */ + vmbreak; + } + vmcase(OP_EXTRAARG) { + lua_assert(0); + vmbreak; + } + } + } +} + +/* }================================================================== */ diff --git a/Lua/lvm.h b/Lua/lvm.h new file mode 100644 index 00000000..2d4ac160 --- /dev/null +++ b/Lua/lvm.h @@ -0,0 +1,134 @@ +/* +** $Id: lvm.h $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#if !defined(LUA_NOCVTN2S) +#define cvt2str(o) ttisnumber(o) +#else +#define cvt2str(o) 0 /* no conversion from numbers to strings */ +#endif + + +#if !defined(LUA_NOCVTS2N) +#define cvt2num(o) ttisstring(o) +#else +#define cvt2num(o) 0 /* no conversion from strings to numbers */ +#endif + + +/* +** You can define LUA_FLOORN2I if you want to convert floats to integers +** by flooring them (instead of raising an error if they are not +** integral values) +*/ +#if !defined(LUA_FLOORN2I) +#define LUA_FLOORN2I F2Ieq +#endif + + +/* +** Rounding modes for float->integer coercion + */ +typedef enum { + F2Ieq, /* no rounding; accepts only integral values */ + F2Ifloor, /* takes the floor of the number */ + F2Iceil /* takes the ceil of the number */ +} F2Imod; + + +/* convert an object to a float (including string coercion) */ +#define tonumber(o,n) \ + (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) + + +/* convert an object to a float (without string coercion) */ +#define tonumberns(o,n) \ + (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \ + (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0)) + + +/* convert an object to an integer (including string coercion) */ +#define tointeger(o,i) \ + (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) + + +/* convert an object to an integer (without string coercion) */ +#define tointegerns(o,i) \ + (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o,i,LUA_FLOORN2I)) + + +#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) + +#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) + + +/* +** fast track for 'gettable': if 't' is a table and 't[k]' is present, +** return 1 with 'slot' pointing to 't[k]' (position of final result). +** Otherwise, return 0 (meaning it will have to check metamethod) +** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL +** (otherwise). 'f' is the raw get function to use. +*/ +#define luaV_fastget(L,t,k,slot,f) \ + (!ttistable(t) \ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = f(hvalue(t), k), /* else, do raw access */ \ + !isempty(slot))) /* result not empty? */ + + +/* +** Special case of 'luaV_fastget' for integers, inlining the fast case +** of 'luaH_getint'. +*/ +#define luaV_fastgeti(L,t,k,slot) \ + (!ttistable(t) \ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \ + ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ + !isempty(slot))) /* result not empty? */ + + +/* +** Finish a fast set operation (when fast get succeeds). In that case, +** 'slot' points to the place to put the value. +*/ +#define luaV_finishfastset(L,t,slot,v) \ + { setobj2t(L, cast(TValue *,slot), v); \ + luaC_barrierback(L, gcvalue(t), v); } + + + + +LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); +LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode); +LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p, + F2Imod mode); +LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode); +LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *slot); +LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + TValue *val, const TValue *slot); +LUAI_FUNC void luaV_finishOp (lua_State *L); +LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci); +LUAI_FUNC void luaV_concat (lua_State *L, int total); +LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y); +LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); +LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); + +#endif diff --git a/Lua/lzio.c b/Lua/lzio.c new file mode 100644 index 00000000..cd0a02d5 --- /dev/null +++ b/Lua/lzio.c @@ -0,0 +1,68 @@ +/* +** $Id: lzio.c $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + +#define lzio_c +#define LUA_CORE + +#include "lprefix.h" + + +#include + +#include "lua.h" + +#include "llimits.h" +#include "lmem.h" +#include "lstate.h" +#include "lzio.h" + + +int luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) + return EOZ; + z->n = size - 1; /* discount char being returned */ + z->p = buff; + return cast_uchar(*(z->p++)); +} + + +void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ +size_t luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (z->n == 0) { /* no bytes in buffer? */ + if (luaZ_fill(z) == EOZ) /* try to read more */ + return n; /* no more input; return number of missing bytes */ + else { + z->n++; /* luaZ_fill consumed first byte; put it back */ + z->p--; + } + } + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + memcpy(b, z->p, m); + z->n -= m; + z->p += m; + b = (char *)b + m; + n -= m; + } + return 0; +} + diff --git a/Lua/lzio.h b/Lua/lzio.h new file mode 100644 index 00000000..38f397fd --- /dev/null +++ b/Lua/lzio.h @@ -0,0 +1,66 @@ +/* +** $Id: lzio.h $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +#include "lua.h" + +#include "lmem.h" + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) + + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_buffremove(buff,i) ((buff)->n -= (i)) +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ + (buff)->buffsize, size), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + + +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ + + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; /* reader function */ + void *data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif diff --git a/QQAPI/DDAPI.cpp b/QQAPI/DDAPI.cpp new file mode 100644 index 00000000..40983e2b --- /dev/null +++ b/QQAPI/DDAPI.cpp @@ -0,0 +1,161 @@ +#include "DDAPI.h" + +using std::string; +using std::unordered_map; +using namespace DD; + +#define DDAPI(Name, ReturnType, ...) using Name##_TYPE = ReturnType (*)(__VA_ARGS__) + +DDAPI(_DriverVer, const char*); +DDAPI(Reload, bool); +DDAPI(Remake, bool); +DDAPI(Killme, void); +DDAPI(DebugLog, void, const std::string&); +DDAPI(IsDiceMaid, bool, long long); +DDAPI(GetDiceSisters, const std::set&); +DDAPI(DiceHeartbeat, void, long long, const std::string&); +DDAPI(DiceUpdate, bool, const std::string&, std::string&); +DDAPI(GetNick, const std::string&, long long, long long); +DDAPI(SendPrivateMsg, void, long long, long long, const std::string&); +DDAPI(SendGroupMsg, void, long long, long long, const std::string&); +DDAPI(SendDiscussMsg, void, long long, long long, const std::string&); +DDAPI(GetFriendQQList, const std::set&, long long); +DDAPI(GetGroupIDList, const std::set&, long long); +DDAPI(GetGroupMemberList, const std::set&, long long, long long); +DDAPI(GetGroupAdminList, const std::set&, long long, long long); +DDAPI(GetGroupAuth, int, long long, long long, long long); +DDAPI(IsGroupAdmin, bool, long long, long long, long long, bool); +DDAPI(IsGroupOwner, bool, long long, long long, long long, bool); +DDAPI(IsGroupMember, bool, long long, long long, long long, bool); +DDAPI(AnswerFriendRequest, void, long long, long long, int, const std::string&); +DDAPI(AnswerGroupInvited, void, long long, long long, int); +DDAPI(GetGroupSize, const Size&, long long, long long); +DDAPI(GetGroupName, const std::string&, long long, long long); +DDAPI(GetGroupNick, const std::string&, long long, long long, long long); +DDAPI(GetGroupLastMsg, long long, long long, long long, long long); +DDAPI(PrintGroupInfo, const std::string&, long long, long long); +DDAPI(SetGroupKick, void, long long, long long, long long); +DDAPI(SetGroupBan, void, long long, long long, long long, int); +DDAPI(SetGroupAdmin, void, long long, long long, long long, bool); +DDAPI(SetGroupCard, void, long long, long long, long long, const string&); +DDAPI(SetGroupTitle, void, long long, long long, long long, const string&); +DDAPI(SetGroupWholeBan, void, long long, long long, int); +DDAPI(SetGroupLeave, void, long long, long long); +DDAPI(SetDiscussLeave, void, long long, long long); + +#define CALLVOID(Name, ...) if(ApiList.count(#Name))reinterpret_cast(ApiList[#Name])(__VA_ARGS__) +#define CALLGET(Name, ...) ApiList.count(#Name) ? reinterpret_cast(ApiList[#Name])(__VA_ARGS__) + +namespace DD { + const std::string& getDriVer() { + static string ver{ CALLGET(_DriverVer) :"" }; + return ver; + } + bool reload() { + return CALLGET(Reload) :false; + } + bool remake() { + return CALLGET(Remake) :false; + } + void killme() { + CALLVOID(Killme); + } + void debugLog(const std::string& log) { + CALLVOID(DebugLog, log); + } + bool isDiceMaid(long long aimQQ){ + return CALLGET(IsDiceMaid, aimQQ) :false; + } + std::set getDiceSisters() { + return CALLGET(GetDiceSisters) :std::set(); + } + void heartbeat(const string& info) { + CALLVOID(DiceHeartbeat, loginQQ, info); + } + bool updateDice(const std::string& ver, std::string& ret) { + ret = "½ӿڲ"; + return CALLGET(DiceUpdate, ver, ret) :false; + } + std::string getQQNick(long long aimQQ) { + return CALLGET(GetNick, loginQQ, aimQQ) :""; + } + void sendPrivateMsg(long long rcvQQ, const std::string& msg) { + CALLVOID(SendPrivateMsg, loginQQ, rcvQQ, msg); + } + void sendGroupMsg(long long rcvChat, const std::string& msg) { + CALLVOID(SendGroupMsg, loginQQ, rcvChat, msg); + } + void sendDiscussMsg(long long rcvChat, const std::string& msg) { + CALLVOID(SendDiscussMsg, loginQQ, rcvChat, msg); + } + std::set getFriendQQList() { + return CALLGET(GetFriendQQList, loginQQ) :std::set(); + } + std::set getGroupIDList() { + return CALLGET(GetGroupIDList, loginQQ) :std::set(); + } + std::set getGroupMemberList(long long aimGroup) { + return CALLGET(GetGroupMemberList, loginQQ, aimGroup) :std::set(); + } + std::set getGroupAdminList(long long aimGroup) { + return CALLGET(GetGroupAdminList, loginQQ, aimGroup) :std::set(); + } + int getGroupAuth(long long llgroup, long long llQQ, int iDefault) { + int auth{ CALLGET(GetGroupAuth, loginQQ, llgroup, llQQ) : iDefault }; + return auth < 0 ? iDefault : auth; + } + bool isGroupAdmin(long long llgroup, long long llQQ, bool bDefault) { + return CALLGET(IsGroupAdmin, loginQQ, llgroup, llQQ, bDefault) :bDefault; + } + bool isGroupOwner(long long llgroup, long long llQQ, bool bDefault) { + return CALLGET(IsGroupOwner, loginQQ, llgroup, llQQ, bDefault) :bDefault; + } + bool isGroupMember(long long llgroup, long long llQQ, bool bDefault) { + return CALLGET(IsGroupMember, loginQQ, llgroup, llQQ, bDefault) :bDefault; + } + void answerFriendRequest(long long fromQQ, int respon, const std::string& msg) { + CALLVOID(AnswerFriendRequest, loginQQ, fromQQ, respon, msg); + } + void answerGroupInvited(long long fromGroup, int respon) { + CALLVOID(AnswerGroupInvited, loginQQ, fromGroup, respon); + } + Size getGroupSize(long long aimGroup) { + return CALLGET(GetGroupSize, loginQQ, aimGroup) :Size(); + } + std::string getGroupName(long long aimGroup) { + return CALLGET(GetGroupName, loginQQ, aimGroup) :""; + } + std::string getGroupNick(long long aimGroup, long long aimQQ) { + return CALLGET(GetGroupNick, loginQQ, aimGroup, aimQQ) :""; + } + long long getGroupLastMsg(long long aimGroup, long long aimQQ) { + return CALLGET(GetGroupLastMsg, loginQQ, aimGroup, aimQQ) :-1; + } + std::string printGroupInfo(long long aimGroup) { + return CALLGET(PrintGroupInfo, loginQQ, aimGroup) :"[" + getGroupName(aimGroup) + "](" + std::to_string(aimGroup) + ")[" + getGroupSize(aimGroup).tostring() + "]"; + } + void setGroupKick(long long llGroup, long long llQQ) { + CALLVOID(SetGroupKick, loginQQ, llGroup, llQQ); + } + void setGroupBan(long long llGroup, long long llQQ, int intTime) { + CALLVOID(SetGroupBan, loginQQ, llGroup, llQQ, intTime); + } + void setGroupAdmin(long long llGroup, long long llQQ, bool bSet) { + CALLVOID(SetGroupAdmin, loginQQ, llGroup, llQQ, bSet); + } + void setGroupCard(long long llGroup, long long llQQ, const string& card) { + CALLVOID(SetGroupCard, loginQQ, llGroup, llQQ, card); + } + void setGroupTitle(long long llGroup, long long llQQ, const string& card){ + CALLVOID(SetGroupTitle, loginQQ, llGroup, llQQ, card); + } + void setGroupWholeBan(long long llGroup, int intTime){ + CALLVOID(SetGroupWholeBan, loginQQ, llGroup, intTime); + } + void setGroupLeave(long long llGroup){ + CALLVOID(SetGroupLeave, loginQQ, llGroup); + } + void setDiscussLeave(long long llGroup){ + CALLVOID(SetDiscussLeave, loginQQ, llGroup); + } +} diff --git a/QQAPI/DDAPI.h b/QQAPI/DDAPI.h new file mode 100644 index 00000000..96876223 --- /dev/null +++ b/QQAPI/DDAPI.h @@ -0,0 +1,59 @@ +#pragma once +#include +#include +#include + +inline long long loginQQ{ 0 }; + +struct Size { + unsigned int siz{ 0 }; + unsigned int cap{ 0 }; + std::string tostring() { + return std::to_string(siz) + "/" + std::to_string(cap); + } +}; + +using api_list = std::unordered_map; +//DiceDriverӿ +namespace DD { + const std::string& getDriVer(); + bool reload(); + bool remake(); + void killme(); + bool updateDice(const std::string&, std::string&); + inline api_list ApiList; + inline long long getLoginQQ() { return loginQQ; } + std::string getQQNick(long long); + inline std::string getLoginNick() { return getQQNick(loginQQ); } + void debugLog(const std::string&); + bool isDiceMaid(long long); + std::set getDiceSisters(); + void heartbeat(const std::string&); + void sendPrivateMsg(long long, const std::string& msg); + void sendGroupMsg(long long, const std::string& msg); + void sendDiscussMsg(long long, const std::string& msg); + std::set getFriendQQList(); + std::set getGroupIDList(); + std::set getGroupMemberList(long long); + std::set getGroupAdminList(long long); + int getGroupAuth(long long llgroup, long long llQQ, int iDefault); + bool isGroupAdmin(long long, long long, bool); + bool isGroupOwner(long long, long long, bool); + bool isGroupMember(long long, long long, bool); + void answerFriendRequest(long long fromQQ, int respon, const std::string& msg = {}); + void answerGroupInvited(long long fromGroup, int respon); + Size getGroupSize(long long); + std::string getGroupName(long long); + std::string getGroupNick(long long, long long); + long long getGroupLastMsg(long long, long long); + std::string printGroupInfo(long long); + void setGroupKick(long long, long long); + void setGroupBan(long long, long long, int); + void setGroupAdmin(long long, long long, bool = true); + void setGroupCard(long long, long long, const std::string&); + void setGroupTitle(long long, long long, const std::string&); + void setGroupWholeBan(long long, int); + void setGroupLeave(long long); + void setDiscussLeave(long long); +} +//enum class msgtype : int { Private = 0, Group = 1, Discuss = 2 }; \ No newline at end of file diff --git a/QQAPI/QQEvent.cpp b/QQAPI/QQEvent.cpp new file mode 100644 index 00000000..ec8344a2 --- /dev/null +++ b/QQAPI/QQEvent.cpp @@ -0,0 +1,10 @@ +#include "QQEvent.h" +#include "DDAPI.h" +using namespace QQ; + +EVE_Startup(eventStartUp) { + if (!botQQ)return; + loginQQ = botQQ; + DD::ApiList = reinterpret_cast(initApi)(); + DD::debugLog("Dice" + std::to_string(botQQ) + ".init"); +} \ No newline at end of file diff --git a/QQAPI/QQEvent.h b/QQAPI/QQEvent.h new file mode 100644 index 00000000..71f95248 --- /dev/null +++ b/QQAPI/QQEvent.h @@ -0,0 +1,35 @@ +#pragma once +namespace QQ { +#ifdef _MSC_VER +#define QQEVENT(ReturnType, Name, Size) __pragma(comment(linker, "/EXPORT:" #Name "=_" #Name "@" #Size))\ + extern "C" __declspec(dllexport) ReturnType __stdcall Name +#else +#define QQEVENT(ReturnType, Name, Size)\ + extern "C" __attribute__((dllexport)) ReturnType __attribute__((__stdcall__)) Name +#endif /*_MSC_VER*/ + +/* +ʼ¼apiȡQQ +*/ +#define EVE_Startup(Name) QQEVENT(void, Name, 12)(void* initApi, long long botQQ) +/* +Dice +*/ +#define EVE_Enable(Name) QQEVENT(void, Name, 0)() +#define EVE_Disable(Name) QQEVENT(void, Name, 0)() +#define EVE_Exit(Name) QQEVENT(void, Name, 0)() +#define EVE_PrivateMsg(Name) QQEVENT(int, Name, 16)(int msgId, long long fromQQ, const char* message) +#define EVE_GroupMsg(Name) QQEVENT(int, Name, 24)(int msgId, long long fromGroup, long long fromQQ, const char* message) +#define EVE_DiscussMsg(Name) QQEVENT(int, Name, 24)(int msgId, long long fromDiscuss, long long fromQQ, const char* message) +#define EVE_GroupMemberKicked(Name) QQEVENT(int, Name, 24)(long long fromGroup, long long beingOperateQQ, long long fromQQ) +#define EVE_GroupMemberIncrease(Name) QQEVENT(int, Name, 24)(long long fromGroup, long long fromQQ, long long operatorQQ) +#define EVE_GroupBan(Name) QQEVENT(int, Name, 28)(long long fromGroup, long long beingOperateQQ, long long operatorQQ, const char* duration) +#define EVE_GroupInvited(Name) QQEVENT(int, Name, 16)(long long fromGroup, long long fromQQ) +#define EVE_FriendRequest(Name) QQEVENT(int, Name, 12)(long long fromQQ, const char* message) +#define EVE_FriendAdded(Name) QQEVENT(int, Name, 8)(long long fromQQ) + +//˵ +#define EVE_Menu(Name) QQEVENT(int, Name, 0)() +//ʱ +//#define EVE_Status_EX(Name) QQEVENT(const char*, Name, 0)() +} \ No newline at end of file From 3166d8e01d2fb4285273fb537519c52022a3a7bb Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Tue, 5 Jan 2021 19:04:38 +0800 Subject: [PATCH 045/171] fix QQAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化接口调用 隐藏后台功能源码 --- Dice/BlackListManager.cpp | 19 ++- Dice/CardDeck.cpp | 14 ++- Dice/CharacterCard.cpp | 183 ++++++++++++++++++++++++--- Dice/CharacterCard.h | 252 ++++++++++++-------------------------- Dice/Dice.cpp | 18 ++- Dice/DiceConsole.cpp | 9 +- Dice/DiceEvent.cpp | 148 +++++++++++++--------- Dice/DiceEvent.h | 1 + Dice/DiceFile.hpp | 16 ++- Dice/DiceJob.cpp | 6 +- Dice/DiceLua.cpp | 33 ++++- Dice/DiceMod.cpp | 4 + Dice/DiceSession.cpp | 11 +- Dice/GlobalVar.cpp | 16 +-- Dice/GlobalVar.h | 4 +- Dice/ManagerSystem.cpp | 8 +- Dice/MsgFormat.cpp | 6 + Dice/MsgMonitor.cpp | 13 +- Dice/RDConstant.h | 2 + QQAPI/DDAPI.cpp | 9 ++ QQAPI/DDAPI.h | 1 + QQAPI/QQEvent.cpp | 1 - 22 files changed, 463 insertions(+), 311 deletions(-) diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 292e8af4..eda0bb06 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -402,17 +402,16 @@ void DDBlackMark::erase() void DDBlackMark::upload() { - std::string frmdata = "fromQQ=" + std::to_string(fromQQ.first) + "&fromGroup=" + std::to_string(fromGroup.first) + - "&DiceMaid=" + std::to_string(DiceMaid) + "&masterQQ=" + std::to_string(masterQQ) + "&type=" + type + "&time=" + - time + "¬e=" + UrlEncode(GBKtoUTF8(note)); - string temp; - if (!Network::POST("shiki.stringempty.xyz", "/DiceCloud/warning_upload.php", 80, frmdata.data(), temp)) { - console.log("ϴ¼ʧ:" + temp, 0b1000); + std::string info = "&masterQQ=" + std::to_string(masterQQ) + "&time=" + time + "¬e=" + UrlEncode(GBKtoUTF8(note)); + int res{ DD::uploadBlack(console.DiceMaid, fromQQ.first, fromGroup.first, type, info) }; + if (!res) { + erase(); + } + else if (res < 0) { + console.log("ϴ¼ʧ:" + info, 0b1); } - else if (isdigit(static_cast(temp[0])))wid = stoi(temp); - else if (temp == "denied")erase(); - else if (temp.find("error") == 0) { - console.log("ϴ¼ܾ:" + temp, 0b10); + else { + wid = res; } } diff --git a/Dice/CardDeck.cpp b/Dice/CardDeck.cpp index c9f8fe42..45221b05 100644 --- a/Dice/CardDeck.cpp +++ b/Dice/CardDeck.cpp @@ -1104,9 +1104,7 @@ namespace CardDeck sortedIndex.push_back(sortedIndex[sortedIndex.size() - 1] + cnt); } } - - - + std::string strReply; if (TempDeck.empty())return ""; if (TempDeck.size() == 1) @@ -1154,6 +1152,11 @@ namespace CardDeck int lq = 0, rq = 0; while ((lq = strExp.find('{', intCnt)) != std::string::npos && (rq = strExp.find('}', lq)) != std::string::npos) { + if (lq > 0 && strExp[lq - 1] == '\\') { + strExp.erase(strExp.begin() + lq - 1); + intCnt = rq; + continue; + } bool isTmpBack = false; string strTempName = strExp.substr(lq + 1, rq - lq - 1); if (strTempName[0] == '%') @@ -1177,6 +1180,11 @@ namespace CardDeck intCnt = 0; while ((lq = strExp.find('[', intCnt)) != std::string::npos && (rq = strExp.find(']', lq)) != std::string::npos) { + if (lq > 0 && strExp[lq - 1] == '\\') { + strExp.erase(strExp.begin() + lq - 1); + intCnt = rq; + continue; + } string strRoll = strExp.substr(lq + 1, rq - lq - 1); intCnt = rq + 1; RD RDroll(strRoll); diff --git a/Dice/CharacterCard.cpp b/Dice/CharacterCard.cpp index d9ab233d..d1fb148e 100644 --- a/Dice/CharacterCard.cpp +++ b/Dice/CharacterCard.cpp @@ -4,34 +4,177 @@ */ #include "CharacterCard.h" -map& getmCardTemplet() +string CardTemp::show() { + ResList res; + if (!mVariable.empty()) { + ResList resVar; + for (const auto& [key, val] : mVariable) { + resVar << key; + } + res << "̬:" + resVar.show(); + } + if (!mExpression.empty()) { + ResList resExp; + for (const auto& [key, val] : mExpression) { + resExp << key; + } + res << "ʽ:" + resExp.show(); + } + return "pcģ:" + type + res.show(); +} + +CardTemp& getCardTemplet(const string& type) { - static map mCardTemplet = { - { - "COC7", { - "COC7", SkillNameReplace, BasicCOC7, InfoCOC7, AutoFillCOC7, mVariableCOC7, ExpressionCOC7, - SkillDefaultVal, { - {"", CardBuild({BuildCOC7}, CardDeck::mPublicDeck[""], {})}, - { - "bg", CardBuild({ - {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, - {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, - {"֮", "{֮}"}, {"", "{Աص}"} - }, CardDeck::mPublicDeck[""], {}) - } - } - } - }, - {"BRP", {}} - }; - return mCardTemplet; + if (type.empty() || !mCardTemplet.count(type))return mCardTemplet["BRP"]; + return mCardTemplet[type]; +} + +void CharaCard::writeb(std::ofstream& fout) const { + fwrite(fout, string("Name")); + fwrite(fout, Name); + if (!Attr.empty()) { + fwrite(fout, string("Attr")); + fwrite(fout, Attr); + } + if (!Info.empty()) { + fwrite(fout, string("Info")); + fwrite(fout, Info); + } + if (!DiceExp.empty()) { + fwrite(fout, string("DiceExp")); + fwrite(fout, DiceExp); + } + if (!Note.empty()) { + fwrite(fout, string("Note")); + fwrite(fout, Note); + } + fwrite(fout, string("END")); +} +int CharaCard::setInfo(const string& key, const string& s) { + if (key.empty() || s.length() > 255)return -1; + Info[key] = s; + if (key == "__Type") + pTemplet = &getCardTemplet(s); + return 0; +} + +bool CharaCard::count(const string& strKey) const { + if (Attr.count(strKey))return true; + string key{ standard(strKey) }; + return Attr.count(key) || DiceExp.count(key) || pTemplet->mAutoFill.count(key) || pTemplet->mVariable.count(key) + || pTemplet->defaultSkill.count(key); +} +short& CharaCard::operator[](const string& strKey) { + if (Attr.count(strKey))return Attr[strKey]; + string key{ standard(strKey) }; + if (!Attr.count(key)) { + if (pTemplet->mAutoFill.count(key))Attr[key] = cal(pTemplet->mAutoFill.find(key)->second); + if (pTemplet->defaultSkill.count(key))Attr[key] = pTemplet->defaultSkill.find(key)->second; + } + return Attr[key]; } +void CharaCard::clear() { + Attr.clear(); + map info_new{ {"__Type",Info["__Type"]},{"__Name",Info["__Name"]} }; + Info.swap(info_new); + DiceExp.clear(); + Note.clear(); +} +[[nodiscard]] string CharaCard::show(bool isWhole) const { + std::set sDefault; + ResList Res; + for (const auto& list : pTemplet->vBasicList) { + ResList subList; + string strVal; + for (const auto& it : list) { + switch (show(it, strVal)) { + case 0: + sDefault.insert(it); + subList << it + ":" + strVal; + break; + case 1: + sDefault.insert(it); + subList << "&" + it + "=" + strVal; + break; + case 3: + sDefault.insert(it); + subList.dot("\t"); + subList << it + ":" + strVal; + break; + default: + continue; + } + } + Res << subList.show(); + } + string strAttrRest; + for (const auto& [key,val] : Attr) { + if (sDefault.count(key) || + (key[0] == '_' && (key.length() < 2 || key[1] != '_' || !isWhole)))continue; + strAttrRest += key + ":" + to_string(val) + " "; + } + Res << strAttrRest; + if (isWhole && !Info.empty()) + for (const auto& it : Info) { + if (sDefault.count(it.first) || it.first[0] == '_')continue; + Res << it.first + ":" + it.second; + } + if (isWhole && !DiceExp.empty()) + for (const auto& it : DiceExp) { + if (sDefault.count(it.first) || it.first[0] == '_')continue; + Res << "&" + it.first + "=" + it.second; + } + if (isWhole && !Note.empty())Res << "====================\n" + Note; + return Res.show(); +} +void CharaCard::readb(std::ifstream& fin) { + string tag = fread(fin); + while (tag != "END") { + switch (mCardTag[tag]) { + case 1: + Name = fread(fin); + break; + case 2: + Info["__Type"] = fread(fin); + break; + case 11: + fread(fin, Attr); + Attr.erase(""); + break; + case 21: + fread(fin, DiceExp); + DiceExp.erase(""); + break; + case 102: + fread(fin, Info); + Info.erase(""); + scanImage(Info, sReferencedImage); + break; + case 101: + Note = fread(fin); + scanImage(Note, sReferencedImage); + break; + default: + break; + } + tag = fread(fin); + } + pTemplet = &getCardTemplet(Info["__Type"]); +} Player& getPlayer(long long qq) { if (!PList.count(qq))PList[qq] = {}; return PList[qq]; } +string Player::listCard() { + ResList Res; + for (auto& [idx, pc] : mCardList) { + Res << "[" + to_string(idx) + "]<" + getCardTemplet(pc.Info["__Type"]).type + ">" + pc.Name; + } + Res << "default:" + (*this)[0].Name; + return Res.show(); +} void getPCName(FromMsg& msg) { diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 946ba667..07430694 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -137,9 +137,9 @@ class CardTemp { } - CardTemp(DDOM d) + CardTemp(const DDOM& d) { - readt(std::move(d)); + readt(d); } void readt(const DDOM& d) @@ -166,6 +166,7 @@ class CardTemp { sInfoList.insert(sub); } + break; case 22: readini(node.strValue, mAutoFill); break; @@ -201,30 +202,76 @@ class CardTemp if (strItem.empty())return type; return type + "[" + strItem + "]"; } + string show(); }; +inline map mCardTemplet = { + { + "COC7", { + "COC7", SkillNameReplace, BasicCOC7, InfoCOC7, AutoFillCOC7, mVariableCOC7, ExpressionCOC7, + SkillDefaultVal, { + {"_default", CardBuild({BuildCOC7}, CardDeck::mPublicDeck[""], {})}, + { + "bg", CardBuild({ + {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, + {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, + {"֮", "{֮}"}, {"", "{Աص}"} + }, CardDeck::mPublicDeck[""], {}) + } + } + } + }, + {"BRP", { + "BRP", {}, {}, {}, {}, {}, {}, { + {"__DefaultDice",100} + }, { + {"_default", CardBuild({}, CardDeck::mPublicDeck[""], {})}, + { + "bg", CardBuild({ + {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, + {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, + {"֮", "{֮}"}, {"", "{Աص}"} + }, CardDeck::mPublicDeck[""], {}) + } + } + }}, + {"DND", { + "DND", {}, {}, {}, {}, {}, {}, { + {"__DefaultDice",20} + }, { + {"_default", CardBuild({}, CardDeck::mPublicDeck[""], {})}, + { + "bg", CardBuild({ + {"Ա", "{Ա}"}, + }, CardDeck::mPublicDeck[""], {}) + } + } + }}, +}; +CardTemp& getCardTemplet(const string& type); -map& getmCardTemplet(); - - +struct lua_State; class CharaCard { private: public: string Name = "ɫ"; - string Type = "COC7"; - map Attr{}; - map Info{}; - map DiceExp{}; + //string Type = "COC7"; + map Attr{}; + map Info{ {"__Type","BRP"} }; + map DiceExp{}; string Note; - const CardTemp* pTemplet = &getmCardTemplet()[Type]; + CardTemp* pTemplet{ nullptr }; - CharaCard() - { + CharaCard(){ + pTemplet = &getCardTemplet("BRP"); } - CharaCard(string name, string type = "COC7") : Name(std::move(name)), Type(std::move(type)) + CharaCard(const string& name, const string& type = "COC7") : Name(name) { + Info["__Type"] = type; + Info["__Name"] = name; + pTemplet = &getCardTemplet(type); } short call(string& key) @@ -363,7 +410,7 @@ class CharaCard } } - [[nodiscard]] string standard(string key) const + [[nodiscard]] string standard(const string& key) const { if (pTemplet->replaceName.count(key))return pTemplet->replaceName.find(key)->second; return key; @@ -382,12 +429,7 @@ class CharaCard return 0; } - int setInfo(const string& key, const string& s) - { - if (key.empty() || s.length() > 48)return -1; - Info[key] = s; - return 0; - } + int setInfo(const string& key, const string& s); int setExp(const string& key, const string& exp) { @@ -436,13 +478,7 @@ class CharaCard return false; } - void clear() - { - Attr.clear(); - Info.clear(); - DiceExp.clear(); - Note.clear(); - } + void clear(); int show(string key, string& val) const { @@ -474,67 +510,9 @@ class CharaCard return -1; } - [[nodiscard]] string show(bool isWhole) const - { - std::set sDefault; - ResList Res; - for (const auto& list : pTemplet->vBasicList) - { - ResList subList; - string strVal; - for (const auto& it : list) - { - switch (show(it, strVal)) - { - case 0: - sDefault.insert(it); - subList << it + ":" + strVal; - break; - case 1: - sDefault.insert(it); - subList << "&" + it + "=" + strVal; - break; - case 3: - sDefault.insert(it); - subList.dot("\t"); - subList << it + ":" + strVal; - break; - default: - continue; - } - } - Res << subList.show(); - } - string strAttrRest; - for (const auto& it : Attr) - { - if (sDefault.count(it.first))continue; - strAttrRest += it.first + ":" + to_string(it.second) + " "; - } - Res << strAttrRest; - if (isWhole && !Info.empty()) - for (const auto& it : Info) - { - if (sDefault.count(it.first))continue; - Res << it.first + ":" + it.second; - } - if (isWhole && !DiceExp.empty()) - for (const auto& it : DiceExp) - { - if (sDefault.count(it.first))continue; - Res << "&" + it.first + "=" + it.second; - } - if (isWhole && !Note.empty())Res << "====================\n" + Note; - return Res.show(); - } + [[nodiscard]] string show(bool isWhole) const; - bool count(string& key) const - { - if (Attr.count(key))return true; - key = standard(key); - return Attr.count(key) || DiceExp.count(key) || pTemplet->mAutoFill.count(key) || pTemplet->mVariable.count(key) - || pTemplet->defaultSkill.count(key); - } + bool count(const string& key) const; bool stored(string& key) const { @@ -542,16 +520,7 @@ class CharaCard return Attr.count(key) || pTemplet->mAutoFill.count(key) || pTemplet->defaultSkill.count(key); } - short& operator[](string& key) - { - key = standard(key); - if (!Attr.count(key)) - { - if (pTemplet->mAutoFill.count(key))Attr[key] = cal(pTemplet->mAutoFill.find(key)->second); - if (pTemplet->defaultSkill.count(key))Attr[key] = pTemplet->defaultSkill.find(key)->second; - } - return Attr[key]; - } + short& operator[](const string& key); void operator<<(const CharaCard& card) { @@ -560,69 +529,12 @@ class CharaCard Name = name; } - void writeb(std::ofstream& fout) const - { - fwrite(fout, string("Name")); - fwrite(fout, Name); - fwrite(fout, string("Type")); - fwrite(fout, Type); - if (!Attr.empty()) - { - fwrite(fout, string("Attr")); - fwrite(fout, Attr); - } - if (!Info.empty()) - { - fwrite(fout, string("Info")); - fwrite(fout, Info); - } - if (!DiceExp.empty()) - { - fwrite(fout, string("DiceExp")); - fwrite(fout, DiceExp); - } - if (!Note.empty()) - { - fwrite(fout, string("Note")); - fwrite(fout, Note); - } - fwrite(fout, string("END")); - } + void writeb(std::ofstream& fout) const; - void readb(std::ifstream& fin) - { - string tag = fread(fin); - while (tag != "END") - { - switch (mCardTag[tag]) - { - case 1: - Name = fread(fin); - break; - case 2: - Type = fread(fin); - break; - case 11: - Attr = fread(fin); - break; - case 21: - DiceExp = fread(fin); - break; - case 102: - Info = fread(fin); - scanImage(Note, sReferencedImage); - break; - case 101: - Note = fread(fin); - scanImage(Note, sReferencedImage); - break; - default: - break; - } - tag = fread(fin); - } - pTemplet = getmCardTemplet().count(Type) ? &getmCardTemplet()[Type] : &getmCardTemplet()["COC7"]; - } + void readb(std::ifstream& fin); + + void pushTable(lua_State*); + void toCard(lua_State*); }; class Player @@ -681,7 +593,7 @@ class Player s.erase(s.begin(), s.begin() + Cnt + 1); if (type == "COC")type = "COC7"; } - else if (getmCardTemplet().count(s)) + else if (mCardTemplet.count(s)) { type = s; s.clear(); @@ -691,8 +603,8 @@ class Player vOption.push(type.substr(Cnt + 1)); type.erase(type.begin() + Cnt, type.end()); } - //Чģ - if (!getmCardTemplet().count(type))return -2; + //Чģ岻ٱ + //if (!getmCardTemplet().count(type))return -2; if (mNameIndex.count(s))return -4; if (s.find("=") != string::npos)return -6; mCardList[++indexMax] = CharaCard(s, type); @@ -705,7 +617,7 @@ class Player card.build(para); if (card.Name.empty()) { - std::vector list = getmCardTemplet()[type].mBuildOption[para].vNameList; + std::vector list = getCardTemplet(type).mBuildOption[para].vNameList; while (!list.empty()) { s = CardDeck::draw(list[0]); @@ -720,7 +632,7 @@ class Player } if (card.Name.empty()) { - std::vector list = getmCardTemplet()[type].mBuildOption[""].vNameList; + std::vector list = getCardTemplet(type).mBuildOption["_default"].vNameList; while (!list.empty()) { s = CardDeck::draw(list[0]); @@ -827,16 +739,7 @@ class Player return 0; } - string listCard() - { - ResList Res; - for (const auto& it : mCardList) - { - Res << "[" + to_string(it.first) + "]" + it.second.Name; - } - Res << "default:" + (*this)[0].Name; - return Res.show(); - } + string listCard(); string listMap() { @@ -913,7 +816,6 @@ class Player Unpack card; card.add(it.first); card.add(it.second.Name); - card.add(it.second.Type); Unpack skills; for (const auto& skill : it.second.Attr) { diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index f82a6781..cc33a0cc 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -73,14 +73,14 @@ void loadData() { mkDir(DiceDir); ResList logList; - loadDir(loadXML, string(DiceDir + "\\CardTemp\\"), getmCardTemplet(), logList, true); + loadDir(loadXML, string(DiceDir + "\\CardTemp\\"), mCardTemplet, logList, true); if (loadJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck) < 0 && loadJMap( strFileLoc + "ReplyDeck.json", CardDeck::mReplyDeck) > 0) { logList << "ǨԶظ" + to_string(CardDeck::mReplyDeck.size()) + ""; saveJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck); } - fmt->set_help("ظб", "ظб:" + listKey(CardDeck::mReplyDeck)); + fmt->set_help("ظб", "ظб:{list_reply_deck}"); if (loadDir(loadJMap, string(DiceDir + "\\PublicDeck\\"), CardDeck::mExternPublicDeck, logList) < 1) { loadJMap(strFileLoc + "PublicDeck.json", CardDeck::mExternPublicDeck); @@ -388,7 +388,7 @@ EVE_Enable(eventEnable) } for (auto gid : DD::getGroupIDList()) { - chat(gid).group().name(DD::getGroupName(gid)).reset("δ").reset(""); + chat(gid).group().reset("δ").reset(""); } blacklist = make_unique(); if (blacklist->loadJson(DiceDir + "\\conf\\BlackList.json") < 0) @@ -469,7 +469,7 @@ bool eve_GroupAdd(Chat& grp) if (grp.Name.empty()) grp.Name = DD::getGroupName(fromGroup); Size gsize(DD::getGroupSize(fromGroup)); - if (grp.boolConf.empty() && gsize.siz > 499) { + if (console["GroupInvalidSize"] > 0 && grp.boolConf.empty() && gsize.siz > (size_t)console["GroupInvalidSize"]) { grp.set("ЭЧ"); } if (!console["ListenGroupAdd"] || grp.isset(""))return 0; @@ -617,6 +617,7 @@ bool eve_GroupAdd(Chat& grp) EVE_PrivateMsg(eventPrivateMsg) { if (!Enabled)return 0; + if (fromQQ == console.DiceMaid && !console["ListenSelfEcho"])return 0; shared_ptr Msg(make_shared(message, fromQQ)); return Msg->DiceFilter(); } @@ -660,6 +661,7 @@ EVE_DiscussMsg(eventDiscussMsg) EVE_GroupMemberIncrease(eventGroupMemberAdd) { + if (!Enabled)return 0; Chat& grp = chat(fromGroup); if (grp.isset(""))return 0; if (fromQQ != console.DiceMaid) @@ -821,7 +823,7 @@ EVE_GroupInvited(eventGroupInvited) this_thread::sleep_for(3s); const string strNow = printSTNow(); string strMsg = "Ⱥԣ" + printQQ(fromQQ) + ",Ⱥ:" + - to_string(fromGroup) + ""; + DD::printGroupInfo(fromGroup); if (ExceptGroups.count(fromGroup)) { strMsg += "\nѺԣĬЭЧ"; console.log(strMsg, 0b10, strNow); @@ -860,6 +862,12 @@ EVE_GroupInvited(eventGroupInvited) console.log(strMsg, 0b10, strNow); DD::answerGroupInvited(fromGroup, 3); } + else if (console["GroupInvalidSize"] > 0 && DD::getGroupSize(grp.ID).siz > (size_t)console["GroupInvalidSize"]) { + grp.set("ЭЧ"); + strMsg += "\nѺԣȺĬЭЧ"; + console.log(strMsg, 0b10, strNow); + DD::answerGroupInvited(fromGroup, 3); + } else if (console && console["Private"]) { DD::sendPrivateMsg(fromQQ, getMsg("strPreserve")); diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 3968e672..5744925b 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -46,13 +46,14 @@ const std::mapConsole::intDefault{ {"AutoClearBlack",1},{"LeaveBlackQQ",0}, {"ListenGroupKick",1},{"ListenGroupBan",1},{"ListenSpam",1}, {"BannedBanInviter",0},{"KickedBanInviter",0}, -{"GroupClearLimit",20}, +{"GroupInvalidSize",500},{"GroupClearLimit",20}, {"CloudBlackShare",1},{"BelieveDiceList",0},{"CloudVisible",1}, {"SystemAlarmCPU",90},{"SystemAlarmRAM",90},{"SystemAlarmDisk",90}, {"SendIntervalIdle",500},{"SendIntervalBusy",100}, //Զ¼[min],ԶͼƬ[h],Զܼ[h] -{"AutoSaveInterval",10},{"AutoClearImage",0},{"AutoFrameRemake",0}, -{"ListenGroupEcho",0} +{"AutoSaveInterval",5},{"AutoClearImage",0},{"AutoFrameRemake",0}, +//ȺԼϢԼ˽Ϣ +{"ListenGroupEcho",0},{"ListenSelfEcho",0}, }; const enumap Console::mClockEvent{"off", "on", "save", "clear"}; @@ -161,6 +162,7 @@ int Console::log(const std::string& strMsg, int note_lv, const string& strTime) } if (!Cnt)DD::sendPrivateMsg(DiceMaid, note); } + else DD::debugLog(note); return Cnt; } void Console::newMaster(long long qq) @@ -377,6 +379,5 @@ bool operator<(const Console::Clock clock, const SYSTEMTIME& st) if (th.joinable())th.join(); } vTh = {}; - DD::debugLog("Dice߳"); } diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index c0dd15d3..173225d0 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -42,6 +42,7 @@ void FromMsg::reply(const std::string& msgReply, const std::initializer_list(strReply[0]))) strReply.erase(strReply.begin()); @@ -84,7 +85,7 @@ void FromMsg::replyHidden() { void FromMsg::fwdMsg() { - if (LinkList.count(fromSession) && LinkList[fromSession].second && strLowerMessage.find(".link") != 0) + if (LinkList.count(fromSession) && LinkList[fromSession].second && fromQQ != console.DiceMaid && strLowerMessage.find(".link") != 0) { string strFwd; if (trusted < 5)strFwd += printFrom(); @@ -1130,9 +1131,12 @@ int FromMsg::BasicOrder() int FromMsg::InnerOrder() { if (strMsg[0] != '.')return 0; - if (strLowerMessage.substr(intMsgCnt, 8) == "setreply") { + if (WordCensor()) { return 1; } + if (strLowerMessage.substr(intMsgCnt, 8) == "setreply") { + return 0; + } else if (strLowerMessage.substr(intMsgCnt, 7) == "welcome") { if (fromChat.second != msgtype::Group) { reply(GlobalMsg["strWelcomePrivate"]); @@ -1849,7 +1853,7 @@ int FromMsg::InnerOrder() { vector ProDeck; vector* TempDeck = nullptr; string& key{ strVar["deck_name"] = readAttrName() }; - if (!strVar["deck_name"].empty() && strVar["deck_name"][0] == '_') { + while (!strVar["deck_name"].empty() && strVar["deck_name"][0] == '_') { isPrivate = true; strVar["hidden"]; strVar["deck_name"].erase(strVar["deck_name"].begin()); @@ -1863,7 +1867,7 @@ int FromMsg::InnerOrder() { gm->session(fromSession)._draw(this); return 1; } - else if (strVar["deck_name"][0] == '_' || CardDeck::findDeck(strVar["deck_name"]) == 0) { + else if (CardDeck::findDeck(strVar["deck_name"]) == 0) { strReply = GlobalMsg["strDeckNotFound"]; reply(strReply); return 1; @@ -2290,27 +2294,31 @@ int FromMsg::InnerOrder() { intMsgCnt += 3; while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - string strDefaultDice = strLowerMessage.substr(intMsgCnt, strLowerMessage.find(' ', intMsgCnt) - intMsgCnt); - while (strDefaultDice[0] == '0') - strDefaultDice.erase(strDefaultDice.begin()); - if (strDefaultDice.empty()) - strDefaultDice = "100"; - for (auto charNumElement : strDefaultDice) + strVar["default"] = readDigit(); + while (strVar["default"][0] == '0') + strVar["default"].erase(strVar["default"].begin()); + if (strVar["default"].empty()) + strVar["default"] = "100"; + for (auto charNumElement : strVar["default"]) if (!isdigit(static_cast(charNumElement))) { reply(GlobalMsg["strSetInvalid"]); return 1; } - if (strDefaultDice.length() > 4) { + if (strVar["default"].length() > 4) { reply(GlobalMsg["strSetTooBig"]); return 1; } - const int intDefaultDice = stoi(strDefaultDice); + const int intDefaultDice = stoi(strVar["default"]); + if (PList.count(fromQQ)) { + PList[fromQQ][fromGroup]["__DefaultDice"] = intDefaultDice; + reply("ѽ" + strVar["pc"] + "Ĭ͸ΪD" + strVar["default"]); + return 1; + } if (intDefaultDice == 100) getUser(fromQQ).rmIntConf("Ĭ"); else getUser(fromQQ).setConf("Ĭ", intDefaultDice); - const string strSetSuccessReply = "ѽ" + strVar["pc"] + "Ĭ͸ΪD" + strDefaultDice; - reply(strSetSuccessReply); + reply("ѽ" + strVar["nick"] + "Ĭ͸ΪD" + strVar["default"]); return 1; } else if (strLowerMessage.substr(intMsgCnt, 3) == "str" && trusted > 3) { @@ -2612,7 +2620,7 @@ int FromMsg::InnerOrder() { if (strOption == "show") { string strName = readRest(); strVar["char"] = pl.getCard(strName, fromGroup).Name; - strVar["type"] = pl.getCard(strName, fromGroup).Type; + strVar["type"] = pl.getCard(strName, fromGroup).Info["__Type"]; strVar["show"] = pl[strVar["char"]].show(true); reply(GlobalMsg["strPcCardShow"]); return 1; @@ -2622,7 +2630,7 @@ int FromMsg::InnerOrder() { filter_CQcode(strVar["char"]); switch (pl.newCard(strVar["char"], fromGroup)) { case 0: - strVar["type"] = pl[fromGroup].Type; + strVar["type"] = pl[fromGroup].Info["__Type"]; strVar["show"] = pl[fromGroup].show(true); if (strVar["show"].empty())reply(GlobalMsg["strPcNewEmptyCard"]); else reply(GlobalMsg["strPcNewCardShow"]); @@ -2758,6 +2766,24 @@ int FromMsg::InnerOrder() { reply(GlobalMsg["strPcClr"]); return 1; } + if (strOption == "type") { + strVar["new_type"] = strip(readRest()); + if (strVar["new_type"].empty()) { + strVar["attr"] = "ģ"; + strVar["val"] = pl[fromGroup].Info["__Type"]; + reply(GlobalMsg["strProp"]); + } + else { + pl[fromGroup].setInfo("__Type", strVar["new_type"]); + reply(GlobalMsg["strSetPropSuccess"]); + } + return 1; + } + else if (strOption == "temp") { + CardTemp& temp{ *pl[fromGroup].pTemplet}; + reply(temp.show()); + return 1; + } reply(fmt->get_help("pc")); return 1; } @@ -3160,7 +3186,7 @@ int FromMsg::InnerOrder() { strVar["attr"] = readAttrName(); if (strVar["attr"].empty()) { strVar["char"] = pc.Name; - strVar["type"] = pc.Type; + strVar["type"] = pc.Info["__Type"]; strVar["show"] = pc.show(false); reply(GlobalMsg["strPropList"]); return 1; @@ -3182,7 +3208,10 @@ int FromMsg::InnerOrder() { readSkipSpace(); if (strMsg[intMsgCnt] == '&') { intMsgCnt++; - strVar["attr"] = readToColon(); + strVar["attr"] = readToColon(); + if (strVar["attr"].empty()) { + continue; + } if (pc.setExp(strVar["attr"], readExp())) { reply(GlobalMsg["strPcTextTooLong"]); return 1; @@ -3194,6 +3223,7 @@ int FromMsg::InnerOrder() { strVar["val"] = readDigit(false); continue; } + strSkillName = pc.standard(strSkillName); if (pc.pTemplet->sInfoList.count(strSkillName)) { while (isspace(static_cast(strLowerMessage[intMsgCnt])) || strLowerMessage[intMsgCnt] == '=' || strLowerMessage[intMsgCnt] == ':')intMsgCnt++; @@ -3487,7 +3517,13 @@ int FromMsg::InnerOrder() { intMsgCnt++; string strMainDice; strVar["reason"] = strMsg.substr(intMsgCnt); - if (PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].countExp(strMsg.substr(intMsgCnt))) { + if (strVar["reason"].empty()) { + string key{ "__DefaultDiceExp" }; + if (PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].countExp(strVar[key])) { + strMainDice = getPlayer(fromQQ)[fromGroup].getExp(key); + } + } + if (PList.count(fromQQ) && getPlayer(fromQQ)[fromGroup].countExp(strVar["reason"])) { strMainDice = getPlayer(fromQQ)[fromGroup].getExp(strVar["reason"]); } else { @@ -3503,7 +3539,9 @@ int FromMsg::InnerOrder() { else strMainDice.clear(); } int intTurnCnt = 1; - const int intDefaultDice = get(getUser(fromQQ).intConf, string("Ĭ"), 100); + const int intDefaultDice = (PList.count(fromQQ) && PList[fromQQ][fromGroup].count("__DefaultDice")) + ? PList[fromQQ][fromGroup]["__DefaultDice"] + : get(getUser(fromQQ).intConf, string("Ĭ"), 100); if (strMainDice.find('#') != string::npos) { strVar["turn"] = strMainDice.substr(0, strMainDice.find('#')); if (strVar["turn"].empty()) @@ -3553,13 +3591,7 @@ int FromMsg::InnerOrder() { reply(GlobalMsg["strRollTurn"], { strVar["pc"], strVar["turn"] }); } else { - strReply = format("" + printChat(fromChat) + " " + GlobalMsg["strRollTurn"], GlobalMsg, strVar); - AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) { - if (qq != fromQQ) { - AddMsgToQueue(strReply, qq, msgtype::Private); - } - } + replyHidden("" + printChat(fromChat) + " " + GlobalMsg["strRollTurn"]); } } } @@ -3628,14 +3660,7 @@ int FromMsg::InnerOrder() { reply(); } else { - strReply = format(strReply, GlobalMsg, strVar); - strReply = "" + printChat(fromChat) + " " + strReply; - AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) { - if (qq != fromQQ) { - AddMsgToQueue(strReply, qq, msgtype::Private); - } - } + replyHidden("" + printChat(fromChat) + " " + strReply); } } else { @@ -3661,14 +3686,7 @@ int FromMsg::InnerOrder() { reply(); } else { - strReply = format(strReply, GlobalMsg, strVar); - strReply = "" + printChat(fromChat) + " " + strReply; - AddMsgToQueue(strReply, fromQQ, msgtype::Private); - for (auto qq : gm->session(fromSession).get_ob()) { - if (qq != fromQQ) { - AddMsgToQueue(strReply, qq, msgtype::Private); - } - } + replyHidden("" + printChat(fromChat) + " " + strReply); } } if (isHidden) { @@ -3683,9 +3701,11 @@ int FromMsg::CustomReply() { const string strKey = readRest(); if (auto deck = CardDeck::mReplyDeck.find(strKey); deck != CardDeck::mReplyDeck.end() - || (!isDisabled && (deck = CardDeck::mReplyDeck.find(strMsg)) != CardDeck::mReplyDeck.end())) + || (deck = CardDeck::mReplyDeck.find(strMsg)) != CardDeck::mReplyDeck.end()) { - reply(CardDeck::drawCard(deck->second, true)); + string strAns(CardDeck::drawCard(deck->second, true)); + if (fromQQ == console.DiceMaid && strAns == strKey)return 0; + reply(strAns); if(!isVirtual)AddFrq(fromQQ, fromTime, fromChat); return 1; } @@ -3739,6 +3759,27 @@ bool FromMsg::DiceFilter() if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); return 1; } + if (fromChat.second == msgtype::Group && ((console["CheckGroupLicense"] > 0 && pGrp->isset("δ")) + || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ")) + || blacklist->get_group_danger(fromGroup))) { + isDisabled = true; + } + if (blacklist->get_qq_danger(fromQQ))isDisabled = true; + if (!isDisabled && (isCalled || !pGrp->isset("ָͣ"))) { + if (fmt->listen_order(this) || InnerOrder()) { + if (!isVirtual) { + AddFrq(fromQQ, fromTime, fromChat); + getUser(fromQQ).update(fromTime); + if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); + } + return true; + } + } + if (!isDisabled && (isCalled || !pGrp->isset("ûظ")) && CustomReply())return true; + if (isDisabled)return console["DisabledBlock"]; + return false; +} +bool FromMsg::WordCensor() { //С4ûдʼ if (trusted < 4) { unordered_setsens_words; @@ -3772,27 +3813,12 @@ bool FromMsg::DiceFilter() break; } } - if (fromChat.second == msgtype::Group && ((console["CheckGroupLicense"] > 0 && pGrp->isset("δ")) - || (console["CheckGroupLicense"] == 2 && !pGrp->isset("ʹ")) - || blacklist->get_group_danger(fromGroup))) { - isDisabled = true; - } - if (blacklist->get_qq_danger(fromQQ))isDisabled = true; - if (!isDisabled && (isCalled || !pGrp->isset("ָͣ"))) { - if ((fmt->listen_order(this) || InnerOrder()) && !isVirtual) { - AddFrq(fromQQ, fromTime, fromChat); - getUser(fromQQ).update(fromTime); - if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); - return true; - } - } - if (!isDisabled && (isCalled || !pGrp->isset("ûظ")) && CustomReply())return true; - if (isDisabled)return console["DisabledBlock"]; return false; } void FromMsg::operator()() { isVirtual = true; + isCalled = true; DiceFilter(); delete this; } diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 30b8a970..cf647f31 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -81,6 +81,7 @@ class FromMsg : public DiceJobDetail { int CustomReply(); //жǷӦ bool DiceFilter(); + bool WordCensor(); void operator()(); short trusted = 0; diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index c4300012..343b1a61 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -135,6 +135,7 @@ std::map fread(ifstream& fin) { std::map m; short len = fread(fin); + if (len < 0)return m; while (len--) { T1 key = fread(fin); @@ -143,12 +144,23 @@ std::map fread(ifstream& fin) } return m; } +template +void fread(ifstream& fin, std::map& dir) { + short len = fread(fin); + if (len < 0)return; + while (len--) { + T1 key = fread(fin); + T2 val = fread(fin); + dir[key] = val; + } +} // ȡļstd::set template std::set fread(ifstream& fin) { short len = fread(fin); + if (len < 0)return {}; set s{}; while (len--) { @@ -470,8 +482,8 @@ void fwrite(ofstream& fout, C& obj) obj.writeb(fout); } -template -void fwrite(ofstream& fout, const std::map& m) +template +void fwrite(ofstream& fout, const std::map& m) { const auto len = static_cast(m.size()); fwrite(fout, len); diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index a62881c5..9ef1aca9 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -291,7 +291,7 @@ void list_group(DiceJob& job) { if (grp.isset("") || grp.isset("δ") || !grp.isGroup)continue; Size size(DD::getGroupSize(id)); if (!size.siz)continue; - qSize.emplace(size.siz, printGroup(id)); + qSize.emplace(size.siz, DD::printGroupInfo(id)); } if (qSize.empty()) { job.reply("{self}ȺĻȺϢʧܣ"); @@ -299,7 +299,7 @@ void list_group(DiceJob& job) { size_t intCnt(0); ResList res; while (!qSize.empty()) { - res << qSize.top().second + "[" + to_string(qSize.top().first) + "]"; + res << qSize.top().second; qSize.pop(); if (++intCnt > 32 || qSize.top().first < 7)break; } @@ -349,7 +349,7 @@ void dice_cloudblack(DiceJob& job) { break; } if (isSuccess) { - job.note("ͬƲ¼ɹ" + getMsg("self") + "ʼȡ", 1); + if (job.fromQQ)job.note("ͬƲ¼ɹ" + getMsg("self") + "ʼȡ", 1); blacklist->loadJson(DiceDir + "/conf/CloudBlackList.json", true); } if (console["CloudBlackShare"]) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index afcf94db..ea2c036b 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -33,6 +33,12 @@ void lua_push_string(lua_State* L, const string& str) { else lua_pushstring(L, str.c_str()); } +void lua_set_field(lua_State* L, int idx, const string& str) { + if (UTF8Luas.count(L)) + lua_setfield(L, idx, GBKtoUTF8(str).c_str()); + else + lua_setfield(L, idx, str.c_str()); +} void lua_push_msg(lua_State* L, FromMsg* msg) { lua_newtable(L); @@ -45,7 +51,20 @@ void lua_push_msg(lua_State* L, FromMsg* msg) { lua_push_string(L, msg->strMsg); lua_setfield(L, -2, "fromMsg"); } +void CharaCard::pushTable(lua_State* L) { + lua_newtable(L); + for (auto& [key, val] : Info) { + lua_push_string(L, val); + lua_set_field(L, -2, key.c_str()); + } + for (auto& [key, val] : Attr) { + lua_pushnumber(L, val); + lua_set_field(L, -2, key.c_str()); + } +} +void CharaCard::toCard(lua_State* L) { +} //ȡָluaļĺ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { LuaState L(file); @@ -142,6 +161,7 @@ int setUserToday(lua_State* L) { today->set(qq, item, val); return 0; } + int getPlayerCardAttr(lua_State* L) { long long plQQ{ (long long)lua_tonumber(L, 1) }; long long group{ (long long)lua_tonumber(L, 2) }; @@ -157,6 +177,15 @@ int getPlayerCardAttr(lua_State* L) { } return 1; } +int getPlayerCard(lua_State* L) { + long long plQQ{ (long long)lua_tonumber(L, 1) }; + long long group{ (long long)lua_tonumber(L, 2) }; + if (PList.count(plQQ)) { + getPlayer(plQQ)[group].pushTable(L); + return 1; + } + return 0; +} int setPlayerCardAttr(lua_State* L) { long long plQQ{ (long long)lua_tonumber(L, 1) }; long long group{ (long long)lua_tonumber(L, 2) }; @@ -222,6 +251,7 @@ void LuaState::regist() { {"setUserToday", setUserToday}, {"getPlayerCardAttr", getPlayerCardAttr}, {"setPlayerCardAttr", setPlayerCardAttr}, + {"getPlayerCard", getPlayerCard}, {"ranint", ranint}, {"drawDeck", drawDeck}, {"eventMsg", eventMsg}, @@ -236,7 +266,6 @@ void LuaState::regist() { lua_pushstring(state, strPath.c_str()); lua_setfield(state, -3, "path"); lua_pop(state, 2); - SetCurrentDirectory((DiceDir + "\\plugin\\").c_str()); } LuaState::LuaState(const char* file) {//:isValid(false) { @@ -267,7 +296,7 @@ int lua_readStringTable(const char* file, const char* var, std::unordered_mapstrMsg, nameOrder))return false; + if (((FromMsg*)msg)->WordCensor()) { + return true; + } msgorder[nameOrder].exec((FromMsg*)msg); return true; } diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index dc6cfd4e..42df15d4 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -390,8 +390,9 @@ string DiceSession::deck_draw(const string& key) { return "{key}"; } void DiceSession::_draw(FromMsg* msg) { - if (msg->strVar["deck_name"].empty())msg->strVar["deck_name"] = msg->readAttrName(); - DeckInfo& deck = decks[msg->strVar["deck_name"]]; + shared_ptr job{ msg->shared_from_this() }; + if ((*job)["deck_name"].empty())(*job)["deck_name"] = msg->readAttrName(); + DeckInfo& deck = decks[(*job)["deck_name"]]; int intCardNum = 1; switch (msg->readNum(intCardNum)) { case 0: @@ -413,9 +414,9 @@ void DiceSession::_draw(FromMsg* msg) { if (!deck.sizRes)break; } if(!Res.empty()){ - msg->strVar["res"] = Res.dot("|").show(); - msg->strVar["cnt"] = to_string(Res.size()); - msg->initVar({ msg->strVar["pc"], msg->strVar["res"] }); + (*job)["res"] = Res.dot("|").show(); + (*job)["cnt"] = to_string(Res.size()); + msg->initVar({ (*job)["pc"], (*job)["res"] }); if (msg->strVar.count("hidden")) { msg->reply(GlobalMsg["strDrawHidden"]); msg->replyHidden(GlobalMsg["strDrawCard"]); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index a54c9c0a..0da944c5 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -361,6 +361,9 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +575:Ӧ +574:ĬŻ +573:ɫŻ 572:űдɫ 571:¿ܣ࿪ 570:.luaűԶָ @@ -372,20 +375,12 @@ const std::map HelpDoc = { 564:๦Żƶѵ 563:Żָ 562:GUI -561:Ⱥ/бڲ -560:֧Mirai, GUI 559:Զ̸²/¼ -558:ÿͳ 557:ʱҵϵͳ -556:ϵͳ 554:ɫ -553:nameܵ -552:̨ϵͳ 551:ļȡƶ 550:ּ춨 -549:ˢ -547:ָ -537:.send)"}, +549:ˢ)"}, {"Э","0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, {"","Dice!̳: https://kokona.tech \n Dice!̳: https://forum.kokona.tech"}, {"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ{}\n{}{}\nûȺ:{ûȺ}\nٷ(ˮ)Ⱥ: 882747577\n˽Ⱥ863062599 192499947\nȺ1029435374"}, @@ -454,7 +449,7 @@ const std::map HelpDoc = { .deck new [ƶ]=[1](...|[n]) //Զƶ : .deck new ˹=е|޵|޵|޵|޵|޵ -showȺڲҪȨ)"}, +showȺڲҪûλȨ)"}, {"Ⱥ","&dismiss"}, {"Ⱥָ","&dismiss"}, {"dismiss","ָҪȺԱȨޣʹú˳Ⱥ\n!dismiss [ĿQQ(ĩλ)]ָȺ\n!dismissú;Ĭ״ֻ̬ҪЧ"}, @@ -499,6 +494,7 @@ const std::map HelpDoc = { ȺĬʹ˽İ󶨿δʹ0ſ .pc show ([]) //չʾָм¼ԣΪչʾǰ .pc nn [¿] //ǰ +.pc type [ģ] //лǰģ .pc cpy [1]=[2] //ԸƸǰ .pc del [] //ɾָ .pc list //гȫɫ diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 8e352006..ee9c30a1 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -35,8 +35,8 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 572u; -inline const std::string Dice_Ver_Without_Build = "2.5.0beta2"; +const unsigned short Dice_Build = 575u; +inline const std::string Dice_Ver_Without_Build = "2.5.0"; constexpr auto DiceRequestHeader = "Dice/2.5.0"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index ae5766b8..441cda43 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -137,13 +137,10 @@ Chat& chat(long long id) } Chat& Chat::id(long long grp) { ID = grp; + Name = DD::getGroupName(grp); if (!Enabled)return *this; if (DD::getGroupIDList().count(grp)) { - Name = DD::getGroupName(grp); isGroup = true; - if (ExceptGroups.count(grp) || DD::getGroupSize(grp).siz > 499) { - boolConf.insert("ЭЧ"); - } } else { boolConf.insert("δ"); @@ -172,7 +169,8 @@ int groupset(long long id, const string& st) string printChat(Chat& grp) { - if (DD::getGroupIDList().count(grp.ID))return "[" + DD::getGroupName(grp.ID) + "](" + to_string(grp.ID) + ")"; + string name{ DD::getGroupName(grp.ID) }; + if (!name.empty())return "[" + name + "](" + to_string(grp.ID) + ")"; if (!grp.Name.empty())return "[" + grp.Name + "](" + to_string(grp.ID) + ")"; if (grp.isGroup) return "Ⱥ(" + to_string(grp.ID) + ")"; return "(" + to_string(grp.ID) + ")"; diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 5fe7e67d..1dd9f136 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -26,16 +26,22 @@ #include "DiceJob.h" #include "EncodingConvert.h" #include "StrExtern.hpp" +#include "CardDeck.h" using std::string; std::map GlobalChar{ {"FormFeed","\f"}, + {"LBrace","{"}, + {"RBrace","}"}, + {"LBracket","["}, + {"RBracket","]"}, }; std::map strFuncs{ {"master_QQ",print_master}, {"list_extern_deck",list_extern_deck}, {"list_all_deck",list_deck}, + {"list_reply_deck",[]() {return listKey(CardDeck::mReplyDeck); }}, {"list_extern_order",list_order_ex}, {"list_dice_sister",list_dice_sister}, }; diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index 72885df1..a15594b8 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -7,6 +7,7 @@ #include #include "MsgMonitor.h" #include "DiceSchedule.h" +#include "DDAPI.h" std::atomic FrqMonitor::sumFrqTotal = 0; std::map FrqMonitor::mFrequence = {}; @@ -78,14 +79,14 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT if (mFrequence.count(fromQQ)) { mFrequence[fromQQ] += 10; mCntOrder[fromQQ] += 1; - if (!console["ListenSpam"] || trustedQQ(fromQQ) > 1 || console.is_self(QQ))return; + if ((!console["ListenSpam"] || trustedQQ(fromQQ) > 1) && !console.is_self(QQ))return; if (mFrequence[fromQQ] > 60 && mWarnLevel[fromQQ] < 60) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; const std::string strMsg = "ѣ\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 18 ? "/5min" : (mCntOrder[fromQQ] > 8 ? "/min" : "/30s")); - AddMsgToQueue(getMsg("strSpamFirstWarning"), CT); + if(!console.is_self(QQ))AddMsgToQueue(getMsg("strSpamFirstWarning"), CT); console.log(strMsg, 1, printSTNow()); } else if (mFrequence[fromQQ] > 120 && mWarnLevel[fromQQ] < 120) { @@ -94,6 +95,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 36 ? "/5min" : (mCntOrder[fromQQ] > 15 ? "/min" : "/30s")); + if (!console.is_self(QQ))AddMsgToQueue(getMsg("strSpamFinalWarning"), CT); console.log(strMsg, 0b10, printSTNow()); } else if (mFrequence[fromQQ] > 200 && mWarnLevel[fromQQ] < 200) { @@ -103,7 +105,12 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 60 ? "/5min" : (mCntOrder[fromQQ] > 25 ? "/min" : "/30s")); - if (mDiceList.count(fromQQ)) { + if (console.is_self(QQ)) { + console.set("ListenSelfEcho", 0); + console.set("ListenGroupEcho", 0); + console.log(strNote + "\nǿֹͣջ", 0b1000, strNow); + } + else if (DD::getDiceSisters().count(fromQQ)) { console.log(strNote, 0b1000, strNow); } else { diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index 5c72a59b..4f9779ac 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -143,6 +143,7 @@ static std::map mVariableCOC7 = { {"ȡ", "&Ȼ"}, }; static std::map ExpressionCOC7 = { + {"__DefaultDiceExp", "1D100"}, {"ҽѧظ", "1D3"}, {"ظ", "1D3"}, {"ͽ˺", "1D3+[DB]"}, @@ -271,6 +272,7 @@ static std::map AutoFillCOC7 = { {"", "[]/2"} }; static std::map SkillDefaultVal = { + {"__DefaultDice", 100}, {"", 5}, {"ѧ", 1}, {"", 5}, diff --git a/QQAPI/DDAPI.cpp b/QQAPI/DDAPI.cpp index 40983e2b..7eb72bd1 100644 --- a/QQAPI/DDAPI.cpp +++ b/QQAPI/DDAPI.cpp @@ -14,6 +14,7 @@ DDAPI(DebugLog, void, const std::string&); DDAPI(IsDiceMaid, bool, long long); DDAPI(GetDiceSisters, const std::set&); DDAPI(DiceHeartbeat, void, long long, const std::string&); +DDAPI(DiceUploadBlack, int, long long, long long, long long, const std::string&, std::string&); DDAPI(DiceUpdate, bool, const std::string&, std::string&); DDAPI(GetNick, const std::string&, long long, long long); DDAPI(SendPrivateMsg, void, long long, long long, const std::string&); @@ -72,6 +73,11 @@ namespace DD { void heartbeat(const string& info) { CALLVOID(DiceHeartbeat, loginQQ, info); } + int uploadBlack(long long DiceMaid, long long fromQQ, long long fromGroup, + const std::string& type, std::string& info) { + info = "ϴӿڲ"; + return CALLGET(DiceUploadBlack, DiceMaid, fromQQ, fromGroup, type, info) :-1; + } bool updateDice(const std::string& ver, std::string& ret) { ret = "½ӿڲ"; return CALLGET(DiceUpdate, ver, ret) :false; @@ -80,12 +86,15 @@ namespace DD { return CALLGET(GetNick, loginQQ, aimQQ) :""; } void sendPrivateMsg(long long rcvQQ, const std::string& msg) { + if (msg.empty())return; CALLVOID(SendPrivateMsg, loginQQ, rcvQQ, msg); } void sendGroupMsg(long long rcvChat, const std::string& msg) { + if (msg.empty())return; CALLVOID(SendGroupMsg, loginQQ, rcvChat, msg); } void sendDiscussMsg(long long rcvChat, const std::string& msg) { + if (msg.empty())return; CALLVOID(SendDiscussMsg, loginQQ, rcvChat, msg); } std::set getFriendQQList() { diff --git a/QQAPI/DDAPI.h b/QQAPI/DDAPI.h index 96876223..b49afe52 100644 --- a/QQAPI/DDAPI.h +++ b/QQAPI/DDAPI.h @@ -29,6 +29,7 @@ namespace DD { bool isDiceMaid(long long); std::set getDiceSisters(); void heartbeat(const std::string&); + int uploadBlack(long long DiceMaid, long long fromQQ, long long fromGroup, const std::string& type, std::string& info); void sendPrivateMsg(long long, const std::string& msg); void sendGroupMsg(long long, const std::string& msg); void sendDiscussMsg(long long, const std::string& msg); diff --git a/QQAPI/QQEvent.cpp b/QQAPI/QQEvent.cpp index ec8344a2..7ca894c3 100644 --- a/QQAPI/QQEvent.cpp +++ b/QQAPI/QQEvent.cpp @@ -3,7 +3,6 @@ using namespace QQ; EVE_Startup(eventStartUp) { - if (!botQQ)return; loginQQ = botQQ; DD::ApiList = reinterpret_cast(initApi)(); DD::debugLog("Dice" + std::to_string(botQQ) + ".init"); From b212acab0e0c9f5c094593d17e7bca08a2f08813 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sun, 24 Jan 2021 19:24:33 +0800 Subject: [PATCH 046/171] update CharaCard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将角色卡姓名视为一项属性 --- Dice/CharacterCard.cpp | 35 ++++++++++++++++++++---- Dice/CharacterCard.h | 62 ++++++++++++------------------------------ Dice/DiceEvent.cpp | 14 +++++----- Dice/DiceLua.cpp | 54 +++++++++++++++++++++++++++++++----- Dice/GlobalVar.cpp | 3 +- 5 files changed, 104 insertions(+), 64 deletions(-) diff --git a/Dice/CharacterCard.cpp b/Dice/CharacterCard.cpp index d1fb148e..cd26a26b 100644 --- a/Dice/CharacterCard.cpp +++ b/Dice/CharacterCard.cpp @@ -29,6 +29,10 @@ CardTemp& getCardTemplet(const string& type) return mCardTemplet[type]; } +void CharaCard::setName(const string& strName) { + Name = strName; + Info["__Name"] = strName; +} void CharaCard::writeb(std::ofstream& fout) const { fwrite(fout, string("Name")); fwrite(fout, Name); @@ -58,6 +62,27 @@ int CharaCard::setInfo(const string& key, const string& s) { return 0; } +int CharaCard::show(string key, string& val) const { + if (Info.count(key)) { + val = Info.find(key)->second; + return 3; + } + if (key == "note") { + val = Note; + return 2; + } + if (DiceExp.count(key)) { + val = DiceExp.find(key)->second; + return 1; + } + key = standard(key); + if (Attr.count(key)) { + val = to_string(Attr.find(key)->second); + return 0; + } + return -1; +} + bool CharaCard::count(const string& strKey) const { if (Attr.count(strKey))return true; string key{ standard(strKey) }; @@ -133,7 +158,7 @@ void CharaCard::readb(std::ifstream& fin) { while (tag != "END") { switch (mCardTag[tag]) { case 1: - Name = fread(fin); + setName(fread(fin)); break; case 2: Info["__Type"] = fread(fin); @@ -170,15 +195,15 @@ Player& getPlayer(long long qq) string Player::listCard() { ResList Res; for (auto& [idx, pc] : mCardList) { - Res << "[" + to_string(idx) + "]<" + getCardTemplet(pc.Info["__Type"]).type + ">" + pc.Name; + Res << "[" + to_string(idx) + "]<" + getCardTemplet(pc.Info["__Type"]).type + ">" + pc.getName(); } - Res << "default:" + (*this)[0].Name; + Res << "default:" + (*this)[0].getName(); return Res.show(); } void getPCName(FromMsg& msg) { - msg["pc"] = (PList.count(msg.fromQQ) && PList[msg.fromQQ][msg.fromGroup].Name != "ɫ") - ? PList[msg.fromQQ][msg.fromGroup].Name + msg["pc"] = (PList.count(msg.fromQQ) && PList[msg.fromQQ][msg.fromGroup].getName() != "ɫ") + ? PList[msg.fromQQ][msg.fromGroup].getName() : msg["nick"]; } diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 07430694..b8662913 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -254,11 +254,13 @@ struct lua_State; class CharaCard { private: -public: string Name = "ɫ"; +public: + const string& getName()const { return Name; } + void setName(const string&); //string Type = "COC7"; map Attr{}; - map Info{ {"__Type","BRP"} }; + map Info{ {"__Type","COC7"} }; map DiceExp{}; string Note; CardTemp* pTemplet{ nullptr }; @@ -480,35 +482,7 @@ class CharaCard void clear(); - int show(string key, string& val) const - { - if (pTemplet->sInfoList.count(key)) - { - if (Info.count(key)) - { - val = Info.find(key)->second; - return 3; - } - return -1; - } - if (key == "note") - { - val = Note; - return 2; - } - if (DiceExp.count(key)) - { - val = DiceExp.find(key)->second; - return 1; - } - key = standard(key); - if (Attr.count(key)) - { - val = to_string(Attr.find(key)->second); - return 0; - } - return -1; - } + int show(string key, string& val) const; [[nodiscard]] string show(bool isWhole) const; @@ -615,7 +589,7 @@ class Player string para = vOption.top(); vOption.pop(); card.build(para); - if (card.Name.empty()) + if (card.getName().empty()) { std::vector list = getCardTemplet(type).mBuildOption[para].vNameList; while (!list.empty()) @@ -624,13 +598,13 @@ class Player if (mNameIndex.count(s))list.erase(list.begin()); else { - card.Name = s; + card.setName(s); break; } } } } - if (card.Name.empty()) + if (card.getName().empty()) { std::vector list = getCardTemplet(type).mBuildOption["_default"].vNameList; while (!list.empty()) @@ -639,13 +613,13 @@ class Player if (mNameIndex.count(s))list.erase(list.begin()); else { - card.Name = s; + card.setName(s); break; } } - if (card.Name.empty())card.Name = to_string(indexMax + 1); + if (card.getName().empty())card.setName(to_string(indexMax + 1)); } - s = card.Name; + s = card.getName(); mNameIndex[s] = indexMax; mGroupIndex[group] = indexMax; return 0; @@ -664,12 +638,12 @@ class Player if (!strName.empty() && !mNameIndex.count(strName)) { if (const int res = newCard(name, group))return res; - name = getCard(strName, group).Name; + name = getCard(strName, group).getName(); (*this)[name].buildv(); } else { - name = getCard(strName, group).Name; + name = getCard(strName, group).getName(); if (isClear)(*this)[name].clear(); (*this)[name].buildv(strType); } @@ -718,7 +692,7 @@ class Player if (name_new.find(":") != string::npos)return -6; const int i = mNameIndex[name_new] = mNameIndex[name]; mNameIndex.erase(name); - mCardList[i].Name = name_new; + mCardList[i].setName(name_new); return 0; } @@ -746,8 +720,8 @@ class Player ResList Res; for (const auto& it : mGroupIndex) { - if (!it.first)Res << "default:" + mCardList[it.second].Name; - else Res << "(" + to_string(it.first) + ")" + mCardList[it.second].Name; + if (!it.first)Res << "default:" + mCardList[it.second].getName(); + else Res << "(" + to_string(it.first) + ")" + mCardList[it.second].getName(); } return Res.show(); } @@ -815,7 +789,7 @@ class Player { Unpack card; card.add(it.first); - card.add(it.second.Name); + card.add(it.second.getName()); Unpack skills; for (const auto& skill : it.second.Attr) { @@ -850,7 +824,7 @@ class Player mCardList = fread(fin); for (const auto& card : mCardList) { - mNameIndex[card.second.Name] = card.first; + mNameIndex[card.second.getName()] = card.first; } mGroupIndex = fread(fin); } diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 0761e34a..026abf67 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -2434,7 +2434,7 @@ int FromMsg::InnerOrder() { return 1; } else if (strLowerMessage.substr(intMsgCnt, 2) == "li") { - string strAns = strVar["pc"] + "ķ-֢ܽ״:\n"; + string strAns = "{pc}ķ-֢ܽ״:\n"; LongInsane(strAns); reply(strAns); return 1; @@ -2622,7 +2622,7 @@ int FromMsg::InnerOrder() { } if (strOption == "show") { string strName = readRest(); - strVar["char"] = pl.getCard(strName, fromGroup).Name; + strVar["char"] = pl.getCard(strName, fromGroup).getName(); strVar["type"] = pl.getCard(strName, fromGroup).Info["__Type"]; strVar["show"] = pl[strVar["char"]].show(true); reply(GlobalMsg["strPcCardShow"]); @@ -2691,7 +2691,7 @@ int FromMsg::InnerOrder() { reply(GlobalMsg["strPCNameEmpty"]); return 1; } - strVar["old_name"] = pl[fromGroup].Name; + strVar["old_name"] = pl[fromGroup].getName(); switch (pl.renameCard(strVar["old_name"], strVar["new_name"])) { case 0: reply(GlobalMsg["strPcCardRename"]); @@ -2744,7 +2744,7 @@ int FromMsg::InnerOrder() { strVar["char1"] = strName.substr(0, strName.find('=')); strVar["char2"] = (strVar["char1"].length() < strName.length() - 1) ? strip(strName.substr(strVar["char1"].length() + 1)) - : pl[fromGroup].Name; + : pl[fromGroup].getName(); switch (pl.copyCard(strVar["char1"], strVar["char2"], fromGroup)) { case 0: reply(GlobalMsg["strPcCardCpy"]); @@ -3157,7 +3157,7 @@ int FromMsg::InnerOrder() { return 1; } getPlayer(fromQQ)[fromGroup].clear(); - strVar["char"] = getPlayer(fromQQ)[fromGroup].Name; + strVar["char"] = getPlayer(fromQQ)[fromGroup].getName(); reply(GlobalMsg["strPropCleared"], { strVar["char"] }); return 1; } @@ -3188,7 +3188,7 @@ int FromMsg::InnerOrder() { intMsgCnt++; strVar["attr"] = readAttrName(); if (strVar["attr"].empty()) { - strVar["char"] = pc.Name; + strVar["char"] = pc.getName(); strVar["type"] = pc.Info["__Type"]; strVar["show"] = pc.show(false); reply(GlobalMsg["strPropList"]); @@ -3293,7 +3293,7 @@ int FromMsg::InnerOrder() { return 1; } else if (strLowerMessage.substr(intMsgCnt, 2) == "ti") { - string strAns = strVar["pc"] + "ķ-ʱ֢״:\n"; + string strAns = "{pc}ķ-ʱ֢״:\n"; TempInsane(strAns); reply(strAns); return 1; diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index ea2c036b..e5789d90 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -103,6 +103,24 @@ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { * luaõĺ */ + //luaű +int loadLua(lua_State* L) { + string nameFile{ UTF8toGBK(lua_tostring(L, -1),true) }; + string pathFile = DiceDir + "\\plugin\\" + nameFile + ".lua"; + if (!std::filesystem::exists(pathFile) && nameFile.find('/') == string::npos && nameFile.find('\\') == string::npos) + pathFile = DiceDir + "\\plugin\\" + nameFile + "\\init.lua"; + if (luaL_loadfile(L, pathFile.c_str())) { + const char* pErrorMsg = lua_tostring(L, -1); + console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + pathFile + "ʧ:"+ pErrorMsg, 0b10); + return 0; + } + if (lua_pcall(L, 0, 1, 0)) { + const char* pErrorMsg = lua_tostring(L, -1); + console.log(GlobalMsg["strSelfName"] + "luaļ" + pathFile + "ʧ:"+ pErrorMsg, 0b10); + return 1; + } + return 1; +} //ȡDiceMaid int getDiceQQ(lua_State* L) { lua_push_string(L, to_string(console.DiceMaid)); @@ -165,11 +183,21 @@ int setUserToday(lua_State* L) { int getPlayerCardAttr(lua_State* L) { long long plQQ{ (long long)lua_tonumber(L, 1) }; long long group{ (long long)lua_tonumber(L, 2) }; - string item{ UTF8toGBK(lua_tostring(L, 3),true) }; + string key{ UTF8toGBK(lua_tostring(L, 3),true) }; CharaCard& pc = getPlayer(plQQ)[group]; - string val; - if (pc.show(item, val) > -1) { - lua_push_string(L, val); + if (pc.Info.count(key)) { + lua_push_string(L, pc.Info.find(key)->second); + return 3; + } + else if (key == "note") { + lua_push_string(L, pc.Note); + return 2; + } + else if (pc.DiceExp.count(key)) { + lua_push_string(L, pc.DiceExp.find(key)->second); + } + else if (key = pc.standard(key); pc.Attr.count(key)) { + lua_pushnumber(L, (double)pc.Attr.find(key)->second); } else { lua_pushnil(L); @@ -195,7 +223,9 @@ int setPlayerCardAttr(lua_State* L) { pc.set(item, (int)lua_tonumber(L, -1)); } else if (lua_isstring(L, -1)) { - pc.setInfo(item, UTF8toGBK(lua_tostring(L, -1), true)); + if (item.empty())return 0; + else if (item[0] == '&')pc.setExp(item.substr(1), UTF8toGBK(lua_tostring(L, -1), true)); + else pc.setInfo(item, UTF8toGBK(lua_tostring(L, -1), true)); } return 0; } @@ -207,6 +237,12 @@ int ranint(lua_State* L) { lua_pushnumber(L, RandomGenerator::Randint(l,r)); return 1; } +//̵߳ȴ +int sleepTime(lua_State* L) { + int ms{ (int)lua_tonumber(L, -1) }; + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + return 0; +} int drawDeck(lua_State* L) { long long fromGroup{ (long long)lua_tonumber(L, -3) }; @@ -242,6 +278,7 @@ int eventMsg(lua_State* L) { void LuaState::regist() { const luaL_Reg Dicelibs[] = { + {"loadLua", loadLua}, {"getDiceQQ", getDiceQQ}, {"getDiceDir", getDiceDir}, {"mkDirs", mkDirs}, @@ -253,6 +290,7 @@ void LuaState::regist() { {"setPlayerCardAttr", setPlayerCardAttr}, {"getPlayerCard", getPlayerCard}, {"ranint", ranint}, + {"sleepTime", sleepTime}, {"drawDeck", drawDeck}, {"eventMsg", eventMsg}, {nullptr, nullptr}, @@ -272,7 +310,8 @@ LuaState::LuaState(const char* file) {//:isValid(false) { state = luaL_newstate(); if (!state)return; if (luaL_loadfile(state, file)) { - console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + file + "ʧ!", 0b10); + const char* pErrorMsg = lua_tostring(state, -1); + console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + file + "ʧ:" + pErrorMsg, 0b10); lua_close(state); state = nullptr; return; @@ -280,7 +319,8 @@ LuaState::LuaState(const char* file) {//:isValid(false) { luaL_openlibs(state); regist(); if (lua_pcall(state, 0, 0, 0)) { - console.log(GlobalMsg["strSelfName"] + "luaļ" + file + "ʧ!", 0b10); + const char* pErrorMsg = lua_tostring(state, -1); + console.log(GlobalMsg["strSelfName"] + "luaļ" + file + "ʧ:" + pErrorMsg, 0b10); lua_close(state); state = nullptr; return; diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index aa8d9e8d..7485f9ac 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -542,8 +542,9 @@ const std::map HelpDoc = { "draw", R"(ƣ.draw [ƶ] ([]) .draw _[ƶ] ([]) //飬˽ķ +.drawh [ƶ] ([]) //飬hո .draw _ɱ //ƽͨ˽Ļȡob -*ƶƿǹƶƶʵ +*ƶȵƶʵδͬƶʱʵ *鵽ƲŻأƶѳպ޷ *鿴{self}Ѱװƶѣ.help ȫƶб.help չƶ)" }, From 21447b5e2eb3b6978675175cba3c586f6586dcef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Mon, 1 Feb 2021 22:29:55 -0800 Subject: [PATCH 047/171] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..def0fc55 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://afdian.net/@suhuiw4123'] From cb2d1daedd151bcd7372ac5413edc17ca2f07cd4 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 3 Feb 2021 18:06:28 +0800 Subject: [PATCH 048/171] fix replyHidden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复暗骰的抄送格式 --- Dice/Dice.cpp | 2 +- Dice/DiceEvent.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index cc33a0cc..c49c54bd 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -63,7 +63,7 @@ ThreadFactory threads; string strFileLoc; constexpr auto msgInit{ R"(ӭʹDice!ˣ -Ӳ˵Dice!->ۺϹĺ̨ +뷢.system guiĺ̨ Masterģʽͨ󼴿ɳΪҵ~ ɷ.help鿴 οĵο.help)" }; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 026abf67..170bcae8 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -3018,8 +3018,10 @@ int FromMsg::InnerOrder() { } readSkipSpace(); string strname = strMsg.substr(intMsgCnt); - if (strname.empty()) + if (strname.empty()) { + if (!strVar.count("pc") || strVar["pc"].empty())getPCName(*this); strname = strVar["pc"]; + } else strname = strip(strname); RD initdice(strinit, 20); @@ -3594,7 +3596,7 @@ int FromMsg::InnerOrder() { reply(GlobalMsg["strRollTurn"], { strVar["pc"], strVar["turn"] }); } else { - replyHidden("" + printChat(fromChat) + " " + GlobalMsg["strRollTurn"]); + replyHidden(GlobalMsg["strRollTurn"]); } } } @@ -3663,7 +3665,7 @@ int FromMsg::InnerOrder() { reply(); } else { - replyHidden("" + printChat(fromChat) + " " + strReply); + replyHidden(strReply); } } else { @@ -3689,7 +3691,7 @@ int FromMsg::InnerOrder() { reply(); } else { - replyHidden("" + printChat(fromChat) + " " + strReply); + replyHidden(strReply); } } if (isHidden) { From 1f74291fe9659cd1ffa3bf51d3e6cf709c15c8d5 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Thu, 4 Feb 2021 11:31:09 +0800 Subject: [PATCH 049/171] update GetRoorDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由Driver处理根目录以适应Mirai 弃用 clock() --- Dice/Dice.cpp | 16 +++++++++------- Dice/DiceConsole.cpp | 2 +- Dice/DiceEvent.cpp | 2 +- Dice/DiceJob.cpp | 2 +- Dice/GlobalVar.cpp | 2 +- Dice/GlobalVar.h | 2 +- Dice/ManagerSystem.h | 2 +- Dice/MsgMonitor.cpp | 2 +- QQAPI/DDAPI.cpp | 5 +++++ QQAPI/DDAPI.h | 1 + 10 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index c49c54bd..a5b3d86c 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -174,13 +174,15 @@ EVE_Enable(eventEnable) { if (isIniting || Enabled)return; isIniting = true; - llStartTime = clock(); - char path[MAX_PATH]; - GetModuleFileNameA(nullptr, path, MAX_PATH); - strModulePath = string(path); + llStartTime = time(nullptr); + if ((dirExe = DD::getRootDir()).empty()) { + char path[MAX_PATH]; + GetModuleFileNameA(nullptr, path, MAX_PATH); + string strPath(path); + dirExe = strPath.substr(0, strPath.rfind("\\") + 1); + } Dice_Full_Ver_On = Dice_Full_Ver + " on\n" + DD::getDriVer(); DD::debugLog(Dice_Full_Ver_On); - dirExe = strModulePath.substr(0, strModulePath.rfind("\\") + 1); if (console.DiceMaid = DD::getLoginQQ()) { DiceDir = dirExe + "Dice" + to_string(console.DiceMaid); @@ -446,12 +448,12 @@ EVE_Enable(eventEnable) threads(warningHandler); threads(frqHandler); sch.start(); - console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, + console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string(time(nullptr) - llStartTime) + "", 0b1, printSTNow()); // getDiceList(); getExceptGroup(); - llStartTime = clock(); + llStartTime = time(nullptr); isIniting = false; } diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 5744925b..e7a9fc6c 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -225,7 +225,7 @@ std::set ExceptGroups; std::map mDiceList; //ʱ -long long llStartTime = clock(); +long long llStartTime = time(nullptr); //ǰʱ SYSTEMTIME stNow{}; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 170bcae8..3ca5347a 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1260,7 +1260,7 @@ int FromMsg::InnerOrder() { << "ڴռ:" + to_string(getRamPort()) + "%" << "CPUռ:" + toString(getWinCpuUsage() / 10.0) + "%" << "Ӳռ:" + toString(milDisk / 10.0) + "%(:" + toString(mbFreeBytes) + "GB/ " + toString(mbTotalBytes) + "GB)" - << "ʱ:" + printDuringTime((clock() - llStartTime) / 1000) + << "ʱ:" + printDuringTime(time(nullptr) - llStartTime) << "ָ:" + to_string(today->get("frq")) << "ָ:" + to_string(FrqMonitor::sumFrqTotal); reply(res.show()); diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 9ef1aca9..e7967e2f 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -43,7 +43,7 @@ void frame_restart(DiceJob& job) { sch.add_job_for(60 * 60, job); return; } - else if (int tWait{ console["AutoFrameRemake"] * 60 * 60 - int(clock() - llStartTime) / 1000 }; tWait > 0) { + else if (int tWait{ console["AutoFrameRemake"] * 60 * 60 - int(time(nullptr) - llStartTime) }; tWait > 0) { sch.add_job_for(tWait, job); return; } diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 7485f9ac..df6e2caf 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -31,7 +31,7 @@ bool Enabled = false; std::string Dice_Full_Ver_On; -std::string strModulePath; +//std::string strModulePath; HMODULE hDllModule = nullptr; diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index ee9c30a1..4cde22d2 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -78,7 +78,7 @@ extern bool Enabled; extern std::string Dice_Full_Ver_On; // ִļλ -extern std::string strModulePath; +//extern std::string strModulePath; // Ϣ߳Ƿ extern bool msgSendThreadRunning; diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index 68b16f15..261198e9 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -21,7 +21,7 @@ using std::vector; using std::unordered_map; constexpr auto CQ_IMAGE = "[CQ:image,file="; constexpr auto CQ_AT = "[CQ:at,qq="; -constexpr time_t NEWYEAR = 1596211200; +constexpr time_t NEWYEAR = 1609430400; // void loadData(); diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index a15594b8..2b21c0e0 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -141,7 +141,7 @@ EVE_Status_EX(statusFrq) } //ƽӵƵ const int intFrq = FrqMonitor::getFrqTotal(); - //long long llDuration = (clock() - llStartTime) / 1000; + //long long llDuration = time(nullptr) - llStartTime; if (intFrq < 0) { eve.data = "N"; diff --git a/QQAPI/DDAPI.cpp b/QQAPI/DDAPI.cpp index 7eb72bd1..50c099ae 100644 --- a/QQAPI/DDAPI.cpp +++ b/QQAPI/DDAPI.cpp @@ -7,6 +7,7 @@ using namespace DD; #define DDAPI(Name, ReturnType, ...) using Name##_TYPE = ReturnType (*)(__VA_ARGS__) DDAPI(_DriverVer, const char*); +DDAPI(GetRootDir, const std::string&); DDAPI(Reload, bool); DDAPI(Remake, bool); DDAPI(Killme, void); @@ -52,6 +53,10 @@ namespace DD { static string ver{ CALLGET(_DriverVer) :"" }; return ver; } + const string& getRootDir() { + static string dir{ CALLGET(GetRootDir) :"" }; + return dir; + } bool reload() { return CALLGET(Reload) :false; } diff --git a/QQAPI/DDAPI.h b/QQAPI/DDAPI.h index b49afe52..2f133a96 100644 --- a/QQAPI/DDAPI.h +++ b/QQAPI/DDAPI.h @@ -22,6 +22,7 @@ namespace DD { void killme(); bool updateDice(const std::string&, std::string&); inline api_list ApiList; + const std::string& getRootDir(); inline long long getLoginQQ() { return loginQQ; } std::string getQQNick(long long); inline std::string getLoginNick() { return getQQNick(loginQQ); } From e23f117d410130212c844ec4d6422ddccc573ff6 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 4 Feb 2021 12:03:37 +0800 Subject: [PATCH 050/171] Update Cross-Platform support --- CQSDK/CQEVE.h | 6 +++ CQSDKCPP/CQEVE.cpp | 3 -- Dice.cppcheck | 17 ------- Dice/APPINFO.h | 3 +- Dice/BlackListManager.cpp | 10 ++-- Dice/BlackListManager.h | 4 +- Dice/CardDeck.h | 4 +- Dice/CharacterCard.h | 6 ++- Dice/CustomMsg.h | 6 ++- Dice/Dice.cpp | 96 ++++++++++++++++++++++++--------------- Dice/DiceCensor.cpp | 3 +- Dice/DiceCensor.h | 1 + Dice/DiceCloud.cpp | 23 ++++++++-- Dice/DiceCloud.h | 4 +- Dice/DiceConsole.cpp | 71 +++++++++++++++++++---------- Dice/DiceConsole.h | 10 ++-- Dice/DiceEvent.cpp | 33 +++++++++++--- Dice/DiceEvent.h | 4 +- Dice/DiceFile.hpp | 45 ++++++++++++++++-- Dice/DiceGUI.cpp | 4 +- Dice/DiceGUI.h | 2 + Dice/DiceJob.cpp | 53 +++++++++++++-------- Dice/DiceJob.h | 2 + Dice/DiceL5R.h | 2 +- Dice/DiceMod.cpp | 6 +-- Dice/DiceMod.h | 4 +- Dice/DiceMsgSend.h | 4 +- Dice/DiceNetwork.cpp | 67 +++++++++++++++++++++++++-- Dice/DiceNetwork.h | 4 +- Dice/DiceSchedule.cpp | 26 +++++++++-- Dice/DiceSchedule.h | 4 +- Dice/DiceSession.cpp | 16 +++---- Dice/DiceSession.h | 2 +- Dice/DiceXMLTree.h | 4 +- Dice/EncodingConvert.cpp | 16 +++++-- Dice/EncodingConvert.h | 38 +++++++++++++++- Dice/GetRule.cpp | 12 ++--- Dice/GetRule.h | 4 +- Dice/GlobalVar.cpp | 4 ++ Dice/GlobalVar.h | 8 +++- Dice/Jsonio.h | 4 +- Dice/ManagerSystem.cpp | 12 +++-- Dice/ManagerSystem.h | 10 ++-- Dice/MsgFormat.h | 4 +- Dice/MsgMonitor.cpp | 6 +-- Dice/RD.h | 1 + Dice/RDConstant.h | 1 + Dice/RandomGenerator.cpp | 20 ++++++-- Dice/RandomGenerator.h | 2 +- Dice/S3PutObject.cpp | 17 +++---- Dice/SHKQuerier.h | 1 + Dice/SHKTrie.h | 1 + Dice/dllmain.cpp | 2 + Dice/strExtern.cpp | 17 +++++++ 54 files changed, 533 insertions(+), 196 deletions(-) delete mode 100644 Dice.cppcheck diff --git a/CQSDK/CQEVE.h b/CQSDK/CQEVE.h index 94e06eb7..6158f89a 100644 --- a/CQSDK/CQEVE.h +++ b/CQSDK/CQEVE.h @@ -4,6 +4,7 @@ Api Version 9.10 Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs */ #pragma once +#ifdef _WIN32 #ifdef _MSC_VER #define CQEVENT(ReturnType, Name, Size) __pragma(comment(linker, "/EXPORT:" #Name "=_" #Name "@" #Size))\ extern "C" __declspec(dllexport) ReturnType __stdcall Name @@ -11,6 +12,11 @@ Written by MukiPy2001 & Thanks for the help of orzFly and Coxxs #define CQEVENT(ReturnType, Name, Size)\ extern "C" __attribute__((dllexport)) ReturnType __attribute__((__stdcall__)) Name #endif /*_MSC_VER*/ +#else +#define CQEVENT(ReturnType, Name, Size)\ + extern "C" __attribute__((visibility("default"))) ReturnType __attribute__((__stdcall__)) Name +#endif + /* ӦõApiVerAppid󽫲 */ diff --git a/CQSDKCPP/CQEVE.cpp b/CQSDKCPP/CQEVE.cpp index 4d279128..d8e8eaf8 100644 --- a/CQSDKCPP/CQEVE.cpp +++ b/CQSDKCPP/CQEVE.cpp @@ -9,9 +9,6 @@ #include "CQTools.h" #include "Unpack.h" -#define WIN32_LEAN_AND_MEAN -#include - using namespace CQ; diff --git a/Dice.cppcheck b/Dice.cppcheck deleted file mode 100644 index 837da140..00000000 --- a/Dice.cppcheck +++ /dev/null @@ -1,17 +0,0 @@ - - - Dice-cppcheck-build-dir - win32A - Dice.sln - false - true - false - 10 - - Debug - Release - - - windows - - diff --git a/Dice/APPINFO.h b/Dice/APPINFO.h index 24c9bab6..4cc99450 100644 --- a/Dice/APPINFO.h +++ b/Dice/APPINFO.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -24,7 +26,6 @@ // ļڿQȡӦAPPINFO // ޸ -#pragma once #ifndef DICE_APPINFO #define DICE_APPINFO diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index cde8eb36..4a55e89a 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -652,7 +652,7 @@ bool DDBlackManager::insert(DDBlackMark& ex_mark) up_qq_danger(mark.ownerQQ.first, mark); } } - if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "/conf/BlackList.json"); return !mark.isClear; } @@ -811,7 +811,7 @@ bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) else if (old_mark.comment.empty()) { old_mark.comment = printSTNow() + " "; } - if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "/conf/BlackList.json"); } return isUpdated; } @@ -942,7 +942,7 @@ void DDBlackManager::rm_black_group(long long llgroup, FromMsg* msg) } mGroupDanger.erase(llgroup); msg->note("ע" + printGroup(llgroup) + "ĺ¼"); - blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + blacklist->saveJson(DiceDir + "/conf/BlackList.json"); } void DDBlackManager::rm_black_qq(long long llqq, FromMsg* msg) @@ -965,7 +965,7 @@ void DDBlackManager::rm_black_qq(long long llqq, FromMsg* msg) } reset_qq_danger(llqq); msg->note("ע" + printQQ(llqq) + "ĺ¼"); - blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + blacklist->saveJson(DiceDir + "/conf/BlackList.json"); } void DDBlackManager::isban(FromMsg* msg) @@ -1297,7 +1297,7 @@ int DDBlackManager::loadJson(string strPath, bool isExtern) if (isExtern) { filesystem::remove(strPath); console.log("Դ¼Ŀ" + to_string(cnt) + "", 1, printSTNow()); - blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + blacklist->saveJson(DiceDir + "/conf/BlackList.json"); } return cnt; } diff --git a/Dice/BlackListManager.h b/Dice/BlackListManager.h index 0fd4da7a..e19027dc 100644 --- a/Dice/BlackListManager.h +++ b/Dice/BlackListManager.h @@ -1,9 +1,11 @@ +#pragma once + /** * ϸ * ݿʽĹ * Copyright (C) 2019-2020 String.Empty */ -#pragma once + #include #include #include diff --git a/Dice/CardDeck.h b/Dice/CardDeck.h index e1dbf9d7..22dfcaa9 100644 --- a/Dice/CardDeck.h +++ b/Dice/CardDeck.h @@ -1,9 +1,11 @@ +#pragma once + /* * ƶѳ鿨 * ΪչDiceϣԸ»ֹij̶ֳ * Copyright (C) 2019 String.Empty */ -#pragma once + #ifndef CARD_DECK #define CARD_DECK #include diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 1c082cd6..1d9c2f93 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -1,8 +1,10 @@ +#pragma once + /* * │ * Copyright (C) 2019 String.Empty */ -#pragma once + #include #include #include @@ -934,7 +936,7 @@ class Player fout << base64_encode(pack.getAll()); } - void writeb(std::ofstream& fout) + void writeb(std::ofstream& fout) const { fwrite(fout, indexMax); fwrite(fout, mCardList); diff --git a/Dice/CustomMsg.h b/Dice/CustomMsg.h index f4c6802a..87151dd2 100644 --- a/Dice/CustomMsg.h +++ b/Dice/CustomMsg.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -20,9 +22,11 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#pragma once + #ifndef DICE_CUSTOM_MSG #define DICE_CUSTOM_MSG +#include +#include void ReadCustomMsg(std::ifstream& in); void SaveCustomMsg(std::string strPath); diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 89ff52b2..de88e3fe 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -21,8 +21,6 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#define WIN32_LEAN_AND_MEAN -#include #include #include #include @@ -52,6 +50,10 @@ #include "S3PutObject.h" #include "DiceCensor.h" +#ifndef _WIN32 +#include +#endif + #pragma warning(disable:4996) #pragma warning(disable:6031) @@ -73,15 +75,15 @@ void loadData() { mkDir(DiceDir); string strLog; - loadDir(loadXML, string(DiceDir + "\\CardTemp\\"), getmCardTemplet(), strLog, true); - if (loadJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck) < 0 && loadJMap( + loadDir(loadXML, string(DiceDir + "/CardTemp/"), getmCardTemplet(), strLog, true); + if (loadJMap(DiceDir + "/conf/CustomReply.json", CardDeck::mReplyDeck) < 0 && loadJMap( strFileLoc + "ReplyDeck.json", CardDeck::mReplyDeck) > 0) { console.log("ǨԶظ" + to_string(CardDeck::mReplyDeck.size()) + "", 1); - saveJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck); + saveJMap(DiceDir + "/conf/CustomReply.json", CardDeck::mReplyDeck); } fmt->set_help("ظб", "ظб:" + listKey(CardDeck::mReplyDeck)); - if (loadDir(loadJMap, string(DiceDir + "\\PublicDeck\\"), CardDeck::mExternPublicDeck, strLog) < 1) + if (loadDir(loadJMap, string(DiceDir + "/PublicDeck/"), CardDeck::mExternPublicDeck, strLog) < 1) { loadJMap(strFileLoc + "PublicDeck.json", CardDeck::mExternPublicDeck); loadJMap(strFileLoc + "ExternDeck.json", CardDeck::mExternPublicDeck); @@ -89,7 +91,7 @@ void loadData() map_merge(CardDeck::mPublicDeck, CardDeck::mExternPublicDeck); //ȡĵ fmt->load(strLog); - if (int cnt; (cnt = loadJMap(DiceDir + "\\conf\\CustomHelp.json", CustomHelp)) < 0) + if (int cnt; (cnt = loadJMap(DiceDir + "/conf/CustomHelp.json", CustomHelp)) < 0) { if (cnt == -1)console.log("Զļjsonʧܣ", 1); ifstream ifstreamHelpDoc(strFileLoc + "HelpDoc.txt"); @@ -105,7 +107,7 @@ void loadData() } if (!CustomHelp.empty()) { - saveJMap(DiceDir + "\\conf\\CustomHelp.json", CustomHelp); + saveJMap(DiceDir + "/conf/CustomHelp.json", CustomHelp); console.log("ʼԶ" + to_string(CustomHelp.size()) + "", 1); } } @@ -113,8 +115,8 @@ void loadData() } map_merge(fmt->helpdoc, CustomHelp); //ȡдʿ - loadDir(load_words, DiceDir + "\\conf\\censor\\", censor, strLog, true); - loadJMap(DiceDir + "\\conf\\CustomCensor.json", censor.CustomWords); + loadDir(load_words, DiceDir + "/conf/censor/", censor, strLog, true); + loadJMap(DiceDir + "/conf/CustomCensor.json", censor.CustomWords); censor.build(); if (!strLog.empty()) { @@ -158,24 +160,37 @@ void dataInit() // void dataBackUp() { - mkDir(DiceDir + "\\conf"); - mkDir(DiceDir + "\\user"); - mkDir(DiceDir + "\\audit"); + mkDir(DiceDir + "/conf"); + mkDir(DiceDir + "/user"); + mkDir(DiceDir + "/audit"); //б - saveBFile(DiceDir + "\\user\\PlayerCards.RDconf", PList); - saveFile(DiceDir + "\\user\\ChatList.txt", ChatList); - saveBFile(DiceDir + "\\user\\ChatConf.RDconf", ChatList); - saveFile(DiceDir + "\\user\\UserList.txt", UserList); - saveBFile(DiceDir + "\\user\\UserConf.RDconf", UserList); + saveBFile(DiceDir + "/user/PlayerCards.RDconf", PList); + saveFile(DiceDir + "/user/ChatList.txt", ChatList); + saveBFile(DiceDir + "/user/ChatConf.RDconf", ChatList); + saveFile(DiceDir + "/user/UserList.txt", UserList); + saveBFile(DiceDir + "/user/UserConf.RDconf", UserList); } EVE_Enable(eventEnable) { llStartTime = clock(); - char path[MAX_PATH]; +#ifndef _WIN32 + CURLcode err; + err = curl_global_init(CURL_GLOBAL_DEFAULT); + if (err != CURLE_OK) + { + console.log(": libcurlʧܣ", 1); + } +#endif + char path[260]; +#ifdef _WIN32 GetModuleFileNameA(nullptr, path, MAX_PATH); +#else + *path = 0; +#endif std::string pathStr(path); strModulePath = pathStr; + // TODO: ΪLinux޸ string pathExe = pathStr.substr(pathStr.rfind("\\") + 1); std::transform(pathStr.begin(), pathStr.end(), pathStr.begin(), [](unsigned char c) { return tolower(c); }); if (pathExe.substr(0, 4) == "java") @@ -191,7 +206,7 @@ EVE_Enable(eventEnable) dirExe = pathStr.substr(0, pathStr.find_last_of('\\') + 1); this_thread::sleep_for(3s); } - if (console.DiceMaid = getLoginQQ()) + if ((console.DiceMaid = getLoginQQ())) { DiceDir = dirExe + "Dice" + to_string(console.DiceMaid); filesystem::path pathDir(DiceDir); @@ -201,7 +216,7 @@ EVE_Enable(eventEnable) else filesystem::create_directory(pathDir); } } - console.setPath(DiceDir + "\\conf\\Console.xml"); + console.setPath(DiceDir + "/conf/Console.xml"); strFileLoc = getAppDirectory(); mkDir(strFileLoc); // MiraiԶļ GlobalMsg["strSelfName"] = getLoginNick(); @@ -209,9 +224,9 @@ EVE_Enable(eventEnable) { GlobalMsg["strSelfName"] = "[" + toString(console.DiceMaid % 1000, 4) + "]"; } - mkDir(DiceDir + "\\conf"); - mkDir(DiceDir + "\\user"); - mkDir(DiceDir + "\\audit"); + mkDir(DiceDir + "/conf"); + mkDir(DiceDir + "/user"); + mkDir(DiceDir + "/audit"); if (!console.load()) { ifstream ifstreamMaster(strFileLoc + "Master.RDconf"); @@ -246,7 +261,7 @@ EVE_Enable(eventEnable) console.save(); } //ȡб - if (loadBFile(DiceDir + "\\user\\UserConf.RDconf", UserList) < 1) + if (loadBFile(DiceDir + "/user/UserConf.RDconf", UserList) < 1) { map DefaultDice; if (loadFile(strFileLoc + "Default.RDconf", DefaultDice) > 0) @@ -273,7 +288,7 @@ EVE_Enable(eventEnable) } } } - if (loadFile(DiceDir + "\\user\\UserList.txt", UserList) < 1) + if (loadFile(DiceDir + "/user/UserList.txt", UserList) < 1) { set WhiteQQ; if (loadFile(strFileLoc + "WhiteQQ.RDconf", WhiteQQ) > 0) @@ -291,7 +306,7 @@ EVE_Enable(eventEnable) if (console.master())getUser(console.master()).create(NEWYEAR).trust(5); if (UserList.size())console.log("ʼû¼" + to_string(UserList.size()) + "", 1); } - if (loadBFile(DiceDir + "\\user\\ChatConf.RDconf", ChatList) < 1) + if (loadBFile(DiceDir + "/user/ChatConf.RDconf", ChatList) < 1) { set GroupList; if (loadFile(strFileLoc + "DisabledDiscuss.RDconf", GroupList) > 0) @@ -378,7 +393,7 @@ EVE_Enable(eventEnable) } } } - if (loadFile(DiceDir + "\\user\\ChatList.txt", ChatList) < 1) + if (loadFile(DiceDir + "/user/ChatList.txt", ChatList) < 1) { map mLastMsgList; for (const auto& it : mLastMsgList) @@ -401,20 +416,20 @@ EVE_Enable(eventEnable) chat(gid).group().name(gname).reset(""); } blacklist = make_unique(); - if (blacklist->loadJson(DiceDir + "\\conf\\BlackList.json") < 0) + if (blacklist->loadJson(DiceDir + "/conf/BlackList.json") < 0) { blacklist->loadJson(strFileLoc + "BlackMarks.json"); int cnt = blacklist->loadHistory(strFileLoc); if (cnt) { - blacklist->saveJson(DiceDir + "\\conf\\BlackList.json"); + blacklist->saveJson(DiceDir + "/conf/BlackList.json"); console.log("ʼ¼" + to_string(cnt) + "", 1); } } else { - blacklist->loadJson(DiceDir + "\\conf\\BlackListEx.json", true); + blacklist->loadJson(DiceDir + "/conf/BlackListEx.json", true); } fmt = make_unique(); - if (loadJMap(DiceDir + "\\conf\\CustomMsg.json", EditedMsg) < 0)loadJMap(strFileLoc + "CustomMsg.json", EditedMsg); + if (loadJMap(DiceDir + "/conf/CustomMsg.json", EditedMsg) < 0)loadJMap(strFileLoc + "CustomMsg.json", EditedMsg); //Ԥ޸ijظı if (EditedMsg.count("strSelfName"))GlobalMsg["strSelfName"] = EditedMsg["strSelfName"]; for (auto it : EditedMsg) @@ -424,7 +439,7 @@ EVE_Enable(eventEnable) GlobalMsg[it.first] = it.second; } loadData(); - if (loadBFile(DiceDir + "\\user\\PlayerCards.RDconf", PList) < 1) + if (loadBFile(DiceDir + "/user/PlayerCards.RDconf", PList) < 1) { ifstream ifstreamCharacterProp(strFileLoc + "CharacterProp.RDconf"); if (ifstreamCharacterProp) @@ -447,7 +462,7 @@ EVE_Enable(eventEnable) } dataInit(); // ȷִ߳н - while (msgSendThreadRunning)Sleep(10); + while (msgSendThreadRunning)this_thread::sleep_for(10ms); Aws::InitAPI(options); Enabled = true; threads(SendMsg); @@ -455,7 +470,7 @@ EVE_Enable(eventEnable) threads(warningHandler); threads(frqHandler); sch.start(); - console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / 1000) + "", 0b1, + console.log(GlobalMsg["strSelfName"] + "ʼɣʱ" + to_string((clock() - llStartTime) / CLOCKS_PER_SEC) + "", 0b1, printSTNow()); // getDiceList(); @@ -657,7 +672,7 @@ EVE_DiscussMsg_EX(eventDiscussMsg) if (console["LeaveDiscuss"]) { sendDiscussMsg(eve.fromDiscuss, getMsg("strLeaveDiscuss")); - Sleep(1000); + this_thread::sleep_for(1000ms); setDiscussLeave(eve.fromDiscuss); return; } @@ -907,21 +922,27 @@ EVE_Menu(eventMasterMode) { console.isMasterMode = false; console.killMaster(); +#ifdef _WIN32 MessageBoxA(nullptr, "Masterģʽѹرա\nmaster", "Masterģʽл", MB_OK | MB_ICONINFORMATION); +#endif } else { console.isMasterMode = true; console.save(); +#ifdef _WIN32 MessageBoxA(nullptr, "Masterģʽѿ\n﷢.master public/private", "Masterģʽл", MB_OK | MB_ICONINFORMATION); +#endif } return 0; } +#ifdef _WIN32 EVE_Menu(eventGUI) { return GUIMain(); } +#endif void global_exit() { Enabled = false; @@ -939,6 +960,9 @@ void global_exit() { EditedMsg.clear(); blacklist.reset(); Aws::ShutdownAPI(options); +#ifndef _WIN32 + curl_global_cleanup(); +#endif } EVE_Disable(eventDisable) diff --git a/Dice/DiceCensor.cpp b/Dice/DiceCensor.cpp index 85c1d164..08037ff1 100644 --- a/Dice/DiceCensor.cpp +++ b/Dice/DiceCensor.cpp @@ -6,6 +6,7 @@ #include "DiceConsole.h" #include "EncodingConvert.h" #include +#include TrieG wordG; @@ -79,7 +80,7 @@ void Censor::build() { wordG.build(words); } void Censor::save() { - saveJMap(DiceDir + "\\conf\\CustomCensor.json", censor.CustomWords); + saveJMap(DiceDir + "/conf/CustomCensor.json", censor.CustomWords); } int Censor::search(const string& text, unordered_set& res) { diff --git a/Dice/DiceCensor.h b/Dice/DiceCensor.h index a802b10c..f4a7a415 100644 --- a/Dice/DiceCensor.h +++ b/Dice/DiceCensor.h @@ -1,4 +1,5 @@ #pragma once + #include #include "STLExtern.hpp" #include diff --git a/Dice/DiceCloud.cpp b/Dice/DiceCloud.cpp index d7654ab4..1811b487 100644 --- a/Dice/DiceCloud.cpp +++ b/Dice/DiceCloud.cpp @@ -2,10 +2,7 @@ * * Copyright (C) 2019 String.Empty */ -#define WIN32_LEAN_AND_MEAN -#include -#include -#include +#include #include "json.hpp" #include "DiceCloud.h" #include "GlobalVar.h" @@ -16,7 +13,13 @@ #include "DiceMsgSend.h" #include "DiceEvent.h" +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include #pragma comment(lib, "urlmon.lib") +#endif using namespace std; using namespace nlohmann; @@ -30,7 +33,11 @@ namespace Cloud strVer + "&isGlobalOn=" + to_string(!console["DisabledGlobal"]) + "&isPublic=" + to_string(!console["Private"]) + "&isVisible=" + to_string(console["CloudVisible"]); char* frmdata = new char[data.length() + 1]; +#ifdef _MSC_VER strcpy_s(frmdata, data.length() + 1, data.c_str()); +#else + strcpy(frmdata, data.c_str()); +#endif string temp; Network::POST("shiki.stringempty.xyz", "/DiceCloud/update.php", 80, frmdata, temp); //AddMsgToQueue(temp, masterQQ); @@ -40,7 +47,11 @@ namespace Cloud int checkWarning(const char* warning) { char* frmdata = new char[strlen(warning) + 1]; +#ifdef _MSC_VER strcpy_s(frmdata, strlen(warning) + 1, warning); +#else + strcpy(frmdata, warning); +#endif string temp; Network::POST("shiki.stringempty.xyz", "/DiceCloud/warning_check.php", 80, frmdata, temp); delete[] frmdata; @@ -57,10 +68,14 @@ namespace Cloud int DownloadFile(const char* url, const char* downloadPath) { +#ifdef _WIN32 DeleteUrlCacheEntryA(url); if (URLDownloadToFileA(nullptr, url, downloadPath, 0, nullptr) != S_OK) return -1; if (_access(downloadPath, 0))return -2; return 0; +#else + return -1; +#endif } int checkUpdate(FromMsg* msg) diff --git a/Dice/DiceCloud.h b/Dice/DiceCloud.h index 75f0e877..75671776 100644 --- a/Dice/DiceCloud.h +++ b/Dice/DiceCloud.h @@ -1,8 +1,10 @@ +#pragma once + /* * * Copyright (C) 2019 String.Empty */ -#pragma once + class FromMsg; diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 10ab443f..1e579c4e 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -33,6 +33,10 @@ #include "Jsonio.h" #include "BlackListManager.h" #include "DiceSchedule.h" +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif using namespace std; using namespace CQ; @@ -144,7 +148,7 @@ void Console::rmNotice(chatType ct) int Console::log(const std::string& strMsg, int note_lv, const string& strTime) { - ofstream fout(string(DiceDir + "\\audit\\log") + to_string(DiceMaid) + "_" + printDate() + ".txt", + ofstream fout(string(DiceDir + "/audit/log") + to_string(DiceMaid) + "_" + printDate() + ".txt", ios::out | ios::app); fout << strTime << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); @@ -180,7 +184,7 @@ void Console::reset() void Console::loadNotice() { - if (loadFile(DiceDir + "\\conf\\NoticeList.txt", NoticeList) < 1) + if (loadFile(DiceDir + "/conf/NoticeList.txt", NoticeList) < 1) { std::set sChat; if (loadFile(std::string(getAppDirectory()) + "MonitorList.RDconf", sChat) > 0) @@ -189,7 +193,7 @@ void Console::loadNotice() console.setNotice(it, 0b100000); } sChat.clear(); - if (loadFile(DiceDir + "\\conf\\RecorderList.RDconf", sChat) > 0) + if (loadFile(DiceDir + "/conf/RecorderList.RDconf", sChat) > 0) for (const auto& it : sChat) { console.setNotice(it, 0b11011); @@ -209,7 +213,7 @@ void Console::loadNotice() void Console::saveNotice() const { - saveFile(DiceDir + "\\conf\\NoticeList.txt", NoticeList); + saveFile(DiceDir + "/conf/NoticeList.txt", NoticeList); } Console console; @@ -224,25 +228,37 @@ std::map mDiceList; long long llStartTime = clock(); //ǰʱ -SYSTEMTIME stNow{}; -SYSTEMTIME stTmp{}; +tm stNow{}; +tm stTmp{}; std::string printSTNow() { - GetLocalTime(&stNow); + time_t tt = time(nullptr); +#ifdef _MSC_VER + localtime_s(&stNow, &tt); +#else + localtime_r(&tt, &stNow); +#endif return printSTime(stNow); } std::string printDate() { - return to_string(stNow.wYear) + "-" + (stNow.wMonth < 10 ? "0" : "") + to_string(stNow.wMonth) + "-" + ( - stNow.wDay < 10 ? "0" : "") + to_string(stNow.wDay); + return to_string(stNow.tm_year + 1900) + "-" + (stNow.tm_mon + 1 < 10 ? "0" : "") + to_string(stNow.tm_mon + 1) + "-" + ( + stNow.tm_mday < 10 ? "0" : "") + to_string(stNow.tm_mday); } std::string printDate(time_t tt) { tm t{}; - if (!tt || localtime_s(&t, &tt))return R"(????-??-??)"; + if(!tt) return R"(????-??-??)"; +#ifdef _MSC_VER + auto ret = localtime_s(&t, &tt); + if(ret) return R"(????-??-??)"; +#else + auto ret = localtime_r(&tt, &t); + if(!ret) return R"(????-??-??)"; +#endif return to_string(t.tm_year + 1900) + "-" + to_string(t.tm_mon + 1) + "-" + to_string(t.tm_mday); } @@ -260,13 +276,13 @@ string printClock(std::pair clock) return strClock; } -std::string printSTime(const SYSTEMTIME st) +std::string printSTime(const tm st) { - return to_string(st.wYear) + "-" + (st.wMonth < 10 ? "0" : "") + to_string(st.wMonth) + "-" + ( - st.wDay < 10 ? "0" : "") + to_string(st.wDay) + " " + (st.wHour < 10 ? "0" : "") + to_string(st.wHour) + ":" + return to_string(st.tm_year + 1900) + "-" + (st.tm_mon + 1 < 10 ? "0" : "") + to_string(st.tm_mon + 1) + "-" + ( + st.tm_mday < 10 ? "0" : "") + to_string(st.tm_mday) + " " + (st.tm_hour < 10 ? "0" : "") + to_string(st.tm_hour) + ":" + ( - st.wMinute < 10 ? "0" : "") + to_string(st.wMinute) + ":" + (st.wSecond < 10 ? "0" : "") + - to_string(st.wSecond); + st.tm_min < 10 ? "0" : "") + to_string(st.tm_min) + ":" + (st.tm_sec < 10 ? "0" : "") + + to_string(st.tm_sec); } //ӡûdzQQ string printQQ(long long llqq) @@ -312,28 +328,33 @@ void getExceptGroup() { } -bool operator==(const SYSTEMTIME& st, const Console::Clock clock) +bool operator==(const tm& st, const Console::Clock clock) { - return st.wHour == clock.first && st.wHour == clock.second; + return st.tm_hour == clock.first && st.tm_hour == clock.second; } -bool operator<(const Console::Clock clock, const SYSTEMTIME& st) +bool operator<(const Console::Clock clock, const tm& st) { - return st.wHour == clock.first && st.wHour == clock.second; + return st.tm_hour == clock.first && st.tm_hour == clock.second; } //׼ʱ void ConsoleTimer() { - Console::Clock clockNow{stNow.wHour,stNow.wMinute}; + Console::Clock clockNow{stNow.tm_hour,stNow.tm_min}; while (Enabled) { - GetLocalTime(&stNow); + time_t tt = time(nullptr); +#ifdef _MSC_VER + localtime_s(&stNow, &tt); +#else + localtime_r(&tt, &stNow); +#endif //ʱ䶯 - if (stTmp.wMinute != stNow.wMinute) + if (stTmp.tm_min != stNow.tm_min) { stTmp = stNow; - clockNow = {stNow.wHour, stNow.wMinute}; + clockNow = {stNow.tm_hour, stNow.tm_min}; for (const auto& [clock,eve_type] : multi_range(console.mWorkClock, clockNow)) { switch (eve_type) @@ -375,12 +396,16 @@ EVE_Menu(eventGlobalSwitch) if (console["DisabledGlobal"]) { console.set("DisabledGlobal", 0); +#ifdef _WIN32 MessageBoxA(nullptr, "ѽĬ", "ȫֿ", MB_OK | MB_ICONINFORMATION); +#endif } else { console.set("DisabledGlobal", 1); +#ifdef _WIN32 MessageBoxA(nullptr, "ȫ־Ĭ", "ȫֿ", MB_OK | MB_ICONINFORMATION); +#endif } return 0; diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index dd9fffe3..f9fdb753 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -1,15 +1,17 @@ +#pragma once + /* * Copyright (C) 2019-2020 String.Empty */ -#pragma once + #ifndef Dice_Console #define Dice_Console +#include #include #include #include #include #include -#include #include #include #include @@ -186,9 +188,9 @@ void getExceptGroup(); //ʱ extern long long llStartTime; //ǰʱ - extern SYSTEMTIME stNow; + extern tm stNow; std::string printClock(std::pair clock); - std::string printSTime(SYSTEMTIME st); + std::string printSTime(tm st); std::string printSTNow(); std::string printDate(); std::string printDate(time_t tt); diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 8ac9f90f..918717bf 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1,4 +1,4 @@ -#include + #include "DiceEvent.h" #include "Jsonio.h" #include "MsgFormat.h" @@ -13,6 +13,7 @@ #include "DiceNetwork.h" #include "DiceCloud.h" #include +#include using namespace std; using namespace CQ; @@ -1129,7 +1130,7 @@ int FromMsg::DiceReply() fmt->set_help(strVar["key"], strHelpdoc); reply(format(GlobalMsg["strHlpSet"], {strVar["key"]})); } - saveJMap(DiceDir + "\\conf\\CustomHelp.json", CustomHelp); + saveJMap(DiceDir + "/conf/CustomHelp.json", CustomHelp); return true; } if (strLowerMessage.substr(intMsgCnt, 4) == "help") @@ -1316,15 +1317,24 @@ int FromMsg::DiceReply() } if (strOption == "state") { - GetLocalTime(&stNow); + time_t tt = time(nullptr); +#ifdef _MSC_VER + localtime_s(&stNow, &tt); +#else + localtime_r(&tt, &stNow); +#endif +#ifdef _WIN32 double mbFreeBytes = 0, mbTotalBytes = 0; long long milDisk(getDiskUsage(mbFreeBytes, mbTotalBytes)); +#endif ResList res; res << "ʱ:" + printSTime(stNow) +#ifdef _WIN32 << "ڴռ:" + to_string(getRamPort()) + "%" << "CPUռ:" + toString(getWinCpuUsage() / 10.0) + "%" << "Ӳռ:" + toString(milDisk / 10.0) + "%(:" + toString(mbFreeBytes) + "GB/ " + toString(mbTotalBytes) + "GB)" - << "ʱ:" + printDuringTime((clock() - llStartTime) / 1000) +#endif + << "ʱ:" + printDuringTime((clock() - llStartTime) / CLOCKS_PER_SEC) << "ָ:" + to_string(today->get("frq")) << "ָ:" + to_string(FrqMonitor::sumFrqTotal); reply(res.show()); @@ -1358,6 +1368,7 @@ int FromMsg::DiceReply() return 1; } else if (strOption == "remake") { + if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); return -1; @@ -1379,6 +1390,7 @@ int FromMsg::DiceReply() } if (strOption == "rexplorer") { +#ifdef _WIN32 if (trusted < 5 && fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); @@ -1388,9 +1400,11 @@ int FromMsg::DiceReply() system(R"(start %SystemRoot%\explorer.exe)"); this_thread::sleep_for(3s); note("Դ\nǰڴռã" + to_string(getRamPort()) + "%"); +#endif } else if (strOption == "cmd") { +#ifdef _WIN32 if (fromQQ != console.master()) { reply(GlobalMsg["strNotMaster"]); @@ -1400,6 +1414,7 @@ int FromMsg::DiceReply() system(strCMD.c_str()); reply("С"); return 1; +#endif } } else if (strLowerMessage.substr(intMsgCnt, 5) == "admin") @@ -1886,7 +1901,7 @@ int FromMsg::DiceReply() CardDeck::mReplyDeck.erase(strVar["key"]); } else reply(GlobalMsg["strReplySet"], {strVar["key"]}); - saveJMap(DiceDir + "\\conf\\CustomReply.json", CardDeck::mReplyDeck); + saveJMap(DiceDir + "/conf/CustomReply.json", CardDeck::mReplyDeck); return 1; } else if (strLowerMessage.substr(intMsgCnt, 5) == "rules") @@ -2051,7 +2066,7 @@ int FromMsg::DiceReply() else { if (gm->has_session(fromSession) && gm->session(fromSession).has_deck(key)) { - gm->session(fromSession).deck_draw(this); + gm->session(fromSession)._draw(this); return 1; } else if (strVar["deck_name"][0] == '_' || CardDeck::findDeck(strVar["deck_name"]) == 0) @@ -2227,7 +2242,11 @@ int FromMsg::DiceReply() } string data = "QQ=" + to_string(getLoginQQ()) + "&v=20190114" + "&QueryQQ=" + to_string(fromQQ); char* frmdata = new char[data.length() + 1]; +#ifdef _MSC_VER strcpy_s(frmdata, data.length() + 1, data.c_str()); +#else + strcpy(frmdata, data.c_str()); +#endif bool res = Network::POST("api.kokona.tech", "/jrrp", 5555, frmdata, strVar["res"]); delete[] frmdata; if (res) @@ -2612,7 +2631,7 @@ int FromMsg::DiceReply() GlobalMsg[strName] = strMessage; note("Զ" + strName + "ı", 0b1); } - saveJMap(DiceDir + "\\conf\\CustomMsg.json", EditedMsg); + saveJMap(DiceDir + "/conf/CustomMsg.json", EditedMsg); return 1; } else if (strLowerMessage.substr(intMsgCnt, 2) == "en") diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index bdc8c384..12157c88 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -1,3 +1,5 @@ +#pragma once + /* * Ϣ * Copyright (C) 2019 String.Empty @@ -49,7 +51,7 @@ class FromMsg : public DiceJobDetail { void note(std::string strMsg, int note_lv = 0b1) { strMsg = format(strMsg, GlobalMsg, strVar); - ofstream fout(string(DiceDir + "\\audit\\log") + to_string(console.DiceMaid) + "_" + printDate() + ".txt", + ofstream fout(string(DiceDir + "/audit/log") + to_string(console.DiceMaid) + "_" + printDate() + ".txt", ios::out | ios::app); fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index c05cc0ae..35e25531 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -1,8 +1,10 @@ +#pragma once + /* * ļд * Copyright (C) 2019-2020 String.Empty */ -#pragma once + #include #include #include @@ -12,11 +14,10 @@ #include #include #include -#include -#include #include #include "DiceXMLTree.h" #include "StrExtern.hpp" +#include "DiceMsgSend.h" using std::ifstream; @@ -441,6 +442,12 @@ typename std::enable_if::value, void>::type fwrite(ofstream& f void fwrite(ofstream& fout, const std::string& s); +template +void fwrite(ofstream& fout, C& obj) +{ + obj.writeb(fout); +} + template void fwrite(ofstream& fout, C& obj) { @@ -512,7 +519,7 @@ void saveFile(std::string strPath, const unordered_map& mTmp) fout.close(); } -template +template void saveBFile(std::string strPath, std::map& m) { if (clrEmpty(strPath, m))return; @@ -527,6 +534,36 @@ void saveBFile(std::string strPath, std::map& m) fout.close(); } +template +void saveBFile(std::string strPath, std::map& m) +{ + if (clrEmpty(strPath, m))return; + std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); + const int len = m.size(); + fwrite(fout, len); + for (auto& [key, val] : m) + { + fwrite(fout, key); + fwrite(fout, val); + } + fout.close(); +} + +template +void saveBFile(std::string strPath, std::unordered_map& m) +{ + if (clrEmpty(strPath, m))return; + std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); + const int len = m.size(); + fwrite(fout, len); + for (auto& [key, val] : m) + { + fwrite(fout, key); + fwrite(fout, val); + } + fout.close(); +} + template void saveBFile(std::string strPath, std::unordered_map& m) { diff --git a/Dice/DiceGUI.cpp b/Dice/DiceGUI.cpp index df3064ee..3ad6f31c 100644 --- a/Dice/DiceGUI.cpp +++ b/Dice/DiceGUI.cpp @@ -1,4 +1,5 @@ #include "DiceGUI.h" +#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include @@ -634,7 +635,7 @@ LRESULT DiceGUI::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) GlobalMsg[curr] = str; EditedMsg[curr] = str; ListViewCustomMsg.SetItemText(str, ListViewCustomMsgCurrentActivated, 1); - saveJMap(DiceDir + "\\conf\\CustomMsg.json", EditedMsg); + saveJMap(DiceDir + "/conf/CustomMsg.json", EditedMsg); } return 0; case ID_MASTER_BUTTONMASTER: @@ -1163,3 +1164,4 @@ int WINAPI GUIMain() return 0; } +#endif diff --git a/Dice/DiceGUI.h b/Dice/DiceGUI.h index e01b411f..abc5da2a 100644 --- a/Dice/DiceGUI.h +++ b/Dice/DiceGUI.h @@ -1,7 +1,9 @@ #pragma once #ifndef DICE_GUI #define DICE_GUI +#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include int WINAPI GUIMain(); #endif +#endif diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 126df682..62fce63d 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -1,8 +1,11 @@ -#pragma once #include "DiceJob.h" #include "DiceConsole.h" + +#ifdef _WIN32 #include #include +#endif + #include "StrExtern.hpp" #include "CQAPI.h" #include "ManagerSystem.h" @@ -24,8 +27,10 @@ int sendSelf(const string msg) { } void cq_exit(DiceJob& job) { +#ifdef _WIN32 job.note("" + getMsg("self") + "5ɱ", 1); if (frame == QQFrame::CoolQ) { + int pid = _getpid(); PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); @@ -63,8 +68,10 @@ void cq_exit(DiceJob& job) { } keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, 0), 0, 0); } +#endif } +#ifdef _WIN32 inline PROCESSENTRY32 getProcess(int pid) { PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); @@ -72,13 +79,16 @@ inline PROCESSENTRY32 getProcess(int pid) { Process32First(hParentProcess, &pe32); return pe32; } +#endif + void frame_restart(DiceJob& job) { + #ifdef _WIN32 if (!job.fromQQ) { if (console["AutoFrameRemake"] <= 0) { sch.add_job_for(60 * 60, job); return; } - else if (int tWait = console["AutoFrameRemake"] * 60 * 60 - (clock() - llStartTime) / 1000; tWait > 0) { + else if (int tWait = console["AutoFrameRemake"] * 60 * 60 - (clock() - llStartTime) / CLOCKS_PER_SEC; tWait > 0) { sch.add_job_for(tWait, job); return; } @@ -177,9 +187,11 @@ void frame_restart(DiceJob& job) { } */ ShellExecute(NULL, "open", "remake.bat", NULL, NULL, SW_SHOWNORMAL); + #endif } void frame_reload(DiceJob& job){ + #ifdef _WIN32 using cq_reload_type = int(__stdcall*)(int32_t); HMODULE hModule = GetModuleHandleA("CQP.dll"); cq_reload_type cq_reload = (cq_reload_type)GetProcAddress(hModule, "CQ_reload"); @@ -194,19 +206,11 @@ void frame_reload(DiceJob& job){ job.note("" + getMsg("self") + "ɡ", 1); else job.note("" + getMsg("self") + "ʧܡ", 0b10); -} - -void auto_save(DiceJob& job) { - if (sch.is_job_cold("autosave"))return; - dataBackUp(); - console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); - if (console["AutoSaveInterval"] > 0) { - sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"]); - sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); - } + #endif } void check_system(DiceJob& job) { + #ifdef _WIN32 static int perRAM(0), perLastRAM(0); static double perLastCPU(0), perLastDisk(0), perCPU(0), perDisk(0); @@ -260,6 +264,19 @@ void check_system(DiceJob& job) { else { sch.add_job_for(30 * 60, job); } + #endif +} + + + +void auto_save(DiceJob& job) { + if (sch.is_job_cold("autosave"))return; + dataBackUp(); + console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); + if (console["AutoSaveInterval"] > 0) { + sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"]); + sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); + } } //õͼƬб @@ -279,7 +296,7 @@ void clear_image(DiceJob& job) { scanImage(it.second.strConf, sReferencedImage); } job.note("" + GlobalMsg["strSelfName"] + "ͼƬ" + to_string(sReferencedImage.size()) + "", 0b0); - int cnt = clrDir("data\\image\\", sReferencedImage); + int cnt = clrDir("data/image/", sReferencedImage); job.note("imageļ"+ to_string(cnt) + "", 1); if (console["AutoClearImage"] > 0) { sch.refresh_cold("clrimage", time(NULL) + console["AutoClearImage"]); @@ -492,10 +509,8 @@ void dice_update(DiceJob& job) { } } else { - char** path = new char* (); - _get_pgmptr(path); - string strAppPath(*path); - delete path; + // TODO: ΪLinux޸ + string strAppPath(strModulePath); strAppPath = strAppPath.substr(0, strAppPath.find_last_of("\\")) + "\\app\\com.w4123.dice.cpk"; string strURL("https://shiki.stringempty.xyz/DiceVer/" + job.strVar["ver"] + "?" + to_string(job.fromTime)); switch (Cloud::DownloadFile(strURL.c_str(), strAppPath.c_str())) { @@ -545,8 +560,8 @@ void dice_cloudblack(DiceJob& job) { void log_put(DiceJob& job) { job["ret"] = put_s3_object("dicelogger", - job.strVar["log_file"], - job.strVar["log_path"], + job.strVar["log_file"].c_str(), + job.strVar["log_path"].c_str(), "ap-southeast-1"); if (job["ret"] == "SUCCESS") { job.echo(getMsg("strLogUpSuccess", job.strVar)); diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 325e6c8f..9e6ef15d 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -10,8 +10,10 @@ void frame_restart(DiceJob& job); void frame_reload(DiceJob& job); void auto_save(DiceJob& job); + void check_system(DiceJob& job); + void clear_image(DiceJob& job); void clear_group(DiceJob& job); diff --git a/Dice/DiceL5R.h b/Dice/DiceL5R.h index f74515fd..a40eebd9 100644 --- a/Dice/DiceL5R.h +++ b/Dice/DiceL5R.h @@ -25,7 +25,7 @@ class DiceL5R int Randint(int lowest, int highest) { std::mt19937 gen(static_cast(RandomGenerator::GetCycleCount())); - const std::uniform_int_distribution dis(lowest, highest); + std::uniform_int_distribution dis(lowest, highest); return dis(gen); } public: diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 376633d6..bd4bfe1b 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -98,7 +98,7 @@ void DiceModManager::_help(const shared_ptr& job) { } else job->reply("{strHelpNotFound}"); cntHelp[(*job)["help_word"]] += 1; - saveJMap(DiceDir + "\\user\\HelpStatic.json",cntHelp); + saveJMap(DiceDir + "/user/HelpStatic.json",cntHelp); } void DiceModManager::set_help(const string& key, const string& val) @@ -116,7 +116,7 @@ int DiceModManager::load(string& strLog) { vector sFile; vector sFileErr; - int cntFile = listDir(DiceDir + "\\mod\\", sFile, true); + int cntFile = listDir(DiceDir + "/mod/", sFile, true); int cntItem{0}; if (cntFile <= 0)return cntFile; for (auto& filename : sFile) @@ -155,7 +155,7 @@ int DiceModManager::load(string& strLog) factory.detach(); if (cntHelp.empty()) { cntHelp.reserve(helpdoc.size()); - loadJMap(DiceDir + "\\user\\HelpStatic.json", cntHelp); + loadJMap(DiceDir + "/user/HelpStatic.json", cntHelp); } return cntFile; } diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index 7657f98f..9702837e 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -1,8 +1,10 @@ +#pragma once + /* * Դģ * Copyright (C) 2019-2020 String.Empty */ -#pragma once + #include #include #include diff --git a/Dice/DiceMsgSend.h b/Dice/DiceMsgSend.h index 8bdabc7e..8e8174c4 100644 --- a/Dice/DiceMsgSend.h +++ b/Dice/DiceMsgSend.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -20,7 +22,7 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#pragma once + #ifndef DICE_MSG_SEND #define DICE_MSG_SEND #include diff --git a/Dice/DiceNetwork.cpp b/Dice/DiceNetwork.cpp index 556cd100..6e00411a 100644 --- a/Dice/DiceNetwork.cpp +++ b/Dice/DiceNetwork.cpp @@ -20,13 +20,14 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ +#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include - -#ifdef _MSC_VER #pragma comment(lib, "WinInet.lib") -#endif /*_MSC_VER*/ +#else +#include +#endif #include #include "GlobalVar.h" @@ -36,8 +37,19 @@ namespace Network { +#ifndef _WIN32 + CURLcode lastError; + + size_t curlWriteToString(void *contents, size_t size, size_t nmemb, std::string *s) + { + size_t newLength = size*nmemb; + s->append((char*)contents, newLength); + return newLength; + } +#endif std::string getLastErrorMsg() { +#ifdef _WIN32 DWORD dwError = GetLastError(); if (dwError == ERROR_INTERNET_EXTENDED_ERROR) { @@ -83,12 +95,16 @@ namespace Network return ret; } return GlobalMsg["strUnableToGetErrorMsg"]; +#else + return curl_easy_strerror(lastError); +#endif } bool POST(const char* const serverName, const char* const objectName, const unsigned short port, char* const frmdata, std::string& des) { +#ifdef _WIN32 const char* acceptTypes[] = {"*/*", nullptr}; const char* header = "Content-Type: application/x-www-form-urlencoded"; @@ -188,10 +204,33 @@ namespace Network InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); return false; +#else + CURL *curl; + curl = curl_easy_init(); + if (curl) + { + curl_easy_setopt(curl, CURLOPT_URL, (std::string("http://") + serverName + ":" + std::to_string(port) + objectName).c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, frmdata); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteToString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &des); + + lastError = curl_easy_perform(curl); + if (lastError != CURLE_OK) + { + des = getLastErrorMsg(); + } + + curl_easy_cleanup(curl); + return lastError == CURLE_OK; + } + return false; +#endif } bool GET(const char* const serverName, const char* const objectName, const unsigned short port, std::string& des) { +#ifdef _WIN32 const char* acceptTypes[] = {"*/*", nullptr}; const HINTERNET hInternet = InternetOpenA(DiceRequestHeader, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); @@ -290,5 +329,27 @@ namespace Network InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); return false; +#else + CURL *curl; + curl = curl_easy_init(); + if (curl) + { + curl_easy_setopt(curl, CURLOPT_URL, (std::string("http://") + serverName + ":" + std::to_string(port) + objectName).c_str()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteToString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &des); + + lastError = curl_easy_perform(curl); + if (lastError != CURLE_OK) + { + des = getLastErrorMsg(); + } + + curl_easy_cleanup(curl); + return lastError == CURLE_OK; + } + return false; +#endif } + } diff --git a/Dice/DiceNetwork.h b/Dice/DiceNetwork.h index 62e09c61..ab4670ab 100644 --- a/Dice/DiceNetwork.h +++ b/Dice/DiceNetwork.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -20,7 +22,7 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#pragma once + #ifndef DICE_NETWORK #define DICE_NETWORK #include diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 634667ff..29d74d17 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -1,4 +1,6 @@ #include +#define __STDC_WANT_LIB_EXT1__ 1 +#include #include #include #include "GlobalVar.h" @@ -161,9 +163,14 @@ void DiceScheduler::end() { } void DiceToday::daily_clear() { - GetLocalTime(&stNow); - if (stToday.tm_mday != stNow.wDay) { - stToday.tm_mday = stNow.wDay; + time_t tt = time(nullptr); +#ifdef _MSC_VER + localtime_s(&stNow, &tt); +#else + localtime_r(&tt, &stNow); +#endif + if (stToday.tm_mday != stNow.tm_mday) { + stToday.tm_mday = stNow.tm_mday; cntGlobal.clear(); cntUser.clear(); } @@ -180,7 +187,11 @@ void DiceToday::load() { json jFile = freadJson(pathFile); if (jFile.is_null()) { time_t tt = time(nullptr); +#ifdef _MSC_VER localtime_s(&stToday, &tt); +#else + localtime_r(&tt, &stToday); +#endif return; } if (jFile.count("date")) { @@ -197,7 +208,14 @@ void DiceToday::load() { string printTTime(time_t tt) { char tm_buffer[20]; tm t{}; - if (!tt || localtime_s(&t, &tt))return "1970-00-00 00:00:00"; + if(!tt) return "1970-00-00 00:00:00"; +#ifdef _MSC_VER + auto ret = localtime_s(&t, &tt); + if(ret) return "1970-00-00 00:00:00"; +#else + auto ret = localtime_r(&tt, &t); + if(!ret) return "1970-00-00 00:00:00"; +#endif strftime(tm_buffer, 20, "%Y-%m-%d %H:%M:%S", &t); return tm_buffer; } \ No newline at end of file diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index f28e3aa5..b11a346f 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -1,9 +1,11 @@ +#pragma once + /* * Copyright (C) 2019-2020 String.Empty * ʱ¼ * ܼʱɵָ */ -#pragma once + #include #include #include diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index b2535134..1044fcf7 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -105,7 +105,7 @@ void DiceSession::log_new(FromMsg* msg) { logger.fileLog = (type == "solo") ? ("qq_" + to_string(msg->fromQQ) + "_" + to_string(logger.tStart) + ".txt") : ("group_" + to_string(msg->fromGroup) + "_" + to_string(logger.tStart) + ".txt"); - logger.pathLog = DiceDir + LogInfo::dirLog + "\\" + logger.fileLog; + logger.pathLog = DiceDir + LogInfo::dirLog + "/" + logger.fileLog; //ȷϢ msg->reply(GlobalMsg["strLogNew"]); LogList.insert(room); @@ -498,10 +498,10 @@ std::mutex exSessionSave; void DiceSession::save() const { - mkDir(DiceDir + "\\user\\session"); + mkDir(DiceDir + "/user/session"); string pathFile = (type == "solo") - ? (DiceDir + R"(\user\session\Q)" + to_string(~room) + ".json" ) - : (DiceDir + R"(\user\session\)" + to_string(room) + ".json"); + ? (DiceDir + R"(/user/session/Q)" + to_string(~room) + ".json" ) + : (DiceDir + R"(/user/session/)" + to_string(room) + ".json"); nlohmann::json jData; if (!sOB.empty())jData["observer"] = sOB; if (!mTable.empty()) @@ -574,7 +574,7 @@ Session& DiceTableMaster::session(long long group) void DiceTableMaster::session_end(long long group) { std::unique_lock lock(sessionMutex); - remove((DiceDir + R"(\user\session\)" + to_string(group)).c_str()); + remove((DiceDir + R"(/user/session/)" + to_string(group)).c_str()); mSession.erase(group); } @@ -583,7 +583,7 @@ const enumap mSMTag{"type", "room", "gm", "log", "player", "observer", " /* void DiceTableMaster::save() { - mkDir(DiceDir + "\\user\\session"); + mkDir(DiceDir + "/user/session"); std::shared_lock lock(sessionMutex); for (auto [grp, pSession] : mSession) { @@ -597,7 +597,7 @@ int DiceTableMaster::load() string strLog; std::unique_lock lock(sessionMutex); vector sFile; - int cnt = listDir(DiceDir + "\\user\\session\\", sFile); + int cnt = listDir(DiceDir + "/user/session/", sFile); if (cnt <= 0)return cnt; for (auto& filename : sFile) { @@ -617,7 +617,7 @@ int DiceTableMaster::load() jLog["file"].get_to(pSession->logger.fileLog); jLog["logging"].get_to(pSession->logger.isLogging); pSession->logger.update(); - pSession->logger.pathLog = DiceDir + LogInfo::dirLog + "\\" + pSession->logger.fileLog; + pSession->logger.pathLog = DiceDir + LogInfo::dirLog + "/" + pSession->logger.fileLog; if (pSession->logger.isLogging) { LogList.insert(pSession->room); } diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index f014d7b3..0af3a0da 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -18,7 +18,7 @@ class FromMsg; class DiceTableMaster; struct LogInfo{ - static constexpr auto dirLog{ "\\user\\log" }; + static constexpr auto dirLog{ "/user/log" }; bool isLogging{ false }; //ʱ䣬Ϊ0򲻴 time_t tStart{ 0 }; diff --git a/Dice/DiceXMLTree.h b/Dice/DiceXMLTree.h index bbe3df75..7a79ad80 100644 --- a/Dice/DiceXMLTree.h +++ b/Dice/DiceXMLTree.h @@ -1,9 +1,11 @@ +#pragma once + /* * ״ṹ * Copyright (C) 2019-2020 String.Empty * ʵʲϵXMLʽ */ -#pragma once + #include #include #include diff --git a/Dice/EncodingConvert.cpp b/Dice/EncodingConvert.cpp index 05d6b5f3..d05e11ef 100644 --- a/Dice/EncodingConvert.cpp +++ b/Dice/EncodingConvert.cpp @@ -22,18 +22,22 @@ */ #define CP_GBK (936) -#define WIN32_LEAN_AND_MEAN #include "EncodingConvert.h" +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN #include +#else +#include +#endif #include #include #include #include #include -// GBK std::string GBKtoUTF8(const std::string& strGBK) { +#ifdef _WIN32 const int UTF16len = MultiByteToWideChar(CP_GBK, 0, strGBK.c_str(), -1, nullptr, 0); auto* const strUTF16 = new wchar_t[UTF16len]; MultiByteToWideChar(CP_GBK, 0, strGBK.c_str(), -1, strUTF16, UTF16len); @@ -44,6 +48,9 @@ std::string GBKtoUTF8(const std::string& strGBK) delete[] strUTF16; delete[] strUTF8; return strOutUTF8; +#else + return ConvertEncoding(strGBK, "gb18030", "utf-8"); +#endif } std::vector GBKtoUTF8(const std::vector& strGBK) @@ -53,9 +60,9 @@ std::vector GBKtoUTF8(const std::vector& strGBK) return vOutUTF8; } -// ʵGB18030 std::string UTF8toGBK(const std::string& strUTF8) { +#ifdef _WIN32 const int UTF16len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, nullptr, 0); auto* const strUTF16 = new wchar_t[UTF16len]; MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, strUTF16, UTF16len); @@ -66,6 +73,9 @@ std::string UTF8toGBK(const std::string& strUTF8) delete[] strUTF16; delete[] strGBK; return strOutGBK; +#else + return ConvertEncoding(strUTF8, "utf-8", "gb18030"); +#endif } std::vector UTF8toGBK(const std::vector& vUTF8) diff --git a/Dice/EncodingConvert.h b/Dice/EncodingConvert.h index 30d9b470..6b968b0a 100644 --- a/Dice/EncodingConvert.h +++ b/Dice/EncodingConvert.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -20,11 +22,15 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#pragma once + #ifndef DICE_ENCODING_CONVERT #define DICE_ENCODING_CONVERT #include #include +#include +#ifndef _WIN32 +#include +#endif std::string GBKtoUTF8(const std::string& strGBK); std::vector GBKtoUTF8(const std::vector& strGBK); @@ -49,4 +55,34 @@ T UTF8toGBK(T TUTF8) std::string UrlEncode(const std::string& str); std::string UrlDecode(const std::string& str); +#ifndef _WIN32 +template +std::basic_string ConvertEncoding(const std::basic_string& in, const std::string& InEnc, const std::string& OutEnc, const double CapFac = 2.0) +{ + const auto cd = iconv_open(OutEnc.c_str(), InEnc.c_str()); + if (cd == (iconv_t)-1) + { + return std::basic_string(); + } + size_t in_len = in.size() * sizeof(Q); + size_t out_len = size_t(in_len * CapFac + sizeof(T)); + char* in_ptr = const_cast(reinterpret_cast (in.c_str())); + char* out_ptr = new char[out_len](); + + // As out_ptr would be modified by iconv(), store a copy of it pointing to the beginning of the array + char* out_ptr_copy = out_ptr; + if (iconv(cd, &in_ptr, &in_len, &out_ptr, &out_len) == (size_t)-1) + { + delete[] out_ptr_copy; + iconv_close(cd); + return std::basic_string(); + } + memset(out_ptr, 0, sizeof(T)); + std::basic_string ret(reinterpret_cast(out_ptr_copy)); + delete[] out_ptr_copy; + iconv_close(cd); + return ret; +} +#endif + #endif /*DICE_ENCODING_CONVERT*/ diff --git a/Dice/GetRule.cpp b/Dice/GetRule.cpp index b210ab08..681c01ba 100644 --- a/Dice/GetRule.cpp +++ b/Dice/GetRule.cpp @@ -20,14 +20,8 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#define WIN32_LEAN_AND_MEAN -#include - -#ifdef _MSC_VER -#pragma comment(lib, "Wininet.lib") -#endif /*_MSC_VER*/ - #include +#include #include "GetRule.h" #include "GlobalVar.h" #include "EncodingConvert.h" @@ -79,7 +73,11 @@ namespace GetRule data += "&Type=Rules-" + UrlEncode(ruleName); } char* frmdata = new char[data.length() + 1]; +#ifdef _MSC_VER strcpy_s(frmdata, data.length() + 1, data.c_str()); +#else + strcpy(frmdata, data.c_str()); +#endif string temp; const bool reqRes = Network::POST("api.kokona.tech", "/rules", 5555, frmdata, temp); delete[] frmdata; diff --git a/Dice/GetRule.h b/Dice/GetRule.h index a23c3e55..ff7881f1 100644 --- a/Dice/GetRule.h +++ b/Dice/GetRule.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -20,7 +22,7 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#pragma once + #ifndef DICE_GET_RULE #define DICE_GET_RULE #include diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 51554a0d..b3f064ea 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -20,8 +20,10 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ +#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include +#endif #include "CQLogger.h" #include "GlobalVar.h" #include "MsgFormat.h" @@ -34,7 +36,9 @@ std::string Dice_Full_Ver_For = Dice_Full_Ver + " For CoolQ]"; std::string strModulePath; +#ifdef _WIN32 HMODULE hDllModule = nullptr; +#endif bool msgSendThreadRunning = false; diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 45348485..96f39928 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -21,11 +23,13 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#pragma once + #ifndef DICE_GLOBAL_VAR #define DICE_GLOBAL_VAR +#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include +#endif #include "CQLogger.h" #include #include @@ -65,8 +69,10 @@ const std::string Dice_Full_Ver = Dice_Short_Ver + " [UNKNOWN COMPILER"; #endif +#ifdef _WIN32 // DLL hModule extern HMODULE hDllModule; +#endif // ӦǷ extern bool Enabled; diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index d95bf8ce..7182529b 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -1,5 +1,7 @@ -// JsonϢȡԼд #pragma once + +// JsonϢȡԼд + #include #include #include diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index 4941fef5..6659ab24 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -2,8 +2,10 @@ * ̨ϵͳ * Copyright (C) 2019-2020 String.Empty */ -#include -#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif #include #include "ManagerSystem.h" @@ -186,6 +188,8 @@ void scanImage(const vector& v, unordered_set& list) { } } +#ifdef _WIN32 + DWORD getRamPort() { MEMORYSTATUSEX memory_status; @@ -275,4 +279,6 @@ long long getDiskUsage(double& mbFreeBytes, double& mbTotalBytes){ return 1000 - 1000 * i64FreeBytesToCaller / i64TotalBytes; } return 0; -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index 28d2f192..870c2409 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -1,9 +1,11 @@ +#pragma once + /* * ̨ϵͳ * Copyright (C) 2019-2020 String.Empty * û/Ⱥļ¼ͼƬϵͳ */ -#pragma once + #include #include #include @@ -249,7 +251,7 @@ class Chat { if (isGroup)CQ::sendGroupMsg(ID, msg); else CQ::sendDiscussMsg(ID, msg); - Sleep(500); + std::this_thread::sleep_for(500ms); } isGroup ? CQ::setGroupLeave(ID) : CQ::setDiscussLeave(ID); set(""); @@ -389,6 +391,7 @@ void scanImage(const map& m, unordered_set& list) } } +#ifdef _WIN32 DWORD getRamPort(); /*static DWORD getRamPort() { @@ -407,4 +410,5 @@ long long getWinCpuUsage(); long long getProcessCpu(); -long long getDiskUsage(double&, double&); \ No newline at end of file +long long getDiskUsage(double&, double&); +#endif \ No newline at end of file diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index 56e15896..43d6d5ab 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -1,3 +1,5 @@ +#pragma once + /* * _______ ________ ________ ________ __ * | __ \ |__ __| | _____| | _____| | | @@ -20,7 +22,7 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ -#pragma once + #ifndef DICE_MSG_FORMAT #define DICE_MSG_FORMAT #include diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index 5c39ae0d..c1594a0b 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -126,8 +126,8 @@ int FrqMonitor::getFrqTotal() /*EVE_Status_EX(statusUptime) { //ʼ - long long llDuration = clock() / 1000; - //long long llDuration = (clock() - llStartTime) / 1000; + long long llDuration = clock() / CLOCKS_PER_SEC; + //long long llDuration = (clock() - llStartTime) / CLOCKS_PER_SEC; if (llDuration < 0) { eve.data = "N"; eve.dataf = "/A"; @@ -160,7 +160,7 @@ EVE_Status_EX(statusFrq) } //ƽӵƵ const int intFrq = FrqMonitor::getFrqTotal(); - //long long llDuration = (clock() - llStartTime) / 1000; + //long long llDuration = (clock() - llStartTime) / CLOCKS_PER_SEC; if (intFrq < 0) { eve.data = "N"; diff --git a/Dice/RD.h b/Dice/RD.h index 403a4a69..73ea9bac 100644 --- a/Dice/RD.h +++ b/Dice/RD.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "RDConstant.h" #include "RandomGenerator.h" diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index 98092d86..98b3b724 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -24,6 +24,7 @@ #ifndef DICE_RD_CONSTANT #define DICE_RD_CONSTANT #include +#include #include //Error Handle diff --git a/Dice/RandomGenerator.cpp b/Dice/RandomGenerator.cpp index 94b80c44..e7289193 100644 --- a/Dice/RandomGenerator.cpp +++ b/Dice/RandomGenerator.cpp @@ -24,24 +24,38 @@ #include #include +#if defined(__i386__) || defined(__x86_64__) #ifdef _MSC_VER #include #else #include #endif +#endif namespace RandomGenerator { - inline unsigned long long GetCycleCount() + unsigned long long GetCycleCount() { +#if defined(__i386__) || defined(__x86_64__) return __rdtsc(); - // return static_cast (std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); +#else + return static_cast (std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); +#endif } - + +#if defined(__i386__) || defined(__x86_64__) int Randint(int lowest, int highest) { std::mt19937 gen(static_cast(GetCycleCount())); std::uniform_int_distribution dis(lowest, highest); return dis(gen); } +#else + std::mt19937 gen(static_cast(GetCycleCount())); + int Randint(int lowest, int highest) + { + std::uniform_int_distribution dis(lowest, highest); + return dis(gen); + } +#endif } diff --git a/Dice/RandomGenerator.h b/Dice/RandomGenerator.h index 52450372..d5df4f2a 100644 --- a/Dice/RandomGenerator.h +++ b/Dice/RandomGenerator.h @@ -26,7 +26,7 @@ namespace RandomGenerator { - inline unsigned long long GetCycleCount(); + unsigned long long GetCycleCount(); int Randint(int lowest, int highest); } #endif /*DICE_RANDOM_GENERATOR*/ diff --git a/Dice/S3PutObject.cpp b/Dice/S3PutObject.cpp index de670288..e265a349 100644 --- a/Dice/S3PutObject.cpp +++ b/Dice/S3PutObject.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -9,13 +10,8 @@ #include "S3PutObject.h" -#define WIN32_LEAN_AND_MEAN -#include - #include "GlobalVar.h" -// ֹWINAPIAWSSDKͻ -#undef GetMessage // Aws SDK Aws::SDKOptions options; @@ -24,10 +20,8 @@ Aws::Auth::AWSCredentials awsCredentials("", ""); // жļǷ bool file_exists(const std::string& file_name) { - const DWORD dwAttrib = GetFileAttributesA(file_name.c_str()); - - return (dwAttrib != INVALID_FILE_ATTRIBUTES && - !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); + std::error_code ec; + return std::filesystem::is_regular_file(file_name, ec); } // ϴļS3, S3-accelerate @@ -45,6 +39,7 @@ std::string put_s3_object(const Aws::String& s3_bucket_name, Aws::Client::ClientConfiguration clientConfig; //if (!region.empty()) clientConfig.region = region; + clientConfig.verifySSL = false; clientConfig.endpointOverride = "s3-accelerate.amazonaws.com"; // Set up request Aws::S3::S3Client s3_client(awsCredentials, clientConfig); @@ -60,8 +55,8 @@ std::string put_s3_object(const Aws::String& s3_bucket_name, auto put_object_outcome = s3_client.PutObject(object_request); if (!put_object_outcome.IsSuccess()) { const auto& error = put_object_outcome.GetError(); - return "ERROR: " + error.GetExceptionName() + ": " - + error.GetMessage(); + return std::string("ERROR: ") + error.GetExceptionName().c_str() + ": " + + error.GetMessage().c_str(); } return "SUCCESS"; } \ No newline at end of file diff --git a/Dice/SHKQuerier.h b/Dice/SHKQuerier.h index 8148a6ea..6a6b6bd2 100644 --- a/Dice/SHKQuerier.h +++ b/Dice/SHKQuerier.h @@ -4,6 +4,7 @@ * Copyright (C) 2020 String.Empty */ #pragma once +#include #include #include #include diff --git a/Dice/SHKTrie.h b/Dice/SHKTrie.h index eae4556a..49e484f1 100644 --- a/Dice/SHKTrie.h +++ b/Dice/SHKTrie.h @@ -6,6 +6,7 @@ #include #include #include +#include using std::map; using std::unordered_set; using std::string; diff --git a/Dice/dllmain.cpp b/Dice/dllmain.cpp index 614ce13d..70de116b 100644 --- a/Dice/dllmain.cpp +++ b/Dice/dllmain.cpp @@ -20,6 +20,7 @@ * You should have received a copy of the GNU Affero General Public License along with this * program. If not, see . */ +#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include "GlobalVar.h" @@ -32,3 +33,4 @@ BOOL APIENTRY DllMain(HMODULE hModule, hDllModule = hModule; return TRUE; } +#endif \ No newline at end of file diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index a1cfc6ec..a32b6a5e 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -1,9 +1,14 @@ #include #include #include +#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include +#else +#include +#endif #include "StrExtern.hpp" +#include "EncodingConvert.h" using std::string; using std::wstring; @@ -33,25 +38,37 @@ int count_char(const string& s, char ch) string convert_w2a(const wchar_t* wch) { +#ifdef _WIN32 const int len = WideCharToMultiByte(CP_GBK, 0, wch, -1, nullptr, 0, nullptr, nullptr); char* m_char = new char[len]; WideCharToMultiByte(CP_GBK, 0, wch, -1, m_char, len, nullptr, nullptr); std::string str(m_char); delete[] m_char; return str; +#else + return ConvertEncoding(wch, "utf-16le", "gb18030"); +#endif } wstring convert_a2w(const char* ch) { +#ifdef _WIN32 const int len = MultiByteToWideChar(CP_GBK, 0, ch, -1, nullptr, 0); wchar_t* m_char = new wchar_t[len]; MultiByteToWideChar(CP_GBK, 0, ch, -1, m_char, len); std::wstring wstr(m_char); delete[] m_char; return wstr; +#else + return ConvertEncoding(ch, "gb18030", "utf-16le"); +#endif } size_t wstrlen(const char* ch) { +#ifdef _WIN32 return MultiByteToWideChar(CP_GBK, 0, ch, -1, nullptr, 0); +#else + return ConvertEncoding(ch, "gb18030", "utf-16le").length(); +#endif } string printDuringTime(long long seconds) From 292aa57e4f3af26cc43abcbb1b0ec2af3fb9df0d Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 4 Feb 2021 13:21:52 +0800 Subject: [PATCH 051/171] Fix Compilation --- Dice/DiceCloud.cpp | 1 + Dice/DiceEvent.cpp | 22 ++-------------------- Dice/DiceFile.hpp | 3 ++- Dice/DiceJob.cpp | 3 ++- Dice/ManagerSystem.cpp | 2 +- Dice/ManagerSystem.h | 6 ++++++ Dice/S3PutObject.cpp | 3 +++ QQAPI/QQEvent.h | 5 +++++ 8 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Dice/DiceCloud.cpp b/Dice/DiceCloud.cpp index 3e3ac75a..54059f37 100644 --- a/Dice/DiceCloud.cpp +++ b/Dice/DiceCloud.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #pragma comment(lib, "urlmon.lib") #endif diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index d3e5a308..c2505364 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1236,11 +1236,13 @@ int FromMsg::InnerOrder() { return -1; } string strOption = readPara(); +#ifdef _WIN32 if (strOption == "gui") { thread th(GUIMain); th.detach(); return 1; } +#endif if (strOption == "save") { dataBackUp(); note("ֶݡ", 0b1); @@ -2032,28 +2034,8 @@ int FromMsg::InnerOrder() { return 1; } } -<<<<<<< HEAD - string data = "QQ=" + to_string(getLoginQQ()) + "&v=20190114" + "&QueryQQ=" + to_string(fromQQ); - char* frmdata = new char[data.length() + 1]; -#ifdef _MSC_VER - strcpy_s(frmdata, data.length() + 1, data.c_str()); -#else - strcpy(frmdata, data.c_str()); -#endif - bool res = Network::POST("api.kokona.tech", "/jrrp", 5555, frmdata, strVar["res"]); - delete[] frmdata; - if (res) - { - reply(GlobalMsg["strJrrp"], {strVar["nick"], strVar["res"]}); - } - else - { - reply(GlobalMsg["strJrrpErr"], {strVar["res"]}); - } -======= strVar["res"] = to_string(today->getJrrp(fromQQ)); reply(GlobalMsg["strJrrp"], { strVar["nick"], strVar["res"] }); ->>>>>>> 1f74291fe9659cd1ffa3bf51d3e6cf709c15c8d5 return 1; } else if (strLowerMessage.substr(intMsgCnt, 4) == "link") { diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index 1668d896..35ffc5b0 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -478,7 +478,7 @@ void fwrite(ofstream& fout, const std::string& s); void fwrite(ofstream& fout, const var& var); template -void fwrite(ofstream& fout, C& obj) +void fwrite(ofstream& fout, const C& obj) { obj.writeb(fout); } @@ -489,6 +489,7 @@ void fwrite(ofstream& fout, C& obj) obj.writeb(fout); } + template void fwrite(ofstream& fout, const std::map& m) { diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index d9550dac..33ac73e7 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -2,6 +2,8 @@ #include "DiceConsole.h" #ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include #include #include #endif @@ -69,7 +71,6 @@ void frame_reload(DiceJob& job){ job.note("" + getMsg("self") + "ɡ", 1); else job.note("" + getMsg("self") + "ʧܡ", 0b10); - #endif } void check_system(DiceJob& job) { diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index b22f87b4..c961d100 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -154,7 +154,7 @@ void Chat::leave(const string& msg) { if (!msg.empty()) { if (isGroup)DD::sendGroupMsg(ID, msg); else DD::sendDiscussMsg(ID, msg); - Sleep(500); + std::this_thread::sleep_for(500ms); } isGroup ? DD::setGroupLeave(ID) : DD::setDiscussLeave(ID); set(""); diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index ebe23d2e..d027db14 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -15,6 +15,12 @@ #include "DiceFile.hpp" #include "DiceConsole.h" #include "MsgFormat.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + using std::string; using std::to_string; using std::set; diff --git a/Dice/S3PutObject.cpp b/Dice/S3PutObject.cpp index e265a349..3c3f2016 100644 --- a/Dice/S3PutObject.cpp +++ b/Dice/S3PutObject.cpp @@ -12,6 +12,9 @@ #include "GlobalVar.h" +#ifdef _WIN32 +#undef GetMessage +#endif // Aws SDK Aws::SDKOptions options; diff --git a/QQAPI/QQEvent.h b/QQAPI/QQEvent.h index 71f95248..32940a98 100644 --- a/QQAPI/QQEvent.h +++ b/QQAPI/QQEvent.h @@ -1,5 +1,6 @@ #pragma once namespace QQ { +#ifdef _WIN32 #ifdef _MSC_VER #define QQEVENT(ReturnType, Name, Size) __pragma(comment(linker, "/EXPORT:" #Name "=_" #Name "@" #Size))\ extern "C" __declspec(dllexport) ReturnType __stdcall Name @@ -7,6 +8,10 @@ namespace QQ { #define QQEVENT(ReturnType, Name, Size)\ extern "C" __attribute__((dllexport)) ReturnType __attribute__((__stdcall__)) Name #endif /*_MSC_VER*/ +#else +#define QQEVENT(ReturnType, Name, Size)\ + extern "C" __attribute__((visibility ("default"))) ReturnType Name +#endif /* ʼ¼apiȡQQ From fcae0e34b14d93016d08e878aee285f4906f1d80 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sun, 14 Feb 2021 21:17:36 +0800 Subject: [PATCH 052/171] fix addFrq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化对虚拟消息的计量 --- Dice/DiceEvent.cpp | 19 ++++++++++++++----- Dice/DiceJob.cpp | 4 ++++ Dice/DiceSchedule.cpp | 2 +- Dice/MsgMonitor.cpp | 21 ++++++++++++++------- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index c2505364..858f827d 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -3733,6 +3733,8 @@ int FromMsg::CustomReply() if (fromQQ == console.DiceMaid && strAns == strKey)return 0; reply(strAns); if(!isVirtual)AddFrq(fromQQ, fromTime, fromChat); + else + AddFrq(0, fromTime, fromChat); return 1; } return 0; @@ -3777,12 +3779,16 @@ bool FromMsg::DiceFilter() isDisabled = ((console["DisabledGlobal"] && trusted < 4) || groupset(fromGroup, "ЭЧ") > 0); if (BasicOrder()) { - if (isAns) - { - AddFrq(fromQQ, fromTime, fromChat); - getUser(fromQQ).update(fromTime); + if (isAns) { + if (!isVirtual) { + AddFrq(fromQQ, fromTime, fromChat); + getUser(fromQQ).update(fromTime); + if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); + } + else { + AddFrq(0, fromTime, fromChat); + } } - if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); return 1; } if (fromChat.second == msgtype::Group && ((console["CheckGroupLicense"] > 0 && pGrp->isset("δ")) @@ -3798,6 +3804,9 @@ bool FromMsg::DiceFilter() getUser(fromQQ).update(fromTime); if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); } + else { + AddFrq(0, fromTime, fromChat); + } return true; } } diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 33ac73e7..43fec6b9 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -97,6 +97,10 @@ void check_system(DiceJob& job) { //CPU if (console["SystemAlarmCPU"] > 0) { perCPU = getWinCpuUsage() / 10.0; + if (perCPU > 99.9) { + this_thread::sleep_for(10s); + perCPU = getWinCpuUsage() / 10.0; + } if (perCPU > console["SystemAlarmCPU"] && (!isAlarmCPU || perCPU > perLastCPU + 1)) { console.log("棺" + GlobalMsg["strSelfName"] + "ϵͳCPUռô" + toString(perCPU) + "%", 0b1000, printSTime(stNow)); perLastCPU = perCPU; diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 9f9b89a4..c9d82081 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -158,7 +158,7 @@ void DiceScheduler::start() { threads(jobHandle); threads(jobWait); push_job("heartbeat"); - push_job("syscheck"); + add_job_for(60, "syscheck"); if (console["AutoSaveInterval"] > 0)add_job_for(console["AutoSaveInterval"] * 60, "autosave"); if (console["AutoFrameRemake"] > 0) add_job_for(console["AutoFrameRemake"] * 60 * 60, "remake"); diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index 2e1e8d4d..374e0cd3 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -28,9 +28,11 @@ void AddFrq(long long QQ, time_t TT, chatType CT) setFrq.insert(QQ); auto* newFrq = new FrqMonitor(QQ, TT, CT); EarlyMsgQueue.push(newFrq); - FrqMonitor::sumFrqTotal++; - today->inc("frq"); - today->inc(QQ, "frq"); + if (QQ) { + FrqMonitor::sumFrqTotal++; + today->inc("frq"); + today->inc(QQ, "frq"); + } } void frqHandler() @@ -80,7 +82,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT mFrequence[fromQQ] += 10; mCntOrder[fromQQ] += 1; if ((!console["ListenSpam"] || trustedQQ(fromQQ) > 1) && !console.is_self(QQ))return; - if (mFrequence[fromQQ] > 60 && mWarnLevel[fromQQ] < 60) { + if (mFrequence[fromQQ] > 60 && mWarnLevel[fromQQ] < 60 && QQ) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; const std::string strMsg = "ѣ\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) @@ -89,7 +91,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT if(!console.is_self(QQ))AddMsgToQueue(getMsg("strSpamFirstWarning"), CT); console.log(strMsg, 1, printSTNow()); } - else if (mFrequence[fromQQ] > 120 && mWarnLevel[fromQQ] < 120) { + else if (mFrequence[fromQQ] > 120 && mWarnLevel[fromQQ] < 120 && QQ) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; const std::string strMsg = "棺\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) @@ -101,10 +103,15 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT else if (mFrequence[fromQQ] > 200 && mWarnLevel[fromQQ] < 200) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; std::string strNow = printSTNow(); - std::string strNote = (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + - printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + std::string strFrq = to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 60 ? "/5min" : (mCntOrder[fromQQ] > 25 ? "/min" : "/30s")); + if (!QQ) { + console.log("棺" + GlobalMsg["strSelfName"] + "Ƶָ" + strFrq, 0b1000, strNow); + return; + } + std::string strNote = (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + + printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + strFrq; if (console.is_self(QQ)) { console.set("ListenSelfEcho", 0); console.set("ListenGroupEcho", 0); From 0d2de5453ac6f47c67e832454b82005acf15cf21 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 19 Feb 2021 20:17:09 +0800 Subject: [PATCH 053/171] Use cmake for compilation --- CMakeLists.txt | 39 ++++ Dice.sln | 25 --- Dice.sln.DotSettings | 4 - Dice.sln.DotSettings.user | 3 - Dice/Dice.vcxproj | 291 ------------------------- Dice/Dice.vcxproj.DotSettings | 2 - Dice/Dice.vcxproj.filters | 389 ---------------------------------- Dice/Dice.vcxproj.user | 6 - 8 files changed, 39 insertions(+), 720 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Dice.sln delete mode 100644 Dice.sln.DotSettings delete mode 100644 Dice.sln.DotSettings.user delete mode 100644 Dice/Dice.vcxproj delete mode 100644 Dice/Dice.vcxproj.DotSettings delete mode 100644 Dice/Dice.vcxproj.filters delete mode 100644 Dice/Dice.vcxproj.user diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..ebf14c84 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.15) +cmake_policy(SET CMP0091 NEW) +project(Dice) + +include_directories(CQSDK Dice Lua QQAPI) + +aux_source_directory(Dice Dice_SRC) +aux_source_directory(Lua Lua_SRC) +aux_source_directory(CQSDKCPP CQSDK_SRC) +aux_source_directory(QQAPI QQAPI_SRC) + +add_library(w4123.Dice SHARED ${Dice_SRC} ${Lua_SRC} ${CQSDK_SRC} ${QQAPI_SRC}) + +set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD 17) +set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD_REQUIRED ON) + +if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding") + set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ -static-libgcc") +else() + string(REGEX MATCH static IS_STATIC ${VCPKG_TARGET_TRIPLET}) + if(IS_STATIC STREQUAL static) + set_property(TARGET w4123.Dice PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +endif() + +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL Windows) + find_package(CURL REQUIRED) + target_link_libraries(w4123.Dice PRIVATE CURL::libcurl) + find_package(Iconv REQUIRED) + target_link_libraries(w4123.Dice PRIVATE Iconv::Iconv) +endif() + +find_package(OpenSSL REQUIRED) +target_link_libraries(w4123.Dice PRIVATE OpenSSL::SSL OpenSSL::Crypto) +find_package(AWSSDK CONFIG COMPONENTS core s3 REQUIRED) +target_include_directories(w4123.Dice PRIVATE ${AWSSDK_INCLUDE_DIRS}) +target_link_libraries(w4123.Dice PRIVATE ${AWSSDK_LIBRARIES}) \ No newline at end of file diff --git a/Dice.sln b/Dice.sln deleted file mode 100644 index f1c611a7..00000000 --- a/Dice.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30225.117 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "com.w4123.dice", "Dice\Dice.vcxproj", "{BA051175-B8E8-4104-9DD9-B9E225738C42}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Debug|x86.ActiveCfg = Debug|Win32 - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Debug|x86.Build.0 = Debug|Win32 - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Release|x86.ActiveCfg = Release|Win32 - {BA051175-B8E8-4104-9DD9-B9E225738C42}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A83189AF-8958-4DC7-A9D3-3C639C8AEC79} - EndGlobalSection -EndGlobal diff --git a/Dice.sln.DotSettings b/Dice.sln.DotSettings deleted file mode 100644 index 07531f05..00000000 --- a/Dice.sln.DotSettings +++ /dev/null @@ -1,4 +0,0 @@ - - True - True - True \ No newline at end of file diff --git a/Dice.sln.DotSettings.user b/Dice.sln.DotSettings.user deleted file mode 100644 index 99e3685b..00000000 --- a/Dice.sln.DotSettings.user +++ /dev/null @@ -1,3 +0,0 @@ - - True - True \ No newline at end of file diff --git a/Dice/Dice.vcxproj b/Dice/Dice.vcxproj deleted file mode 100644 index 338c4b14..00000000 --- a/Dice/Dice.vcxproj +++ /dev/null @@ -1,291 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - 15.0 - {BA051175-B8E8-4104-9DD9-B9E225738C42} - Win32Proj - Dice - 10.0 - com.w4123.dice - - - - DynamicLibrary - true - v142 - MultiByte - - - DynamicLibrary - false - true - MultiByte - v142 - - - - - - - - - - - - - - - false - MultiThreaded - static - static - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - - - false - static - $(SolutionDir)QQAPI;$(IncludePath) - w4123.Dice - - - true - MultiThreadedDebug - static - static - false - static - w4123.Dice - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)Lua;$(SolutionDir)QQAPI - - - - NotUsing - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;DICE_EXPORTS;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - true - SyncCThrow - true - MultiThreadedDebugDLL - AnySuitable - Speed - true - true - true - Precise - ..\CQSDK;..\Lua;%(AdditionalIncludeDirectories) - true - stdcpp17 - - - Windows - true - true - true - false - UseLinkTimeCodeGeneration - %(ForceSymbolReferences) - NotSet - ..\packages\AWSSDKCPP-Core.1.6.25\build\native\lib\Win32\Release\v141\static;..\packages\AWSSDKCPP-S3.1.6.20060301.25\build\native\lib\Win32\Release\v141\static;%(AdditionalLibraryDirectories) - aws-cpp-sdk-core.lib;aws-cpp-sdk-s3.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - NotUsing - Level3 - Disabled - true - WIN32;_DEBUG;DICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - SyncCThrow - ..\CQSDK;%(AdditionalIncludeDirectories) - MultiThreadedDebug - stdcpp17 - - - Windows - true - ..\packages\AWSSDKCPP-Core.1.6.25\build\native\lib\Win32\Debug\v141\static;..\packages\AWSSDKCPP-S3.1.6.20060301.25\build\native\lib\Win32\Debug\v141\static;%(AdditionalLibraryDirectories) - aws-cpp-sdk-core.lib;aws-cpp-sdk-s3.lib;%(AdditionalDependencies) - 4194304 - - - - - - - - - copy /Y $(TargetPath) "D:\酷Q Air\dev\com.w4123.dice" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 - - - - - - - \ No newline at end of file diff --git a/Dice/Dice.vcxproj.DotSettings b/Dice/Dice.vcxproj.DotSettings deleted file mode 100644 index 21022da7..00000000 --- a/Dice/Dice.vcxproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - Cpp17 \ No newline at end of file diff --git a/Dice/Dice.vcxproj.filters b/Dice/Dice.vcxproj.filters deleted file mode 100644 index 41b2c96c..00000000 --- a/Dice/Dice.vcxproj.filters +++ /dev/null @@ -1,389 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {264cbd68-17fb-46f0-95a7-e30bd02878ac} - - - {9991a6c1-3689-4b27-a14b-c09c80989141} - - - {911e9211-0dd0-42ff-82bc-c6ae90928af6} - - - {f5bad68a-94f2-4074-9c0f-07a82e89f4c1} - - - {fad2c6f3-9fa5-4929-bda1-abc1ebf0f39a} - - - {b57b7ace-ef77-42c8-b7a6-683797f35a8a} - - - {0cd77685-d94c-4a78-a991-b73ea3153780} - - - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件\CQCPP - - - 源文件\CQCPP - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\Lua - - - 源文件\QQAPI - - - 源文件\QQAPI - - - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件\CQHead - - - 头文件 - - - 头文件 - - - 头文件\nlohmann - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件\Lua - - - 头文件\Lua - - - 头文件\Lua - - - 头文件\DDAPI - - - 头文件\DDAPI - - - - - - - 资源文件 - - - - - 资源文件 - - - - - 资源文件 - - - 资源文件 - - - \ No newline at end of file diff --git a/Dice/Dice.vcxproj.user b/Dice/Dice.vcxproj.user deleted file mode 100644 index 7e04c94d..00000000 --- a/Dice/Dice.vcxproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - false - - \ No newline at end of file From a309ad1ea8026d6c4cc93167605d4ba2d4a72d22 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 19 Feb 2021 20:29:00 +0800 Subject: [PATCH 054/171] Update vcpkg.json --- vcpkg.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 vcpkg.json diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000..bf0189d6 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,20 @@ +{ + "name": "dice", + "version-string": "2.5.0", + "dependencies": [ + "openssl", + { + "name": "aws-sdk-cpp", + "features": [ "s3" ] + }, + { + "name": "curl", + "features": [ "openssl" ], + "platform" : "!windows" + }, + { + "name": "libiconv", + "platform" : "!windows" + } + ] +} \ No newline at end of file From 58c5f5cf9b771acede8c2e6fe6d8e542a7faf3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Fri, 19 Feb 2021 20:34:45 +0800 Subject: [PATCH 055/171] Create cmake.yml --- .github/workflows/cmake.yml | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/cmake.yml diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 00000000..eabb5321 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,46 @@ +name: CMake + +on: [push] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + + - name: Test + working-directory: ${{github.workspace}}/build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $BUILD_TYPE From 938a6cc4eb9577cc04c151c2e81c992014b334af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Fri, 19 Feb 2021 20:47:46 +0800 Subject: [PATCH 056/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index eabb5321..94c0a0bf 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -30,7 +30,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-10 -DDCMAKE_C_COMPILER=gcc-10 - name: Build working-directory: ${{github.workspace}}/build From 235ecee0a96deaeb848c33fc3eab744bfe975dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Fri, 19 Feb 2021 23:06:34 +0800 Subject: [PATCH 057/171] Update cmake.yml --- .github/workflows/cmake.yml | 43 ++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 94c0a0bf..d106adc4 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -7,12 +7,49 @@ env: BUILD_TYPE: Release jobs: - build: + build-aarch64: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Install Cross Compiler + run: sudo apt-get install g++-10-aarch64-linux-gnu + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-10-aarch64-linux-gnu -DCMAKE_C_COMPILER=gcc-10-aarch64-linux-gnu -DVCPKG_TARGET_TRIPLET=arm64-linux + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + + - name: Test + working-directory: ${{github.workspace}}/build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $BUILD_TYPE + + build-amd64: # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 @@ -30,7 +67,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-10 -DDCMAKE_C_COMPILER=gcc-10 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_C_COMPILER=gcc-10 -DVCPKG_TARGET_TRIPLET=x64-linux - name: Build working-directory: ${{github.workspace}}/build From 5aa835ed430c30d4b261bedf8fe508e71f10a48c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Fri, 19 Feb 2021 23:22:22 +0800 Subject: [PATCH 058/171] Update cmake.yml --- .github/workflows/cmake.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d106adc4..c2741bca 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -19,7 +19,7 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/build - name: Install Cross Compiler - run: sudo apt-get install g++-10-aarch64-linux-gnu + run: sudo apt-get install g++-aarch64-linux-gnu - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable @@ -29,7 +29,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-10-aarch64-linux-gnu -DCMAKE_C_COMPILER=gcc-10-aarch64-linux-gnu -DVCPKG_TARGET_TRIPLET=arm64-linux + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-aarch64-linux-gnu -DCMAKE_C_COMPILER=gcc-aarch64-linux-gnu -DVCPKG_TARGET_TRIPLET=arm64-linux - name: Build working-directory: ${{github.workspace}}/build From f97c668f21bb4ff21bbdaeab9a706345fc4b7fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Fri, 19 Feb 2021 23:54:43 +0800 Subject: [PATCH 059/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index c2741bca..d0bbd7d9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -29,7 +29,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-aarch64-linux-gnu -DCMAKE_C_COMPILER=gcc-aarch64-linux-gnu -DVCPKG_TARGET_TRIPLET=arm64-linux + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DVCPKG_TARGET_TRIPLET=arm64-linux - name: Build working-directory: ${{github.workspace}}/build From bc65cc389e4e23ab719703261b304dade94d4925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 00:51:32 +0800 Subject: [PATCH 060/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d0bbd7d9..f9361e19 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -29,7 +29,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DVCPKG_TARGET_TRIPLET=arm64-linux + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DVCPKG_TARGET_TRIPLET=arm64-linux -DCMAKE_SYSROOT=/usr/aarch64-linux-gnu - name: Build working-directory: ${{github.workspace}}/build From 93cdf9654731ddc4e10088b137df7b7178437b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 01:05:59 +0800 Subject: [PATCH 061/171] Update cmake.yml --- .github/workflows/cmake.yml | 57 +++++++++++-------------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index f9361e19..1c7d7376 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -7,7 +7,20 @@ env: BUILD_TYPE: Release jobs: - build-aarch64: + build-linux: + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64-linux-gnu + triplet: x64-linux + - arch: i686-linux-gnu + triplet: x86-linux + - arch: aarch64-linux-gnu + triplet: arm64-linux + - arch: arm-linux-gnueabihf + triplet: arm-linux + runs-on: ubuntu-20.04 steps: @@ -19,7 +32,7 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/build - name: Install Cross Compiler - run: sudo apt-get install g++-aarch64-linux-gnu + run: sudo apt-get install g++-${{ matrix.arch }} - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable @@ -29,45 +42,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DVCPKG_TARGET_TRIPLET=arm64-linux -DCMAKE_SYSROOT=/usr/aarch64-linux-gnu - - - name: Build - working-directory: ${{github.workspace}}/build - shell: bash - # Execute the build. You can specify a specific target with "--target " - run: cmake --build . --config $BUILD_TYPE - - - name: Test - working-directory: ${{github.workspace}}/build - shell: bash - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C $BUILD_TYPE - - build-amd64: - # The CMake configure and build commands are platform agnostic and should work equally - # well on Windows or Mac. You can convert this to a matrix build if you need - # cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v2 - - - name: Create Build Environment - # Some projects don't allow in-source building, so create a separate build directory - # We'll use this as our working directory for all subsequent commands - run: cmake -E make_directory ${{github.workspace}}/build - - - name: Configure CMake - # Use a bash shell so we can use the same syntax for environment variable - # access regardless of the host operating system - shell: bash - working-directory: ${{github.workspace}}/build - # Note the current convention is to use the -S and -B options here to specify source - # and build directories, but this is only available with CMake 3.13 and higher. - # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_C_COMPILER=gcc-10 -DVCPKG_TARGET_TRIPLET=x64-linux + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.arch }}-g++ -DCMAKE_C_COMPILER=${{ matrix.arch }}-gcc -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} - name: Build working-directory: ${{github.workspace}}/build From e41ab3517e5d7cc035aa346dc78297cfcb71aa45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 01:13:58 +0800 Subject: [PATCH 062/171] Update cmake.yml --- .github/workflows/cmake.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 1c7d7376..5c3993e1 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -12,13 +12,17 @@ jobs: fail-fast: false matrix: include: - - arch: x86_64-linux-gnu + - apt: g++-9 + cc: gcc-9 + cxx: g++-9 triplet: x64-linux - - arch: i686-linux-gnu - triplet: x86-linux - - arch: aarch64-linux-gnu + - apt: g++-aarch64-linux-gnu + cc: aarch64-linux-gnu-gcc + cxx: aarch64-linux-gnu-g++ triplet: arm64-linux - - arch: arm-linux-gnueabihf + - apt: g++-arm-linux-gnueabihf + cc: arm-linux-gnueabihf-g++ + cxx: arm-linux-gnueabihf-gcc triplet: arm-linux runs-on: ubuntu-20.04 @@ -32,7 +36,7 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/build - name: Install Cross Compiler - run: sudo apt-get install g++-${{ matrix.arch }} + run: sudo apt-get install ${{ matrix.apt }} - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable @@ -42,7 +46,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.arch }}-g++ -DCMAKE_C_COMPILER=${{ matrix.arch }}-gcc -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} - name: Build working-directory: ${{github.workspace}}/build From e664aab86ed1297ce056c66f62c208f0db4bd953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 01:20:08 +0800 Subject: [PATCH 063/171] Update cmake.yml --- .github/workflows/cmake.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 5c3993e1..1f2b1298 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -12,15 +12,18 @@ jobs: fail-fast: false matrix: include: - - apt: g++-9 + - path: / + apt: g++-9 cc: gcc-9 cxx: g++-9 triplet: x64-linux - - apt: g++-aarch64-linux-gnu + - path: /usr/aarch64-linux-gnu + apt: g++-aarch64-linux-gnu cc: aarch64-linux-gnu-gcc cxx: aarch64-linux-gnu-g++ triplet: arm64-linux - - apt: g++-arm-linux-gnueabihf + - path: /usr/arm-linux-gnueabihf + apt: g++-arm-linux-gnueabihf cc: arm-linux-gnueabihf-g++ cxx: arm-linux-gnueabihf-gcc triplet: arm-linux @@ -46,7 +49,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} - name: Build working-directory: ${{github.workspace}}/build From fa7b34225e95102490bcea1a5883f7bb468ade63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 01:31:21 +0800 Subject: [PATCH 064/171] Update cmake.yml --- .github/workflows/cmake.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 1f2b1298..a78ea7bc 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -24,8 +24,8 @@ jobs: triplet: arm64-linux - path: /usr/arm-linux-gnueabihf apt: g++-arm-linux-gnueabihf - cc: arm-linux-gnueabihf-g++ - cxx: arm-linux-gnueabihf-gcc + cc: arm-linux-gnueabihf-gcc + cxx: arm-linux-gnueabihf-g++ triplet: arm-linux runs-on: ubuntu-20.04 From 045fa7311c1bf136e1cfc18abafb55cfa5a1ea33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 01:56:18 +0800 Subject: [PATCH 065/171] Update cmake.yml --- .github/workflows/cmake.yml | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index a78ea7bc..f6b0beaf 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -7,6 +7,50 @@ env: BUILD_TYPE: Release jobs: + build-windows: + strategy: + fail-fast: false + matrix: + include: + - type: Win32 + triplet: x86-windows + - type: x64 + triplet: x64-windows + + runs-on: windows-2019 + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -A {{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET={{ matrix.triplet }} + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + + - name: Test + working-directory: ${{github.workspace}}/build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $BUILD_TYPE + + build-linux: strategy: fail-fast: false From 281e661bceaafb81e7a145dded171ee670a7dd14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 01:59:02 +0800 Subject: [PATCH 066/171] Update cmake.yml --- .github/workflows/cmake.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index f6b0beaf..55a7ed2b 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -28,9 +28,6 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/build - name: Configure CMake - # Use a bash shell so we can use the same syntax for environment variable - # access regardless of the host operating system - shell: bash working-directory: ${{github.workspace}}/build # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. @@ -39,13 +36,11 @@ jobs: - name: Build working-directory: ${{github.workspace}}/build - shell: bash # Execute the build. You can specify a specific target with "--target " run: cmake --build . --config $BUILD_TYPE - name: Test working-directory: ${{github.workspace}}/build - shell: bash # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE From 5663cae90e8d52f63f8742bedd4a9e0c7b9737b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 02:03:02 +0800 Subject: [PATCH 067/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 55a7ed2b..e35604dc 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -32,7 +32,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -A {{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET={{ matrix.triplet }} + run: cmake $GITHUB_WORKSPACE -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} - name: Build working-directory: ${{github.workspace}}/build From 5ef5f082bc66f26c5201997d5d92c3878593a6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 02:18:03 +0800 Subject: [PATCH 068/171] Update cmake.yml --- .github/workflows/cmake.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index e35604dc..3072f0a9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -32,7 +32,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} + run: cmake ${{github.workspace}} -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} - name: Build working-directory: ${{github.workspace}}/build @@ -79,6 +79,15 @@ jobs: - name: Install Cross Compiler run: sudo apt-get install ${{ matrix.apt }} + + - name: Cache Upload + uses: actions/cache@v2.1.4 + with: + # A list of files, directories, and wildcard patterns to cache and restore + path: ${{github.workspace}}/vcpkg_installed + # An explicit key for restoring and saving the cache + key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} + restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable From 352cab8350f7911b5371a0e5da8399ffbf00b50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 02:34:56 +0800 Subject: [PATCH 069/171] Update cmake.yml --- .github/workflows/cmake.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 3072f0a9..9be8377a 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -45,6 +45,43 @@ jobs: # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE + build-macos: + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + triplet: x64-osx + - arch: arm64 + triplet: arm64-osx + + runs-on: macos-11.0 + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Configure CMake + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} + + - name: Build + working-directory: ${{github.workspace}}/build + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + + - name: Test + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $BUILD_TYPE build-linux: strategy: From 96747494cf0a0d880f80f0f06e9b538cae476dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 02:38:24 +0800 Subject: [PATCH 070/171] Update cmake.yml --- .github/workflows/cmake.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 9be8377a..e82f7ca6 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -117,11 +117,11 @@ jobs: - name: Install Cross Compiler run: sudo apt-get install ${{ matrix.apt }} - - name: Cache Upload + - name: Cache uses: actions/cache@v2.1.4 with: # A list of files, directories, and wildcard patterns to cache and restore - path: ${{github.workspace}}/vcpkg_installed + path: ${{github.workspace}}/build/vcpkg_installed # An explicit key for restoring and saving the cache key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- From 3ad42aedc5afa1fc5c5ffdfd9fb9a4510325f492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 03:01:47 +0800 Subject: [PATCH 071/171] Update cmake.yml --- .github/workflows/cmake.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index e82f7ca6..dbce5f18 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -28,6 +28,7 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/build - name: Configure CMake + shell: bash working-directory: ${{github.workspace}}/build # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. @@ -35,11 +36,13 @@ jobs: run: cmake ${{github.workspace}} -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} - name: Build + shell: bash working-directory: ${{github.workspace}}/build # Execute the build. You can specify a specific target with "--target " run: cmake --build . --config $BUILD_TYPE - name: Test + shell: bash working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail From d884e22e05454360999d68314525d90e216d49b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 03:04:00 +0800 Subject: [PATCH 072/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index dbce5f18..84caae09 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -33,7 +33,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake ${{github.workspace}} -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} + run: cmake $GITHUB_WORKSPACE -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} - name: Build shell: bash From 7c5f86b38486f927daec5c0c8f56804c0665a338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 13:18:44 +0800 Subject: [PATCH 073/171] Update cmake.yml --- .github/workflows/cmake.yml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 84caae09..6f885c4d 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -26,6 +26,15 @@ jobs: # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands run: cmake -E make_directory ${{github.workspace}}/build + + - name: Cache + uses: actions/cache@v2.1.4 + with: + # A list of files, directories, and wildcard patterns to cache and restore + path: ${{github.workspace}}/build/vcpkg_installed + # An explicit key for restoring and saving the cache + key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} + restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- - name: Configure CMake shell: bash @@ -58,7 +67,7 @@ jobs: - arch: arm64 triplet: arm64-osx - runs-on: macos-11.0 + runs-on: macos-latest steps: - uses: actions/checkout@v2 @@ -67,6 +76,15 @@ jobs: # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands run: cmake -E make_directory ${{github.workspace}}/build + + - name: Cache + uses: actions/cache@v2.1.4 + with: + # A list of files, directories, and wildcard patterns to cache and restore + path: ${{github.workspace}}/build/vcpkg_installed + # An explicit key for restoring and saving the cache + key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} + restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- - name: Configure CMake working-directory: ${{github.workspace}}/build From 2d75a49b4c1e06b0127f91f0087dcc42c69d8697 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sat, 20 Feb 2021 21:33:18 +0800 Subject: [PATCH 074/171] Fix MacOS Compilation --- CMakeLists.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ebf14c84..a5ae1597 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,15 @@ add_library(w4123.Dice SHARED ${Dice_SRC} ${Lua_SRC} ${CQSDK_SRC} ${QQAPI_SRC}) set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD 17) set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD_REQUIRED ON) -if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding") +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "-fpermissive") set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ -static-libgcc") -else() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding") + set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") string(REGEX MATCH static IS_STATIC ${VCPKG_TARGET_TRIPLET}) if(IS_STATIC STREQUAL static) set_property(TARGET w4123.Dice PROPERTY From 05d60382143e2341513e96c1978437c95832b79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 21:59:32 +0800 Subject: [PATCH 075/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 6f885c4d..b195d8eb 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -91,7 +91,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} + run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} - name: Build working-directory: ${{github.workspace}}/build From 89067f2906301415ebaef1d1ea5ec3e25caf46cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 22:26:51 +0800 Subject: [PATCH 076/171] Update CMakeLists.txt --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ae1597..6fa3340f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,8 @@ add_library(w4123.Dice SHARED ${Dice_SRC} ${Lua_SRC} ${CQSDK_SRC} ${QQAPI_SRC}) set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD 17) set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD_REQUIRED ON) +set_property(TARGET w4123.Dice PEOPERTY PREFIX "") +set_property(TARGET w4123.Dice PEOPERTY SUFFIX ".dll") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "-fpermissive") @@ -41,4 +43,4 @@ find_package(OpenSSL REQUIRED) target_link_libraries(w4123.Dice PRIVATE OpenSSL::SSL OpenSSL::Crypto) find_package(AWSSDK CONFIG COMPONENTS core s3 REQUIRED) target_include_directories(w4123.Dice PRIVATE ${AWSSDK_INCLUDE_DIRS}) -target_link_libraries(w4123.Dice PRIVATE ${AWSSDK_LIBRARIES}) \ No newline at end of file +target_link_libraries(w4123.Dice PRIVATE ${AWSSDK_LIBRARIES}) From fb74b937d321245a8dd1d8604ff8103376431609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sat, 20 Feb 2021 22:27:10 +0800 Subject: [PATCH 077/171] Update CMakeLists.txt --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fa3340f..838792f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,8 @@ add_library(w4123.Dice SHARED ${Dice_SRC} ${Lua_SRC} ${CQSDK_SRC} ${QQAPI_SRC}) set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD 17) set_property(TARGET w4123.Dice PROPERTY CXX_STANDARD_REQUIRED ON) -set_property(TARGET w4123.Dice PEOPERTY PREFIX "") -set_property(TARGET w4123.Dice PEOPERTY SUFFIX ".dll") +set_property(TARGET w4123.Dice PROPERTY PREFIX "") +set_property(TARGET w4123.Dice PROPERTY SUFFIX ".dll") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "-fpermissive") From 99b2c707ed3134604ec0233de0724871c7c7e4ce Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sun, 21 Feb 2021 03:01:21 +0800 Subject: [PATCH 078/171] Add new triplets --- .github/workflows/cmake.yml | 7 ++++++- triplets/arm-android-static.cmake | 6 ++++++ triplets/arm-android.cmake | 6 ++++++ triplets/arm64-android-static.cmake | 6 ++++++ triplets/arm64-android.cmake | 6 ++++++ triplets/x64-android-static.cmake | 6 ++++++ triplets/x64-android.cmake | 6 ++++++ triplets/x86-android-static.cmake | 6 ++++++ triplets/x86-android.cmake | 6 ++++++ triplets/x86-linux.cmake | 10 ++++++++++ 10 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 triplets/arm-android-static.cmake create mode 100644 triplets/arm-android.cmake create mode 100644 triplets/arm64-android-static.cmake create mode 100644 triplets/arm64-android.cmake create mode 100644 triplets/x64-android-static.cmake create mode 100644 triplets/x64-android.cmake create mode 100644 triplets/x86-android-static.cmake create mode 100644 triplets/x86-android.cmake create mode 100644 triplets/x86-linux.cmake diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index b195d8eb..86169351 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -114,6 +114,11 @@ jobs: cc: gcc-9 cxx: g++-9 triplet: x64-linux + - path: /usr/i686-linux-gnu + apt: g++-i686-linux-gnu + cc: i686-linux-gnu-gcc + cxx: i686-linux-gnu-g++ + triplet: x86-linux - path: /usr/aarch64-linux-gnu apt: g++-aarch64-linux-gnu cc: aarch64-linux-gnu-gcc @@ -155,7 +160,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspce }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} - name: Build working-directory: ${{github.workspace}}/build diff --git a/triplets/arm-android-static.cmake b/triplets/arm-android-static.cmake new file mode 100644 index 00000000..ed219a7d --- /dev/null +++ b/triplets/arm-android-static.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE arm) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/arm-android.cmake b/triplets/arm-android.cmake new file mode 100644 index 00000000..07115d9e --- /dev/null +++ b/triplets/arm-android.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE arm) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/arm64-android-static.cmake b/triplets/arm64-android-static.cmake new file mode 100644 index 00000000..c2bfff58 --- /dev/null +++ b/triplets/arm64-android-static.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE arm64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/arm64-android.cmake b/triplets/arm64-android.cmake new file mode 100644 index 00000000..eaee73ff --- /dev/null +++ b/triplets/arm64-android.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE arm64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/x64-android-static.cmake b/triplets/x64-android-static.cmake new file mode 100644 index 00000000..2b7b693d --- /dev/null +++ b/triplets/x64-android-static.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/x64-android.cmake b/triplets/x64-android.cmake new file mode 100644 index 00000000..d5b2dcc6 --- /dev/null +++ b/triplets/x64-android.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/x86-android-static.cmake b/triplets/x86-android-static.cmake new file mode 100644 index 00000000..efb1ecee --- /dev/null +++ b/triplets/x86-android-static.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE x86) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/x86-android.cmake b/triplets/x86-android.cmake new file mode 100644 index 00000000..6ecc4f31 --- /dev/null +++ b/triplets/x86-android.cmake @@ -0,0 +1,6 @@ + +set(VCPKG_TARGET_ARCHITECTURE x86) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) +set(VCPKG_CMAKE_SYSTEM_NAME Android) + diff --git a/triplets/x86-linux.cmake b/triplets/x86-linux.cmake new file mode 100644 index 00000000..527417a6 --- /dev/null +++ b/triplets/x86-linux.cmake @@ -0,0 +1,10 @@ + +set(VCPKG_TARGET_ARCHITECTURE x86) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CMAKE_SYSTEM_NAME Linux) + +if(NOT CMAKE_HOST_SYSTEM_PROCESSOR) + execute_process(COMMAND "uname" "-m" OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_PROCESSOR OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() From 155fdafa709da047acdb266890d01b33635a2663 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sun, 21 Feb 2021 03:05:05 +0800 Subject: [PATCH 079/171] Fix Compilation --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 86169351..b8977af2 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -160,7 +160,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspce }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} - name: Build working-directory: ${{github.workspace}}/build From f6974727744f886a228f361c3e338f1d8165edc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 03:26:11 +0800 Subject: [PATCH 080/171] Update cmake.yml --- .github/workflows/cmake.yml | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index b8977af2..e47c4ad6 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -174,3 +174,55 @@ jobs: # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE + + build-android: + strategy: + fail-fast: false + matrix: + include: + - abi: armeabi-v7a + triplet: arm-android + - abi: arm64-v8a + triplet: arm64-android + - abi: x86 + triplet: x86-android + - abi: x86_64 + triplet: x64-android + + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Cache + uses: actions/cache@v2.1.4 + with: + # A list of files, directories, and wildcard patterns to cache and restore + path: ${{github.workspace}}/build/vcpkg_installed + # An explicit key for restoring and saving the cache + key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} + restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=21 + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + + - name: Test + working-directory: ${{github.workspace}}/build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $BUILD_TYPE + From 4ab3837782bcd8790bcdcd3dfa3cbcd5c6a29306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 03:53:52 +0800 Subject: [PATCH 081/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index e47c4ad6..b88a7baf 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -176,6 +176,8 @@ jobs: run: ctest -C $BUILD_TYPE build-android: + env: + ANDROID_NDK: $ANDROID_NDK_HOME strategy: fail-fast: false matrix: From 0281ab978cf9c6feeb46a696b55cd9f33fb8f9f7 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sun, 21 Feb 2021 13:39:11 +0800 Subject: [PATCH 082/171] Fix x64 build --- QQAPI/QQEvent.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/QQAPI/QQEvent.h b/QQAPI/QQEvent.h index 32940a98..2a0327d1 100644 --- a/QQAPI/QQEvent.h +++ b/QQAPI/QQEvent.h @@ -1,6 +1,6 @@ #pragma once namespace QQ { -#ifdef _WIN32 +#if (defined _WIN32 && !defined _WIN64) #ifdef _MSC_VER #define QQEVENT(ReturnType, Name, Size) __pragma(comment(linker, "/EXPORT:" #Name "=_" #Name "@" #Size))\ extern "C" __declspec(dllexport) ReturnType __stdcall Name @@ -8,6 +8,14 @@ namespace QQ { #define QQEVENT(ReturnType, Name, Size)\ extern "C" __attribute__((dllexport)) ReturnType __attribute__((__stdcall__)) Name #endif /*_MSC_VER*/ +#elif defined _WIN64 +#ifdef _MSC_VER +#define QQEVENT(ReturnType, Name, Size)\ + extern "C" __declspec(dllexport) ReturnType __stdcall Name +#else +#define QQEVENT(ReturnType, Name, Size)\ + extern "C" __attribute__((dllexport)) Name +#endif /*_MSC_VER*/ #else #define QQEVENT(ReturnType, Name, Size)\ extern "C" __attribute__((visibility ("default"))) ReturnType Name From f8d1dbba64e61d26b33ede61cbf6422128050734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 13:52:17 +0800 Subject: [PATCH 083/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index b88a7baf..3a47f8ce 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -114,7 +114,7 @@ jobs: cc: gcc-9 cxx: g++-9 triplet: x64-linux - - path: /usr/i686-linux-gnu + - path: / apt: g++-i686-linux-gnu cc: i686-linux-gnu-gcc cxx: i686-linux-gnu-g++ From 847a3ecf15c8e62c07a7e5d7213a304a1d631832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 14:34:12 +0800 Subject: [PATCH 084/171] Update cmake.yml --- .github/workflows/cmake.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 3a47f8ce..18158ab5 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -109,15 +109,15 @@ jobs: fail-fast: false matrix: include: - - path: / - apt: g++-9 + - apt: g++-9 cc: gcc-9 cxx: g++-9 triplet: x64-linux - - path: / - apt: g++-i686-linux-gnu - cc: i686-linux-gnu-gcc - cxx: i686-linux-gnu-g++ + - apt: g++-multilib + cc: gcc-9 + cxx: g++-9 + ccarg: -m32 + cxxarg: -m32 triplet: x86-linux - path: /usr/aarch64-linux-gnu apt: g++-aarch64-linux-gnu @@ -160,7 +160,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} -DCMAKE_C_FLAGS=${{ matrix.ccarg }} -DCMAKE_CXX_FLAGS=${{ matrix.cxxarg }} - name: Build working-directory: ${{github.workspace}}/build From 00549dd6e16811b079cbf2cfaff228174ea56e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 15:00:05 +0800 Subject: [PATCH 085/171] Update cmake.yml --- .github/workflows/cmake.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 18158ab5..1439ff14 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -113,11 +113,10 @@ jobs: cc: gcc-9 cxx: g++-9 triplet: x64-linux - - apt: g++-multilib - cc: gcc-9 - cxx: g++-9 - ccarg: -m32 - cxxarg: -m32 + - path: /usr/i686-linux-gnu + apt: g++-i686-linux-gnu + cc: i686-linux-gnu-gcc + cxx: i686-linux-gnu-g++ triplet: x86-linux - path: /usr/aarch64-linux-gnu apt: g++-aarch64-linux-gnu @@ -160,7 +159,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} -DCMAKE_C_FLAGS=${{ matrix.ccarg }} -DCMAKE_CXX_FLAGS=${{ matrix.cxxarg }} + run: CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} - name: Build working-directory: ${{github.workspace}}/build From 84e81e0e8bc6444fb7279d2fc8081371ad3173ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 15:17:48 +0800 Subject: [PATCH 086/171] Update cmake.yml --- .github/workflows/cmake.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 1439ff14..466ccbe6 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -1,6 +1,6 @@ name: CMake -on: [push] +on: [push, pull_request] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) @@ -13,9 +13,9 @@ jobs: matrix: include: - type: Win32 - triplet: x86-windows + triplet: x86-windows-static - type: x64 - triplet: x64-windows + triplet: x64-windows-static runs-on: windows-2019 @@ -182,13 +182,13 @@ jobs: matrix: include: - abi: armeabi-v7a - triplet: arm-android + triplet: arm-android-static - abi: arm64-v8a - triplet: arm64-android + triplet: arm64-android-static - abi: x86 - triplet: x86-android + triplet: x86-android-static - abi: x86_64 - triplet: x64-android + triplet: x64-android-static runs-on: ubuntu-20.04 From baaa760979d8981c905a68aeafc917077c7dd315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 15:18:36 +0800 Subject: [PATCH 087/171] [skip ci] Update CMakeLists.txt --- CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 838792f7..328537b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,13 +17,13 @@ set_property(TARGET w4123.Dice PROPERTY PREFIX "") set_property(TARGET w4123.Dice PROPERTY SUFFIX ".dll") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "-fpermissive") - set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ -static-libgcc") + set(CMAKE_CXX_FLAGS "-fpermissive ${CMAKE_CXX_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ -static-libgcc ${CMAKE_SHARED_LINKER_FLAGS}") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding") + set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding ${CMAKE_CXX_FLAGS}") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding") - set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++") + set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding ${CMAKE_CXX_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ ${CMAKE_SHARED_LINKER_FLAGS}") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") string(REGEX MATCH static IS_STATIC ${VCPKG_TARGET_TRIPLET}) if(IS_STATIC STREQUAL static) From 7c765b1a25ccb7b86cb9dd096b8dffc02e985298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 15:36:49 +0800 Subject: [PATCH 088/171] Update cmake.yml --- .github/workflows/cmake.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 466ccbe6..f3ea54b0 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -56,6 +56,14 @@ jobs: # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE + + - name: Upload + uses: actions/upload-artifact@v2.2.2 + with: + # Artifact name + name: ${{ matrix.triplet }} + # A file, directory or wildcard pattern that describes what to upload + path: "**/w4123.Dice.dll" build-macos: strategy: @@ -103,6 +111,14 @@ jobs: # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE + + - name: Upload + uses: actions/upload-artifact@v2.2.2 + with: + # Artifact name + name: ${{ matrix.triplet }} + # A file, directory or wildcard pattern that describes what to upload + path: "**/w4123.Dice.dll" build-linux: strategy: @@ -173,6 +189,14 @@ jobs: # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE + + - name: Upload + uses: actions/upload-artifact@v2.2.2 + with: + # Artifact name + name: ${{ matrix.triplet }} + # A file, directory or wildcard pattern that describes what to upload + path: "**/w4123.Dice.dll" build-android: env: @@ -226,4 +250,12 @@ jobs: # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE + + - name: Upload + uses: actions/upload-artifact@v2.2.2 + with: + # Artifact name + name: ${{ matrix.triplet }} + # A file, directory or wildcard pattern that describes what to upload + path: "**/w4123.Dice.dll" From 80b84ce964e271cf6196e5740dd15703b3412e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 21 Feb 2021 17:35:24 +0800 Subject: [PATCH 089/171] [skip ci] Update Readme.md --- README.md | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 62292bae..cbdb8e47 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,13 @@ QQ Dice Robot For TRPG Based on CoolQ/Mirai/XQ [![License](https://img.shields.io/github/license/Dice-Developer-Team/Dice.svg)](http://www.gnu.org/licenses) -[![Build status](https://ci.appveyor.com/api/projects/status/7uq2qi3348ny1tfv?svg=true)](https://ci.appveyor.com/project/w4123/dice-ovf7o) [![Downloads](https://img.shields.io/github/downloads/Dice-Developer-Team/dice/total.svg)](https://github.com/Dice-Developer-Team/Dice/releases) [![GitHub contributors](https://img.shields.io/github/contributors/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/graphs/contributors) [![GitHub last commit](https://img.shields.io/github/last-commit/Dice-Developer-Team/dice.svg)](https://github.com/Dice-Developer-Team/Dice/commits) ## 简介 -Dice!是一款基于酷Q的QQ跑团掷骰机器人 交流QQ群:882747577或941980833或624807593(已满) 也可在MiraiNative或CQXQ等CQ兼容性况下运行 - -主页: +Dice!是一款基于酷Q/XQ/Mirai/XLZ的QQ跑团掷骰机器人 论坛: @@ -27,15 +24,9 @@ Latest Release: [![GitHub release](https://img.shields.io/github/release-pre/Dic ## 编译须知 -请使用最新版Visual Studio 2019或以上版本 (或其独立编译器)进行编译, 项目主文件为Dice.sln - -新增: 现在可以用GCC/Clang编译, 只测试了几个版本, 编译出现问题请反馈, 下面列出编译选项, 正在写cmake - -- GCC(9.0.0+): ` g++ -shared -static -std=c++17 -O2 -o com.w4123.dice.dll -Wl,--kill-at -I CQSDK\ -I Dice\ CQSDKCPP\*.cpp Dice\*.cpp Dice\CQP.lib -pthread -lWinInet -luser32 ` -- Clang(4+)+MSVC(VS2019+): ` clang-cl --target=i686-pc-windows-msvc /MT /O2 /EHsc /std:c++17 /D "UNICODE" /LD /link "user32.lib" /o com.w4123.dice.dll /I CQSDK\ /I Dice\ CQSDKCPP\*.cpp Dice\*.cpp Dice\CQP.lib -Wno-invalid-source-encoding ` -- Clang(4+)+GCC(9.0.0+): ` clang++ --target=i686-pc-windows-gnu -m32 -shared -static -o com.w4123.dice.dll -Xclang -flto-visibility-public-std -Wl,--kill-at -std=c++17 -O2 -I CQSDK\ -I Dice\ CQSDKCPP\*.cpp Dice\*.cpp Dice\CQP.lib -lWinInet -luser32 -pthread -Wno-invalid-source-encoding ` +请安装好3.15版本或更高的cmake,并且安装好vcpkg,使用```cmake -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake . && cmake --build .```等进行编译。你可能需要设置```CC```, ```CXX```, ```VCPKG_TARGET_TRIPLET```等环境变量到对应的参数才能正常编译。 -编译后会得到com.w4123.dice.dll文件! 请从Releases中下载对应的json文件(或自己编写), 放至酷Q/MN/XQ 对应文件夹下使用 +在大多数情况下,你不需要自行编译。你可以在Github Releases 以及 Github Actions中找到编译好的二进制版本。 ## Issue提交 @@ -45,7 +36,7 @@ Latest Release: [![GitHub release](https://img.shields.io/github/release-pre/Dic Dice! QQ Dice Robot for TRPG -Copyright (C) 2018-2020 w4123溯洄 Shiki +Copyright (C) 2018-2021 w4123溯洄 Shiki This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, From e563ac4522d969549fad123aa9552f33301bdfd9 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 23 Feb 2021 09:48:57 +0800 Subject: [PATCH 090/171] Add vcpkg as submodule --- .github/workflows/cmake.yml | 8 ++++---- .gitmodules | 3 +++ CMakeLists.txt | 2 ++ vcpkg | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .gitmodules create mode 160000 vcpkg diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index f3ea54b0..80e195b9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -42,7 +42,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} + run: cmake $GITHUB_WORKSPACE -A ${{ matrix.type }} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} - name: Build shell: bash @@ -99,7 +99,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} + run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} - name: Build working-directory: ${{github.workspace}}/build @@ -175,7 +175,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} + run: CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_PREFIX_PATH=${{ matrix.path }} - name: Build working-directory: ${{github.workspace}}/build @@ -236,7 +236,7 @@ jobs: - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=21 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=21 - name: Build working-directory: ${{github.workspace}}/build diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..fe07c999 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vcpkg"] + path = vcpkg + url = https://github.com/microsoft/vcpkg diff --git a/CMakeLists.txt b/CMakeLists.txt index 328537b3..c81206a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) +set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake + CACHE STRING "Vcpkg toolchain file") project(Dice) include_directories(CQSDK Dice Lua QQAPI) diff --git a/vcpkg b/vcpkg new file mode 160000 index 00000000..319b8f06 --- /dev/null +++ b/vcpkg @@ -0,0 +1 @@ +Subproject commit 319b8f06ebbf8f524df78b4578fb0efeb6772c50 From dbc6157831471722ae36a3b56fc979d67196cbab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 23 Feb 2021 09:55:58 +0800 Subject: [PATCH 091/171] Update cmake.yml --- .github/workflows/cmake.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 80e195b9..cfcf32b7 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,6 +21,8 @@ jobs: steps: - uses: actions/checkout@v2 + with: + submodules: 'recursive' - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory @@ -79,6 +81,9 @@ jobs: steps: - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory @@ -149,6 +154,8 @@ jobs: steps: - uses: actions/checkout@v2 + with: + submodules: 'recursive' - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory @@ -218,6 +225,8 @@ jobs: steps: - uses: actions/checkout@v2 + with: + submodules: 'recursive' - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory From bb3eaf847090c03e8af38c80829cf552306c7f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 23 Feb 2021 10:11:00 +0800 Subject: [PATCH 092/171] Update cmake.yml --- .github/workflows/cmake.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index cfcf32b7..169daba7 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -38,6 +38,11 @@ jobs: key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- + - name: Setup Vcpkg + shell: cmd + working-directory: ${{github.workspace}}/vcpkg + run: bootstrap-vcpkg.cmd + - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build @@ -83,8 +88,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: 'recursive' - - + - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands @@ -98,6 +102,11 @@ jobs: # An explicit key for restoring and saving the cache key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- + + - name: Setup Vcpkg + shell: bash + working-directory: ${{github.workspace}}/vcpkg + run: ./bootstrap-vcpkg.sh - name: Configure CMake working-directory: ${{github.workspace}}/build @@ -173,6 +182,11 @@ jobs: # An explicit key for restoring and saving the cache key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- + + - name: Setup Vcpkg + shell: bash + working-directory: ${{github.workspace}}/vcpkg + run: ./bootstrap-vcpkg.sh - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable @@ -242,6 +256,11 @@ jobs: key: ${{ runner.os }}-${{ matrix.triplet }}-${{ hashFiles('**/vcpkg.json') }} restore-keys: ${{ runner.os }}-${{ matrix.triplet }}- + - name: Setup Vcpkg + shell: bash + working-directory: ${{github.workspace}}/vcpkg + run: ./bootstrap-vcpkg.sh + - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build From 7b30d3f5aedb9dc96c4fe22fbc72b47c561cd4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 23 Feb 2021 10:18:01 +0800 Subject: [PATCH 093/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 169daba7..73a9f243 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -41,7 +41,7 @@ jobs: - name: Setup Vcpkg shell: cmd working-directory: ${{github.workspace}}/vcpkg - run: bootstrap-vcpkg.cmd + run: bootstrap-vcpkg.bat - name: Configure CMake shell: bash From a62c3bf28827f891d63c78cff7708fd6dccfeaeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 23 Feb 2021 11:39:00 +0800 Subject: [PATCH 094/171] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cbdb8e47..8bcc3683 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,11 @@ Latest Release: [![GitHub release](https://img.shields.io/github/release-pre/Dic ## 编译须知 -请安装好3.15版本或更高的cmake,并且安装好vcpkg,使用```cmake -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake . && cmake --build .```等进行编译。你可能需要设置```CC```, ```CXX```, ```VCPKG_TARGET_TRIPLET```等环境变量到对应的参数才能正常编译。 +1. 安装CMake(v3.15+), git和一个合适的支持C++17的编译器 +2. clone此repo,请注意此repo含有submodule,```git clone https://github.com/Dice-Developer-Team/Dice --recursive``` +3. 如果你在Cross-compile,请先执行```./vcpkg/bootstrap-vcpkg.sh```,以防止Cross-compiler冲突。如果你没在Cross-compile,你可以直接运行下一步,vcpkg会自动被安装。 +4. 执行```cmake .```,如果你在交叉编译,你可能需要设置```CC```, ```CXX```, ```VCPKG_TARGET_TRIPLET```等环境变量到对应的参数才能正常编译。 +5. 执行```cmake --build .```完成编译 在大多数情况下,你不需要自行编译。你可以在Github Releases 以及 Github Actions中找到编译好的二进制版本。 From a30524c8b7ff2f9d1765493563c037be0a79b21b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 23 Feb 2021 14:41:49 +0800 Subject: [PATCH 095/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 73a9f243..50a802c6 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -16,6 +16,8 @@ jobs: triplet: x86-windows-static - type: x64 triplet: x64-windows-static + - type: ARM64 + triplet: arm64-windows-static runs-on: windows-2019 From b1f1fa631b29c7f46812942544087939865891f5 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 26 Feb 2021 10:30:16 +0800 Subject: [PATCH 096/171] Fix encoding bug --- Dice/DiceFile.cpp | 2 +- Dice/DiceFile.hpp | 2 +- Dice/Jsonio.cpp | 4 ++-- Dice/StrExtern.hpp | 6 +++--- Dice/strExtern.cpp | 20 ++++++++++---------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Dice/DiceFile.cpp b/Dice/DiceFile.cpp index 9cb32b99..82e549ca 100644 --- a/Dice/DiceFile.cpp +++ b/Dice/DiceFile.cpp @@ -26,7 +26,7 @@ int clrDir(const std::string& dir, const std::unordered_set& except { if (p.is_regular_file()) { - std::string path = convert_w2a(p.path().filename().wstring().c_str()); + std::string path = convert_w2a(p.path().filename().u16string().c_str()); if (path.length() >= 36 && !exceptList.count(path)) { std::error_code err2; diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index 35ffc5b0..13fee3c4 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -382,7 +382,7 @@ int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2 if (p.is_regular_file()) { intFile++; - string path = convert_w2a(p.path().filename().wstring().c_str()); + string path = convert_w2a(p.path().filename().u16string().c_str()); const int Cnt = load(strDir + path, tmp); if (Cnt < 0) { diff --git a/Dice/Jsonio.cpp b/Dice/Jsonio.cpp index 55b9d239..28ee2993 100644 --- a/Dice/Jsonio.cpp +++ b/Dice/Jsonio.cpp @@ -21,7 +21,7 @@ nlohmann::json freadJson(const std::string& strPath) nlohmann::json freadJson(const std::filesystem::path& path) { - std::ifstream fin(convert_w2a(path.wstring().c_str())); + std::ifstream fin(path); if (!fin)return nlohmann::json(); nlohmann::json j; try @@ -39,4 +39,4 @@ void fwriteJson(std::string strPath, const json& j) { std::ofstream fout(strPath); fout << std::setw(2) << j; -} \ No newline at end of file +} diff --git a/Dice/StrExtern.hpp b/Dice/StrExtern.hpp index 11fbe3c4..52c584e6 100644 --- a/Dice/StrExtern.hpp +++ b/Dice/StrExtern.hpp @@ -7,7 +7,7 @@ #include using std::string; -using std::wstring; +using std::u16string; using std::to_string; #define CP_GBK (936) @@ -36,9 +36,9 @@ string to_signed_string(Dig num) int count_char(const string& s, char ch); -string convert_w2a(const wchar_t* wch); +string convert_w2a(const char16_t* wch); -wstring convert_a2w(const char* ch); +u16string convert_a2w(const char* ch); string printDuringTime(long long); diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index a32b6a5e..5b5be3b1 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -11,7 +11,7 @@ #include "EncodingConvert.h" using std::string; -using std::wstring; +using std::u16string; using std::string_view; using std::to_string; @@ -36,38 +36,38 @@ int count_char(const string& s, char ch) return std::count(s.begin(), s.end(), ch); } -string convert_w2a(const wchar_t* wch) +string convert_w2a(const char16_t* wch) { #ifdef _WIN32 - const int len = WideCharToMultiByte(CP_GBK, 0, wch, -1, nullptr, 0, nullptr, nullptr); + const int len = WideCharToMultiByte(CP_GBK, 0, (const wchar_t*)wch, -1, nullptr, 0, nullptr, nullptr); char* m_char = new char[len]; - WideCharToMultiByte(CP_GBK, 0, wch, -1, m_char, len, nullptr, nullptr); + WideCharToMultiByte(CP_GBK, 0, (const wchar_t*)wch, -1, m_char, len, nullptr, nullptr); std::string str(m_char); delete[] m_char; return str; #else - return ConvertEncoding(wch, "utf-16le", "gb18030"); + return ConvertEncoding(wch, "utf-16le", "gb18030"); #endif } -wstring convert_a2w(const char* ch) +u16string convert_a2w(const char* ch) { #ifdef _WIN32 const int len = MultiByteToWideChar(CP_GBK, 0, ch, -1, nullptr, 0); wchar_t* m_char = new wchar_t[len]; MultiByteToWideChar(CP_GBK, 0, ch, -1, m_char, len); - std::wstring wstr(m_char); + std::u16string wstr((char16_t*)m_char); delete[] m_char; return wstr; #else - return ConvertEncoding(ch, "gb18030", "utf-16le"); + return ConvertEncoding(ch, "gb18030", "utf-16le"); #endif } size_t wstrlen(const char* ch) { #ifdef _WIN32 return MultiByteToWideChar(CP_GBK, 0, ch, -1, nullptr, 0); #else - return ConvertEncoding(ch, "gb18030", "utf-16le").length(); + return ConvertEncoding(ch, "gb18030", "utf-16le").length(); #endif } @@ -92,4 +92,4 @@ string printDuringTime(long long seconds) int days = hours / 24; hours = hours % 24; return std::to_string(days) + "" + std::to_string(hours) + "Сʱ" + std::to_string(mins) + "" + std::to_string(seconds) + ""; -} \ No newline at end of file +} From 832c63d63bd48cbcf002cb13cbcedd26b64b0e1d Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 26 Feb 2021 10:55:26 +0800 Subject: [PATCH 097/171] Update submodule --- .gitmodules | 2 +- vcpkg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index fe07c999..ed4f1eb6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "vcpkg"] path = vcpkg - url = https://github.com/microsoft/vcpkg + url = https://github.com/w4123/vcpkg diff --git a/vcpkg b/vcpkg index 319b8f06..f439a8e9 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 319b8f06ebbf8f524df78b4578fb0efeb6772c50 +Subproject commit f439a8e96a5e3648adc7205eb102960bc44f4149 From 4cfa4aae53d306ee17398d4d30549cdc39e567d1 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 3 Mar 2021 20:49:18 +0800 Subject: [PATCH 098/171] fix cloud MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复后台接口bug与event取{nick}时点bug --- Dice/DiceEvent.cpp | 7 ++++++- QQAPI/DDAPI.cpp | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 858f827d..026a662a 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -2536,6 +2536,7 @@ int FromMsg::InnerOrder() { intMsgCnt += 2; while (isspace(static_cast(strMsg[intMsgCnt]))) intMsgCnt++; + strVar["nick"] = getName(fromQQ, fromGroup); strVar["new_nick"] = strip(strMsg.substr(intMsgCnt)); filter_CQcode(strVar["new_nick"]); if (strVar["new_nick"].length() > 50) { @@ -3040,7 +3041,11 @@ int FromMsg::InnerOrder() { readSkipSpace(); string strname = strMsg.substr(intMsgCnt); if (strname.empty()) { - if (!strVar.count("pc") || strVar["pc"].empty())getPCName(*this); + + if (!strVar.count("pc") || strVar["pc"].empty()) { + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); + } strname = strVar["pc"]; } else diff --git a/QQAPI/DDAPI.cpp b/QQAPI/DDAPI.cpp index 50c099ae..a2856a2c 100644 --- a/QQAPI/DDAPI.cpp +++ b/QQAPI/DDAPI.cpp @@ -80,8 +80,7 @@ namespace DD { } int uploadBlack(long long DiceMaid, long long fromQQ, long long fromGroup, const std::string& type, std::string& info) { - info = "ϴӿڲ"; - return CALLGET(DiceUploadBlack, DiceMaid, fromQQ, fromGroup, type, info) :-1; + return CALLGET(DiceUploadBlack, DiceMaid, fromQQ, fromGroup, type, info) :-2; } bool updateDice(const std::string& ver, std::string& ret) { ret = "½ӿڲ"; From 45048ebbae4fa1539cd6268b27ca44d218c2236a Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 3 Mar 2021 22:23:02 +0800 Subject: [PATCH 099/171] fix CardBuild MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复pc生成参数 --- Dice/CharacterCard.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index be86ac6f..416bd7dd 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -212,13 +212,13 @@ inline map mCardTemplet = { "COC7", { "COC7", SkillNameReplace, BasicCOC7, InfoCOC7, AutoFillCOC7, mVariableCOC7, ExpressionCOC7, SkillDefaultVal, { - {"_default", CardBuild({BuildCOC7}, CardDeck::mPublicDeck[""], {})}, + {"_default", CardBuild({BuildCOC7}, {"{}"}, {})}, { "bg", CardBuild({ {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, {"֮", "{֮}"}, {"", "{Աص}"} - }, CardDeck::mPublicDeck[""], {}) + }, {"{}"}, {}) } } } @@ -227,13 +227,13 @@ inline map mCardTemplet = { "BRP", {}, {}, {}, {}, {}, {}, { {"__DefaultDice",100} }, { - {"_default", CardBuild({}, CardDeck::mPublicDeck[""], {})}, + {"_default", CardBuild({}, {"{}"}, {})}, { "bg", CardBuild({ {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, {"֮", "{֮}"}, {"", "{Աص}"} - }, CardDeck::mPublicDeck[""], {}) + }, {"{}"}, {}) } } }}, @@ -244,8 +244,8 @@ inline map mCardTemplet = { {"_default", CardBuild({}, CardDeck::mPublicDeck[""], {})}, { "bg", CardBuild({ - {"Ա", "{Ա}"}, - }, CardDeck::mPublicDeck[""], {}) + {"Ա", "{Ա}"}, + }, {"{}"}, {}) } } }}, @@ -399,7 +399,7 @@ class CharaCard { std::stack vOption; int Cnt; - vOption.push(""); + vOption.push("_default"); while ((Cnt = para.rfind(':')) != string::npos) { vOption.push(para.substr(Cnt + 1)); From 24a25e4f872cbc8f5626eb137770e52a33d3e4c1 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Thu, 4 Mar 2021 16:16:31 +0800 Subject: [PATCH 100/171] Update CharacterCard.h --- Dice/CharacterCard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 416bd7dd..62ad3e52 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -241,7 +241,7 @@ inline map mCardTemplet = { "DND", {}, {}, {}, {}, {}, {}, { {"__DefaultDice",20} }, { - {"_default", CardBuild({}, CardDeck::mPublicDeck[""], {})}, + {"_default", CardBuild({}, {"{}"}, {})}, { "bg", CardBuild({ {"Ա", "{Ա}"}, From 97ef74f50167b16b270de31673f707e55c29149c Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 5 Mar 2021 11:55:51 +0800 Subject: [PATCH 101/171] Fix a potential buffer overflow --- Dice/CharacterCard.h | 1 + Dice/EncodingConvert.h | 7 +++---- Dice/GlobalVar.h | 12 ++++++------ Dice/StrExtern.hpp | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 62ad3e52..6c098596 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -15,6 +15,7 @@ #include #include "CQTools.h" #include "Unpack.h" +#include "RDConstant.h" #include "RD.h" #include "DiceXMLTree.h" #include "DiceFile.hpp" diff --git a/Dice/EncodingConvert.h b/Dice/EncodingConvert.h index 74b97b40..7b2a891a 100644 --- a/Dice/EncodingConvert.h +++ b/Dice/EncodingConvert.h @@ -9,7 +9,7 @@ * |_______/ |________| |________| |________| |__| * * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2019 w4123 + * Copyright (C) 2018-2021 w4123 * * This program is free software: you can redistribute it and/or modify it under the terms * of the GNU Affero General Public License as published by the Free Software Foundation, @@ -75,7 +75,7 @@ std::string UrlEncode(const std::string& str); std::string UrlDecode(const std::string& str); #ifndef _WIN32 template -std::basic_string ConvertEncoding(const std::basic_string& in, const std::string& InEnc, const std::string& OutEnc, const double CapFac = 2.0) +std::basic_string ConvertEncoding(const std::basic_string& in, const std::string& InEnc, const std::string& OutEnc, const double CapFac = 4.0) { const auto cd = iconv_open(OutEnc.c_str(), InEnc.c_str()); if (cd == (iconv_t)-1) @@ -95,8 +95,7 @@ std::basic_string ConvertEncoding(const std::basic_string& in, const std:: iconv_close(cd); return std::basic_string(); } - memset(out_ptr, 0, sizeof(T)); - std::basic_string ret(reinterpret_cast(out_ptr_copy)); + std::basic_string ret(reinterpret_cast(out_ptr_copy), (out_ptr - out_ptr_copy) / sizeof(T)); delete[] out_ptr_copy; iconv_close(cd); return ret; diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index e002a64b..f6a4c53e 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -48,22 +48,22 @@ inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_strin #ifdef __clang__ #ifdef _MSC_VER -const std::string Dice_Full_Ver = Dice_Short_Ver + " [CLANG " + std::to_string(__clang_major__) + "." + std::to_string(__clang_minor__) + "." + std::to_string(__clang_patchlevel__) + " with MSVC " + std::to_string(_MSC_VER) + " " + __DATE__ + " " + __TIME__; +inline const std::string Dice_Full_Ver = Dice_Short_Ver + " [CLANG " + std::to_string(__clang_major__) + "." + std::to_string(__clang_minor__) + "." + std::to_string(__clang_patchlevel__) + " with MSVC " + std::to_string(_MSC_VER) + " " + __DATE__ + " " + __TIME__; #elif defined(__GNUC__) -const std::string Dice_Full_Ver = Dice_Short_Ver + " [CLANG " + std::to_string(__clang_major__) + "." + std::to_string(__clang_minor__) + "." + std::to_string(__clang_patchlevel__) + " with GNUC " + std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__) + " " + __DATE__ + " " + __TIME__; +inline const std::string Dice_Full_Ver = Dice_Short_Ver + " [CLANG " + std::to_string(__clang_major__) + "." + std::to_string(__clang_minor__) + "." + std::to_string(__clang_patchlevel__) + " with GNUC " + std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__) + " " + __DATE__ + " " + __TIME__; #else -const std::string Dice_Full_Ver = Dice_Short_Ver + " [CLANG " + std::to_string(__clang_major__) + "." + std::to_string(__clang_minor__) + "." + std::to_string(__clang_patchlevel__); +inline const std::string Dice_Full_Ver = Dice_Short_Ver + " [CLANG " + std::to_string(__clang_major__) + "." + std::to_string(__clang_minor__) + "." + std::to_string(__clang_patchlevel__); #endif #else #ifdef _MSC_VER -const std::string Dice_Full_Ver = std::string(Dice_Short_Ver) + "[MSVC " + std::to_string(_MSC_VER) + " " + __DATE__ + +inline const std::string Dice_Full_Ver = std::string(Dice_Short_Ver) + "[MSVC " + std::to_string(_MSC_VER) + " " + __DATE__ + " " + __TIME__ + "]"; #elif defined(__GNUC__) -const std::string Dice_Full_Ver = Dice_Short_Ver + " [GNUC " + std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__) + " " + __DATE__ + " " + __TIME__; +inline const std::string Dice_Full_Ver = Dice_Short_Ver + " [GNUC " + std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__) + " " + __DATE__ + " " + __TIME__; #else -const std::string Dice_Full_Ver = Dice_Short_Ver + " [UNKNOWN COMPILER"; +inline const std::string Dice_Full_Ver = Dice_Short_Ver + " [UNKNOWN COMPILER"; #endif #endif diff --git a/Dice/StrExtern.hpp b/Dice/StrExtern.hpp index 52c584e6..fc376eb3 100644 --- a/Dice/StrExtern.hpp +++ b/Dice/StrExtern.hpp @@ -15,7 +15,7 @@ using std::to_string; string toString(int num, unsigned short size = 0); template -typename std::enable_if::value, string>::type +typename std::enable_if_t, string> toString(F num, unsigned short scale = 2, bool align = false) { string strNum{ to_string(num) }; From 525105cd28fb011d4fe8abfc96215c3b88fbfde3 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 5 Mar 2021 12:11:28 +0800 Subject: [PATCH 102/171] Use inline variable instead of static variable --- Dice/CharacterCard.h | 28 +++++++++------------------- Dice/RDConstant.h | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 6c098596..be3ba327 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -118,25 +118,15 @@ class CardTemp CardTemp(string type, map replace, vector> basic, set info, map autofill, map dynamic, map exp, - map skill, map option) : type( - std::move( - type)), replaceName( - std::move( - replace)), vBasicList( - std::move( - basic)), sInfoList( - std::move( - info)), mAutoFill( - std::move( - autofill)), mVariable( - std::move( - dynamic)), mExpression( - std::move( - exp)), defaultSkill( - std::move( - skill)), mBuildOption( - std::move( - option)) + map skill, map option) : type(std::move(type)), + replaceName(std::move(replace)), + vBasicList(std::move(basic)), + sInfoList(std::move(info)), + mAutoFill(std::move(autofill)), + mVariable(std::move(dynamic)), + mExpression(std::move(exp)), + defaultSkill(std::move(skill)), + mBuildOption(std::move(option)) { } diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index 4c1b0efe..2de3ddfc 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -53,7 +53,7 @@ typedef int int_errno; -static std::map SkillNameReplace = { +inline std::map SkillNameReplace = { {"str", ""}, {"dex", ""}, {"pow", "־"}, @@ -112,19 +112,19 @@ static std::map SkillNameReplace = { {"ʻ", "ʻ"}, {"ʻ()", "ʻ"} }; -static std::vector> BasicCOC7 = { +inline std::vector> BasicCOC7 = { {"ְҵ", "", "Ա", "ס", "", "ʱ"}, {"", "", "", "", "ò", "", "־", ""}, {"", "", "ħ", ""}, {"״̬", "״̬"}, {"DB", "", ""} }; -static std::set InfoCOC7 = { +inline std::set InfoCOC7 = { "ְҵ", "Ա", "ס", "", "ʱ", "״̬", "״̬", "", "˼", "Ҫ֮", "Ƿ֮", "֮", "", "ص", "˿ڰ̺", "־" }; -static std::vector> BuildCOC7 = { +inline std::vector> BuildCOC7 = { {"", "3D6*5"}, {"", "3D6*5"}, {"", "2D6*5+30"}, @@ -137,7 +137,7 @@ static std::vector> BuildCOC7 = { {"", "[ͺ]/10"}, {"ħ", "[־]/5"}, }; -static std::map mVariableCOC7 = { +inline std::map mVariableCOC7 = { {"", "&"}, {"֪ʶ", "&"}, {"ͺ", "[]+[]"}, @@ -265,7 +265,7 @@ static std::map ExpressionCOC7 = { {"", "2D6"}, {"ͷ̹", "8D10"} }; -static std::map AutoFillCOC7 = { +inline std::map AutoFillCOC7 = { {"", "[ͺ]/10"}, {"", "&־"}, {"ħ", "[־]/5"}, @@ -373,7 +373,7 @@ static std::map SkillDefaultVal = { {"Ź", 25} }; -static std::string TempInsanity[11]{ +inline std::string TempInsanity[11]{ "", "ʧ䣺{0}֮ڣԱᷢԼֻǵİȫص㣬ȴûκļ䡣", "ԲмԱԵʧʧϻȱʧУ{0}֡", @@ -387,7 +387,7 @@ static std::string TempInsanity[11]{ "꣺Աij״̬֢״{0}֡\n{1}\n֢: {2}(KPҲдӿ֢״ѡ֢״)" }; -static std::string LongInsanity[11]{ +inline std::string LongInsanity[11]{ "", "ʧ䣺{0}Сʱ󣬵ԱعԼһİĵطԼ˭ʱָ", "ԣԱ{0}СʱָѣԼ", @@ -401,7 +401,7 @@ static std::string LongInsanity[11]{ "꣺ԱһµĿ֢{0}СʱָǡηУԱȫµĿ֢״Ƿᱻ⣨apparent to other peopleȡ˺ʹ˵Ա\n{1}\n֢: {2}(KPҲдӿ֢״ѡ֢״)" }; -static std::string strFear[101] = { +inline std::string strFear[101] = { "", "ϴ־֢AblutophobiaϴӻϴĿ־塣", "ָ֢AcrophobiaߴĿ־塣", @@ -504,7 +504,7 @@ static std::string strFear[101] = { "־֢Xenophobiaİ˻˵Ŀ־塣", "־֢ZoophobiaԶĿ־塣" }; -static std::string strPanic[101] = { +inline std::string strPanic[101] = { "", "ԡ񱣨AblutomaniaִϴԼ", "ԥ񱣨Aboulomania̬ԥ", From 21bb1a320b218e60b43c8b694dd90ebedf0f4b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Fri, 5 Mar 2021 12:42:42 +0800 Subject: [PATCH 103/171] Update CharacterCard.h --- Dice/CharacterCard.h | 76 ++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index be3ba327..ad76680a 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -1,7 +1,7 @@ #pragma once /* - * │ + * 玩家人物卡 * Copyright (C) 2019 String.Empty */ @@ -55,7 +55,7 @@ inline map mCardTag = { {"End", 255} }; -//ģ +//生成模板 class CardBuild { public: @@ -68,11 +68,11 @@ class CardBuild { } - // + //属性生成 vector> vBuildList = {}; - // + //随机姓名 vector vNameList = {}; - //Note + //Note生成 vector vNoteList = {}; CardBuild(const DDOM& d) @@ -101,18 +101,18 @@ class CardTemp public: string type; map replaceName = {}; - //ʱ + //作成时生成 vector> vBasicList = {}; set sInfoList = {}; - //ʱ + //调用时生成 map mAutoFill = {}; - //̬ + //动态引用 map mVariable = {}; - //ʽ + //表达式 map mExpression = {}; - //Ĭֵ + //默认值 map defaultSkill = {}; - //ɲ + //生成参数 map mBuildOption = {}; CardTemp() = default; @@ -201,15 +201,15 @@ class CardTemp inline map mCardTemplet = { { "COC7", { - "COC7", SkillNameReplace, BasicCOC7, InfoCOC7, AutoFillCOC7, mVariableCOC7, ExpressionCOC7, - SkillDefaultVal, { - {"_default", CardBuild({BuildCOC7}, {"{}"}, {})}, + "COC7", {}, {}, {}, {}, {}, {}, + {}, { + {"_default", CardBuild({}, {"{随机姓名}"}, {})}, { "bg", CardBuild({ - {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, - {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, - {"֮", "{֮}"}, {"", "{Աص}"} - }, {"{}"}, {}) + {"性别", "{性别}"}, {"年龄", "7D6+8"}, {"职业", "{调查员职业}"}, {"个人描述", "{个人描述}"}, + {"重要之人", "{重要之人}"}, {"思想信念", "{思想信念}"}, {"意义非凡之地", "{意义非凡之地}"}, + {"宝贵之物", "{宝贵之物}"}, {"特质", "{调查员特点}"} + }, {"{随机姓名}"}, {}) } } } @@ -218,13 +218,13 @@ inline map mCardTemplet = { "BRP", {}, {}, {}, {}, {}, {}, { {"__DefaultDice",100} }, { - {"_default", CardBuild({}, {"{}"}, {})}, + {"_default", CardBuild({}, {"{随机姓名}"}, {})}, { "bg", CardBuild({ - {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, - {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, - {"֮", "{֮}"}, {"", "{Աص}"} - }, {"{}"}, {}) + {"性别", "{性别}"}, {"年龄", "7D6+8"}, {"职业", "{调查员职业}"}, {"个人描述", "{个人描述}"}, + {"重要之人", "{重要之人}"}, {"思想信念", "{思想信念}"}, {"意义非凡之地", "{意义非凡之地}"}, + {"宝贵之物", "{宝贵之物}"}, {"特质", "{调查员特点}"} + }, {"{随机姓名}"}, {}) } } }}, @@ -232,11 +232,11 @@ inline map mCardTemplet = { "DND", {}, {}, {}, {}, {}, {}, { {"__DefaultDice",20} }, { - {"_default", CardBuild({}, {"{}"}, {})}, + {"_default", CardBuild({}, {"{随机姓名}"}, {})}, { "bg", CardBuild({ - {"Ա", "{Ա}"}, - }, {"{}"}, {}) + {"性别", "{性别}"}, + }, {"{随机姓名}"}, {}) } } }}, @@ -247,7 +247,7 @@ struct lua_State; class CharaCard { private: - string Name = "ɫ"; + string Name = "角色卡"; public: const string& getName()const { return Name; } void setName(const string&); @@ -287,7 +287,7 @@ class CharaCard return 0; } - //ʽת + //表达式转义 string escape(string exp, const set& sRef) { if (exp[0] == '&') @@ -308,7 +308,7 @@ class CharaCard return exp; } - //keyӦʽ + //求key对应掷骰表达式 string getExp(string& key, set sRef = {}) { sRef.insert(key); @@ -331,7 +331,7 @@ class CharaCard return DiceExp.count(key) || pTemplet->mExpression.count(key); } - //ʽ + //计算表达式 short cal(string exp) { if (exp[0] == '&') @@ -385,7 +385,7 @@ class CharaCard } } - //ɲ + //解析生成参数 void buildv(string para = "") { std::stack vOption; @@ -508,10 +508,10 @@ class Player { private: short indexMax = 0; - map mCardList{{0, {"ɫ"}}}; + map mCardList{{0, {"角色卡"}}}; map mNameIndex; map mGroupIndex{{0, 0}}; - // │ + // 人物卡互斥 std::mutex cardMutex; public: Player() = default; @@ -548,7 +548,7 @@ class Player int newCard(string& s, long long group = 0) { std::lock_guard lock_queue(cardMutex); - //│ + //人物卡数量上限 if (mCardList.size() > 16)return -1; string type = "COC7"; s = strip(s); @@ -570,7 +570,7 @@ class Player vOption.push(type.substr(Cnt + 1)); type.erase(type.begin() + Cnt, type.end()); } - //Чģ岻ٱ + //无效模板不再报错 //if (!getmCardTemplet().count(type))return -2; if (mNameIndex.count(s))return -4; if (s.find("=") != string::npos)return -6; @@ -627,7 +627,7 @@ class Player strName = strip(name.substr(name.rfind(":") + 1)); strType = name.substr(0, name.rfind(":")); } - //½│ + //不存在则新建人物卡 if (!strName.empty() && !mNameIndex.count(strName)) { if (const int res = newCard(name, group))return res; @@ -692,11 +692,11 @@ class Player int copyCard(const string& name1, const string& name2, long long group = 0) { if (name1.empty() || name2.empty())return -3; - //½│ + //不存在则新建人物卡 if (!mNameIndex.count(name1)) { std::lock_guard lock_queue(cardMutex); - //│ + //人物卡数量上限 if (mCardList.size() > 16)return -1; if (name1.find(":") != string::npos)return -6; mCardList[++indexMax] = name1; From 9dc6f5c6522a0b079e0564d747bc0de095f8236b Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 5 Mar 2021 13:24:16 +0800 Subject: [PATCH 104/171] Fix potential initialization sequence conflict --- Dice/CharacterCard.h | 91 ++++++++++++-------------------------------- Dice/Dice.cpp | 43 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index ad76680a..42385768 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -1,7 +1,7 @@ #pragma once /* - * 玩家人物卡 + * │ * Copyright (C) 2019 String.Empty */ @@ -55,7 +55,7 @@ inline map mCardTag = { {"End", 255} }; -//生成模板 +//ģ class CardBuild { public: @@ -68,11 +68,11 @@ class CardBuild { } - //属性生成 + // vector> vBuildList = {}; - //随机姓名 + // vector vNameList = {}; - //Note生成 + //Note vector vNoteList = {}; CardBuild(const DDOM& d) @@ -101,18 +101,18 @@ class CardTemp public: string type; map replaceName = {}; - //作成时生成 + //ʱ vector> vBasicList = {}; set sInfoList = {}; - //调用时生成 + //ʱ map mAutoFill = {}; - //动态引用 + //̬ map mVariable = {}; - //表达式 + //ʽ map mExpression = {}; - //默认值 + //Ĭֵ map defaultSkill = {}; - //生成参数 + //ɲ map mBuildOption = {}; CardTemp() = default; @@ -198,56 +198,15 @@ class CardTemp string show(); }; -inline map mCardTemplet = { - { - "COC7", { - "COC7", {}, {}, {}, {}, {}, {}, - {}, { - {"_default", CardBuild({}, {"{随机姓名}"}, {})}, - { - "bg", CardBuild({ - {"性别", "{性别}"}, {"年龄", "7D6+8"}, {"职业", "{调查员职业}"}, {"个人描述", "{个人描述}"}, - {"重要之人", "{重要之人}"}, {"思想信念", "{思想信念}"}, {"意义非凡之地", "{意义非凡之地}"}, - {"宝贵之物", "{宝贵之物}"}, {"特质", "{调查员特点}"} - }, {"{随机姓名}"}, {}) - } - } - } - }, - {"BRP", { - "BRP", {}, {}, {}, {}, {}, {}, { - {"__DefaultDice",100} - }, { - {"_default", CardBuild({}, {"{随机姓名}"}, {})}, - { - "bg", CardBuild({ - {"性别", "{性别}"}, {"年龄", "7D6+8"}, {"职业", "{调查员职业}"}, {"个人描述", "{个人描述}"}, - {"重要之人", "{重要之人}"}, {"思想信念", "{思想信念}"}, {"意义非凡之地", "{意义非凡之地}"}, - {"宝贵之物", "{宝贵之物}"}, {"特质", "{调查员特点}"} - }, {"{随机姓名}"}, {}) - } - } - }}, - {"DND", { - "DND", {}, {}, {}, {}, {}, {}, { - {"__DefaultDice",20} - }, { - {"_default", CardBuild({}, {"{随机姓名}"}, {})}, - { - "bg", CardBuild({ - {"性别", "{性别}"}, - }, {"{随机姓名}"}, {}) - } - } - }}, -}; +// ȫֱΪ˱ȫֱʼ˳ͻeventEnableбʼ +inline map mCardTemplet; CardTemp& getCardTemplet(const string& type); struct lua_State; class CharaCard { private: - string Name = "角色卡"; + string Name = "ɫ"; public: const string& getName()const { return Name; } void setName(const string&); @@ -287,7 +246,7 @@ class CharaCard return 0; } - //表达式转义 + //ʽת string escape(string exp, const set& sRef) { if (exp[0] == '&') @@ -308,7 +267,7 @@ class CharaCard return exp; } - //求key对应掷骰表达式 + //keyӦʽ string getExp(string& key, set sRef = {}) { sRef.insert(key); @@ -331,7 +290,7 @@ class CharaCard return DiceExp.count(key) || pTemplet->mExpression.count(key); } - //计算表达式 + //ʽ short cal(string exp) { if (exp[0] == '&') @@ -385,7 +344,7 @@ class CharaCard } } - //解析生成参数 + //ɲ void buildv(string para = "") { std::stack vOption; @@ -508,10 +467,10 @@ class Player { private: short indexMax = 0; - map mCardList{{0, {"角色卡"}}}; + map mCardList{{0, {"ɫ"}}}; map mNameIndex; map mGroupIndex{{0, 0}}; - // 人物卡互斥 + // │ std::mutex cardMutex; public: Player() = default; @@ -548,7 +507,7 @@ class Player int newCard(string& s, long long group = 0) { std::lock_guard lock_queue(cardMutex); - //人物卡数量上限 + //│ if (mCardList.size() > 16)return -1; string type = "COC7"; s = strip(s); @@ -570,7 +529,7 @@ class Player vOption.push(type.substr(Cnt + 1)); type.erase(type.begin() + Cnt, type.end()); } - //无效模板不再报错 + //Чģ岻ٱ //if (!getmCardTemplet().count(type))return -2; if (mNameIndex.count(s))return -4; if (s.find("=") != string::npos)return -6; @@ -627,7 +586,7 @@ class Player strName = strip(name.substr(name.rfind(":") + 1)); strType = name.substr(0, name.rfind(":")); } - //不存在则新建人物卡 + //½│ if (!strName.empty() && !mNameIndex.count(strName)) { if (const int res = newCard(name, group))return res; @@ -692,11 +651,11 @@ class Player int copyCard(const string& name1, const string& name2, long long group = 0) { if (name1.empty() || name2.empty())return -3; - //不存在则新建人物卡 + //½│ if (!mNameIndex.count(name1)) { std::lock_guard lock_queue(cardMutex); - //人物卡数量上限 + //│ if (mCardList.size() > 16)return -1; if (name1.find(":") != string::npos)return -6; mCardList[++indexMax] = name1; diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 13670ae0..6a1d48da 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -197,6 +197,49 @@ EVE_Enable(eventEnable) } Dice_Full_Ver_On = Dice_Full_Ver + " on\n" + DD::getDriVer(); DD::debugLog(Dice_Full_Ver_On); + mCardTemplet = { + { + "COC7", { + "COC7", SkillNameReplace, BasicCOC7, InfoCOC7, AutoFillCOC7, mVariableCOC7, ExpressionCOC7, + SkillDefaultVal, { + {"_default", CardBuild({BuildCOC7}, {"{}"}, {})}, + { + "bg", CardBuild({ + {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, + {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, + {"֮", "{֮}"}, {"", "{Աص}"} + }, {"{}"}, {}) + } + } + } + }, + {"BRP", { + "BRP", {}, {}, {}, {}, {}, {}, { + {"__DefaultDice",100} + }, { + {"_default", CardBuild({}, {"{}"}, {})}, + { + "bg", CardBuild({ + {"Ա", "{Ա}"}, {"", "7D6+8"}, {"ְҵ", "{Աְҵ}"}, {"", "{}"}, + {"Ҫ֮", "{Ҫ֮}"}, {"˼", "{˼}"}, {"Ƿ֮", "{Ƿ֮}"}, + {"֮", "{֮}"}, {"", "{Աص}"} + }, {"{}"}, {}) + } + } + }}, + {"DND", { + "DND", {}, {}, {}, {}, {}, {}, { + {"__DefaultDice",20} + }, { + {"_default", CardBuild({}, {"{}"}, {})}, + { + "bg", CardBuild({ + {"Ա", "{Ա}"}, + }, {"{}"}, {}) + } + } + }}, + }; if ((console.DiceMaid = DD::getLoginQQ())) { DiceDir = dirExe + "Dice" + to_string(console.DiceMaid); From 1b27e6e094662b60af6d8595f8a7d70f3dc94213 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 5 Mar 2021 14:27:11 +0800 Subject: [PATCH 105/171] Use forward slashes instead of backward ones --- Dice/Dice.cpp | 8 ++++---- Dice/DiceMod.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 6a1d48da..7a5e7ee3 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -93,7 +93,7 @@ void loadData() fmt->load(&logList); if (int cnt; (cnt = loadJMap(DiceDir + "/conf/CustomHelp.json", CustomHelp)) < 0) { - if (cnt == -1)logList << DiceDir + "\\conf\\CustomHelp.jsonʧܣ"; + if (cnt == -1)logList << DiceDir + "/conf/CustomHelp.jsonʧܣ"; ifstream ifstreamHelpDoc(strFileLoc + "HelpDoc.txt"); if (ifstreamHelpDoc) { @@ -339,7 +339,7 @@ EVE_Enable(eventEnable) if (console.master())getUser(console.master()).create(NEWYEAR).trust(5); if (UserList.size()){ console.log("ʼû¼" + to_string(UserList.size()) + "", 1); - saveFile(DiceDir + "\\user\\UserList.txt", UserList); + saveFile(DiceDir + "/user/UserList.txt", UserList); } } if (loadBFile(DiceDir + "/user/ChatConf.RDconf", ChatList) < 1) @@ -428,7 +428,7 @@ EVE_Enable(eventEnable) chat(g).group().set("ʹ").set(""); } } - saveBFile(DiceDir + "\\user\\ChatConf.RDconf", ChatList); + saveBFile(DiceDir + "/user/ChatConf.RDconf", ChatList); } if (loadFile(DiceDir + "/user/ChatList.txt", ChatList) < 1) { @@ -442,7 +442,7 @@ EVE_Enable(eventEnable) } if(ChatList.size()){ console.log("ʼȺ¼" + to_string(ChatList.size()) + "", 1); - saveFile(DiceDir + "\\user\\ChatList.txt", ChatList); + saveFile(DiceDir + "/user/ChatList.txt", ChatList); } } for (auto gid : DD::getGroupIDList()) diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 68662e72..7a81bb33 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -171,7 +171,7 @@ int DiceModManager::load(ResList* resLog) cntItem += readJMap(j["global_char"], GlobalChar); } } - *resLog << "ȡ\\mod\\е" + std::to_string(cntFile) + "ļ, " + std::to_string(cntItem) + "Ŀ"; + *resLog << "ȡ/mod/е" + std::to_string(cntFile) + "ļ, " + std::to_string(cntItem) + "Ŀ"; if (!sModErr.empty()) { *resLog << "ȡʧ" + std::to_string(sModErr.size()) + ":"; for (auto& it : sModErr) { @@ -181,7 +181,7 @@ int DiceModManager::load(ResList* resLog) } //ȡplugin vector sLuaFile; - int cntLuaFile = listDir(DiceDir + "\\plugin\\", sLuaFile); + int cntLuaFile = listDir(DiceDir + "/plugin/", sLuaFile); int cntOrder{ 0 }; if (cntLuaFile <= 0)return cntLuaFile; vector sLuaErr; @@ -204,7 +204,7 @@ int DiceModManager::load(ResList* resLog) sLuaErr.push_back(pathFile.filename().string()); } } - *resLog << "ȡ\\plugin\\е" + std::to_string(cntLuaFile) + "ű, " + std::to_string(cntOrder) + "ָ"; + *resLog << "ȡ/plugin/е" + std::to_string(cntLuaFile) + "ű, " + std::to_string(cntOrder) + "ָ"; if (!sLuaErr.empty()) { *resLog << "ȡʧ" + std::to_string(sLuaErr.size()) + ":"; for (auto& it : sLuaErr) { From e7bd0ca6403ab8803126ff330d4b4d68a79ccc22 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sat, 6 Mar 2021 08:13:49 +0800 Subject: [PATCH 106/171] Update minimum MacOS version required --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c81206a2..1c0a4822 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file") +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" + CACHE STRING "Minimum OS X deployment version") project(Dice) include_directories(CQSDK Dice Lua QQAPI) From 7ba933f41e56af2b9902157de4ca5e0b2d875a40 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 9 Mar 2021 10:25:57 +0800 Subject: [PATCH 107/171] Try fix android build --- .gitmodules | 3 +++ CMakeLists.txt | 7 ++++++- libiconv | 1 + vcpkg.json | 4 ++-- 4 files changed, 12 insertions(+), 3 deletions(-) create mode 160000 libiconv diff --git a/.gitmodules b/.gitmodules index ed4f1eb6..c3b43781 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "vcpkg"] path = vcpkg url = https://github.com/w4123/vcpkg +[submodule "libiconv"] + path = libiconv + url = https://github.com/w4123/libiconv-android-prebuilt diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c0a4822..65967371 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") endif() endif() -if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL Windows) +if(NOT ((CMAKE_SYSTEM_NAME STREQUAL Windows) OR (CMAKE_SYSTEM_NAME STREQUAL Android))) find_package(CURL REQUIRED) target_link_libraries(w4123.Dice PRIVATE CURL::libcurl) find_package(Iconv REQUIRED) @@ -48,3 +48,8 @@ target_link_libraries(w4123.Dice PRIVATE OpenSSL::SSL OpenSSL::Crypto) find_package(AWSSDK CONFIG COMPONENTS core s3 REQUIRED) target_include_directories(w4123.Dice PRIVATE ${AWSSDK_INCLUDE_DIRS}) target_link_libraries(w4123.Dice PRIVATE ${AWSSDK_LIBRARIES}) + +if(CMAKE_SYSTEM_NAME STREQUAL Android) + target_include_directories(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIRECTORY}/libiconv/${ANDROID_ABI}/include) + target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIRECTORY}/libiconv/${ANDROID_ABI}/lib/libiconv.a) +endif() diff --git a/libiconv b/libiconv new file mode 160000 index 00000000..dd88e27c --- /dev/null +++ b/libiconv @@ -0,0 +1 @@ +Subproject commit dd88e27c772f3d608303e57f4dc41837e3534e5c diff --git a/vcpkg.json b/vcpkg.json index bf0189d6..67076958 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -14,7 +14,7 @@ }, { "name": "libiconv", - "platform" : "!windows" + "platform" : "!windows & !android" } ] -} \ No newline at end of file +} From 1b37acefcdfb6649603b3cedec0b3f20dbc121cc Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 9 Mar 2021 13:00:20 +0800 Subject: [PATCH 108/171] Update CMakeLists.txt --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 65967371..a463182d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,6 @@ target_include_directories(w4123.Dice PRIVATE ${AWSSDK_INCLUDE_DIRS}) target_link_libraries(w4123.Dice PRIVATE ${AWSSDK_LIBRARIES}) if(CMAKE_SYSTEM_NAME STREQUAL Android) - target_include_directories(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIRECTORY}/libiconv/${ANDROID_ABI}/include) - target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIRECTORY}/libiconv/${ANDROID_ABI}/lib/libiconv.a) + target_include_directories(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/include) + target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/lib/libiconv.a) endif() From 223e9ac63921529ea4d30b57da7664d89c4993fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 9 Mar 2021 13:28:06 +0800 Subject: [PATCH 109/171] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 50a802c6..266bdd49 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -266,7 +266,7 @@ jobs: - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=21 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=21 - name: Build working-directory: ${{github.workspace}}/build From e20f15fecf5d88894eeb7cff651cd1f9c7b6eff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 9 Mar 2021 13:35:00 +0800 Subject: [PATCH 110/171] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..4ca061d0 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,67 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ Shiki ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ Shiki ] + schedule: + - cron: '17 7 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 7fda0331c6ac91628e4731ffffea6a3f2a552e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 9 Mar 2021 13:37:46 +0800 Subject: [PATCH 111/171] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4ca061d0..5446eebf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,6 +36,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v2 + with: + submodules: 'recursive' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL From 85344051b193d91d4013ec01d6cae918c27dec62 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 9 Mar 2021 13:46:40 +0800 Subject: [PATCH 112/171] Explicitly enable OpenSSL --- vcpkg.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vcpkg.json b/vcpkg.json index 67076958..43d92599 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,6 +7,11 @@ "name": "aws-sdk-cpp", "features": [ "s3" ] }, + { + "name": "openssl", + "platform": "!windows" + }, + } { "name": "curl", "features": [ "openssl" ], From 0e427049ccfa27f7b437ebf83241f5b931b68a36 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 9 Mar 2021 13:52:00 +0800 Subject: [PATCH 113/171] Update vcpkg.json --- vcpkg.json | 1 - 1 file changed, 1 deletion(-) diff --git a/vcpkg.json b/vcpkg.json index 43d92599..b67dacb6 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -11,7 +11,6 @@ "name": "openssl", "platform": "!windows" }, - } { "name": "curl", "features": [ "openssl" ], From edac139d55c23e9b69fef274b9a570aeece94938 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 9 Mar 2021 14:41:52 +0800 Subject: [PATCH 114/171] Update vcpkg submodule --- vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index f439a8e9..0bf108d9 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit f439a8e96a5e3648adc7205eb102960bc44f4149 +Subproject commit 0bf108d9f9b33484378666cd33f3ec83eda60ec9 From 0765c522e6102aad34ba8cd6f9ad5b59f9aed04f Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 9 Mar 2021 15:07:46 +0800 Subject: [PATCH 115/171] Update submodule --- vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index 0bf108d9..371e41d8 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 0bf108d9f9b33484378666cd33f3ec83eda60ec9 +Subproject commit 371e41d82165f5e74aa4ffc83c0e54a7e5d10209 From 111ab1fcd938b835217740e571fdd971bb2e2dd5 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Tue, 9 Mar 2021 15:44:54 +0800 Subject: [PATCH 116/171] Update Lua 5.4.2 --- Lua/lapi.c | 19 +++++-- Lua/lauxlib.c | 72 ++++++++++++++++-------- Lua/lcode.c | 14 +++-- Lua/ldblib.c | 29 ++++++---- Lua/ldo.c | 147 ++++++++++++++++++++++++++++++------------------- Lua/ldo.h | 1 + Lua/lfunc.c | 2 +- Lua/lgc.c | 54 ++++++++++-------- Lua/llex.c | 25 +++++---- Lua/llimits.h | 16 +++++- Lua/lobject.c | 2 +- Lua/lobject.h | 18 +++--- Lua/lopcodes.h | 4 +- Lua/lparser.c | 66 +++++----------------- Lua/lparser.h | 5 +- Lua/lstate.c | 98 +++++++++++---------------------- Lua/lstate.h | 91 ++++++++++-------------------- Lua/lstring.c | 20 ++----- Lua/lstring.h | 3 +- Lua/lstrlib.c | 19 +++---- Lua/ltable.c | 46 ++++++++++------ Lua/lua.h | 2 +- Lua/luaconf.h | 15 ----- Lua/lvm.c | 75 +++++++++++++++---------- 24 files changed, 416 insertions(+), 427 deletions(-) diff --git a/Lua/lapi.c b/Lua/lapi.c index 9048245f..c824da27 100644 --- a/Lua/lapi.c +++ b/Lua/lapi.c @@ -1383,13 +1383,16 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { + static const UpVal *const nullup = NULL; LClosure *f; TValue *fi = index2value(L, fidx); api_check(L, ttisLclosure(fi), "Lua function expected"); f = clLvalue(fi); - api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); if (pf) *pf = f; - return &f->upvals[n - 1]; /* get its upvalue pointer */ + if (1 <= n && n <= f->p->sizeupvalues) + return &f->upvals[n - 1]; /* get its upvalue pointer */ + else + return (UpVal**)&nullup; } @@ -1401,11 +1404,14 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { } case LUA_VCCL: { /* C closure */ CClosure *f = clCvalue(fi); - api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); - return &f->upvalue[n - 1]; - } + if (1 <= n && n <= f->nupvalues) + return &f->upvalue[n - 1]; + /* else */ + } /* FALLTHROUGH */ + case LUA_VLCF: + return NULL; /* light C functions have no upvalues */ default: { - api_check(L, 0, "closure expected"); + api_check(L, 0, "function expected"); return NULL; } } @@ -1417,6 +1423,7 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, LClosure *f1; UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up2 = getupvalref(L, fidx2, n2, NULL); + api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index"); *up1 = *up2; luaC_objbarrier(L, f1, *up1); } diff --git a/Lua/lauxlib.c b/Lua/lauxlib.c index cbe9ed31..73504389 100644 --- a/Lua/lauxlib.c +++ b/Lua/lauxlib.c @@ -283,10 +283,10 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { LUALIB_API int luaL_execresult (lua_State *L, int stat) { - const char *what = "exit"; /* type of termination */ if (stat != 0 && errno != 0) /* error with an 'errno'? */ return luaL_fileresult(L, 0, NULL); else { + const char *what = "exit"; /* type of termination */ l_inspectstat(stat, what); /* interpret result */ if (*what == 'e' && stat == 0) /* successful termination? */ lua_pushboolean(L, 1); @@ -1006,43 +1006,67 @@ static int panic (lua_State *L) { /* -** Emit a warning. '*warnstate' means: -** 0 - warning system is off; -** 1 - ready to start a new message; -** 2 - previous message is to be continued. +** Warning functions: +** warnfoff: warning system is off +** warnfon: ready to start a new message +** warnfcont: previous message is to be continued */ -static void warnf (void *ud, const char *message, int tocont) { - int *warnstate = (int *)ud; - if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */ - if (strcmp(message, "@off") == 0) - *warnstate = 0; - else if (strcmp(message, "@on") == 0) - *warnstate = 1; - return; +static void warnfoff (void *ud, const char *message, int tocont); +static void warnfon (void *ud, const char *message, int tocont); +static void warnfcont (void *ud, const char *message, int tocont); + + +/* +** Check whether message is a control message. If so, execute the +** control or ignore it if unknown. +*/ +static int checkcontrol (lua_State *L, const char *message, int tocont) { + if (tocont || *(message++) != '@') /* not a control message? */ + return 0; + else { + if (strcmp(message, "off") == 0) + lua_setwarnf(L, warnfoff, L); /* turn warnings off */ + else if (strcmp(message, "on") == 0) + lua_setwarnf(L, warnfon, L); /* turn warnings on */ + return 1; /* it was a control message */ } - else if (*warnstate == 0) /* warnings off? */ - return; - if (*warnstate == 1) /* previous message was the last? */ - lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ +} + + +static void warnfoff (void *ud, const char *message, int tocont) { + checkcontrol((lua_State *)ud, message, tocont); +} + + +/* +** Writes the message and handle 'tocont', finishing the message +** if needed and setting the next warn function. +*/ +static void warnfcont (void *ud, const char *message, int tocont) { + lua_State *L = (lua_State *)ud; lua_writestringerror("%s", message); /* write message */ if (tocont) /* not the last part? */ - *warnstate = 2; /* to be continued */ + lua_setwarnf(L, warnfcont, L); /* to be continued */ else { /* last part */ lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ - *warnstate = 1; /* ready to start a new message */ + lua_setwarnf(L, warnfon, L); /* next call is a new message */ } } +static void warnfon (void *ud, const char *message, int tocont) { + if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */ + return; /* nothing else to be done */ + lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ + warnfcont(ud, message, tocont); /* finish processing */ +} + + LUALIB_API lua_State *luaL_newstate (void) { lua_State *L = lua_newstate(l_alloc, NULL); if (L) { - int *warnstate; /* space for warning state */ lua_atpanic(L, &panic); - warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0); - luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */ - *warnstate = 0; /* default is warnings off */ - lua_setwarnf(L, warnf, warnstate); + lua_setwarnf(L, warnfoff, L); /* default is warnings off */ } return L; } diff --git a/Lua/lcode.c b/Lua/lcode.c index 6f241c94..14d41f1a 100644 --- a/Lua/lcode.c +++ b/Lua/lcode.c @@ -753,7 +753,7 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { /* -** Ensure that expression 'e' is not a variable (nor a constant). +** Ensure that expression 'e' is not a variable (nor a ). ** (Expression still may have jump lists.) */ void luaK_dischargevars (FuncState *fs, expdesc *e) { @@ -805,8 +805,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { /* -** Ensures expression value is in register 'reg' (and therefore -** 'e' will become a non-relocatable expression). +** Ensure expression value is in register 'reg', making 'e' a +** non-relocatable expression. ** (Expression still may have jump lists.) */ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { @@ -860,7 +860,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { /* -** Ensures expression value is in any register. +** Ensure expression value is in a register, making 'e' a +** non-relocatable expression. ** (Expression still may have jump lists.) */ static void discharge2anyreg (FuncState *fs, expdesc *e) { @@ -946,8 +947,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) { exp2reg(fs, e, e->u.info); /* put final result in it */ return e->u.info; } + /* else expression has jumps and cannot change its register + to hold the jump values, because it is a local variable. + Go through to the default case. */ } - luaK_exp2nextreg(fs, e); /* otherwise, use next available register */ + luaK_exp2nextreg(fs, e); /* default: use next available register */ return e->u.info; } diff --git a/Lua/ldblib.c b/Lua/ldblib.c index 59eb8f0e..5a326ade 100644 --- a/Lua/ldblib.c +++ b/Lua/ldblib.c @@ -281,25 +281,33 @@ static int db_setupvalue (lua_State *L) { ** Check whether a given upvalue from a given closure exists and ** returns its index */ -static int checkupval (lua_State *L, int argf, int argnup) { +static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) { + void *id; int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ - luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup, - "invalid upvalue index"); - return nup; + id = lua_upvalueid(L, argf, nup); + if (pnup) { + luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index"); + *pnup = nup; + } + return id; } static int db_upvalueid (lua_State *L) { - int n = checkupval(L, 1, 2); - lua_pushlightuserdata(L, lua_upvalueid(L, 1, n)); + void *id = checkupval(L, 1, 2, NULL); + if (id != NULL) + lua_pushlightuserdata(L, id); + else + luaL_pushfail(L); return 1; } static int db_upvaluejoin (lua_State *L) { - int n1 = checkupval(L, 1, 2); - int n2 = checkupval(L, 3, 4); + int n1, n2; + checkupval(L, 1, 2, &n1); + checkupval(L, 3, 4, &n2); luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); lua_upvaluejoin(L, 1, n1, 3, n2); @@ -440,10 +448,7 @@ static int db_traceback (lua_State *L) { static int db_setcstacklimit (lua_State *L) { int limit = (int)luaL_checkinteger(L, 1); int res = lua_setcstacklimit(L, limit); - if (res == 0) - lua_pushboolean(L, 0); - else - lua_pushinteger(L, res); + lua_pushinteger(L, res); return 1; } diff --git a/Lua/ldo.c b/Lua/ldo.c index 5473815a..4b55c31c 100644 --- a/Lua/ldo.c +++ b/Lua/ldo.c @@ -139,8 +139,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { - global_State *g = G(L); - l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci); + l_uint32 oldnCcalls = L->nCcalls; struct lua_longjmp lj; lj.status = LUA_OK; lj.previous = L->errorJmp; /* chain new error handler */ @@ -149,7 +148,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { (*f)(L, ud); ); L->errorJmp = lj.previous; /* restore old error handler */ - L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci; + L->nCcalls = oldnCcalls; return lj.status; } @@ -183,21 +182,20 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { - int lim = L->stacksize; - StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue); + int lim = stacksize(L); + StkId newstack = luaM_reallocvector(L, L->stack, + lim + EXTRA_STACK, newsize + EXTRA_STACK, StackValue); lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); if (unlikely(newstack == NULL)) { /* reallocation failed? */ if (raiseerror) luaM_error(L); else return 0; /* do not raise an error */ } for (; lim < newsize; lim++) - setnilvalue(s2v(newstack + lim)); /* erase new segment */ + setnilvalue(s2v(newstack + lim + EXTRA_STACK)); /* erase new segment */ correctstack(L, L->stack, newstack); L->stack = newstack; - L->stacksize = newsize; - L->stack_last = L->stack + newsize - EXTRA_STACK; + L->stack_last = L->stack + newsize; return 1; } @@ -207,51 +205,73 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { ** is true, raises any error; otherwise, return 0 in case of errors. */ int luaD_growstack (lua_State *L, int n, int raiseerror) { - int size = L->stacksize; - int newsize = 2 * size; /* tentative new size */ - if (unlikely(size > LUAI_MAXSTACK)) { /* need more space after extra size? */ + int size = stacksize(L); + if (unlikely(size > LUAI_MAXSTACK)) { + /* if stack is larger than maximum, thread is already using the + extra space reserved for errors, that is, thread is handling + a stack error; cannot grow further than that. */ + lua_assert(stacksize(L) == ERRORSTACKSIZE); if (raiseerror) luaD_throw(L, LUA_ERRERR); /* error inside message handler */ - else return 0; + return 0; /* if not 'raiseerror', just signal it */ } else { - int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; + int newsize = 2 * size; /* tentative new size */ + int needed = cast_int(L->top - L->stack) + n; if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ newsize = LUAI_MAXSTACK; if (newsize < needed) /* but must respect what was asked for */ newsize = needed; - if (unlikely(newsize > LUAI_MAXSTACK)) { /* stack overflow? */ + if (likely(newsize <= LUAI_MAXSTACK)) + return luaD_reallocstack(L, newsize, raiseerror); + else { /* stack overflow */ /* add extra size to be able to handle the error message */ luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); if (raiseerror) luaG_runerror(L, "stack overflow"); - else return 0; + return 0; } - } /* else no errors */ - return luaD_reallocstack(L, newsize, raiseerror); + } } static int stackinuse (lua_State *L) { CallInfo *ci; + int res; StkId lim = L->top; for (ci = L->ci; ci != NULL; ci = ci->previous) { if (lim < ci->top) lim = ci->top; } lua_assert(lim <= L->stack_last); - return cast_int(lim - L->stack) + 1; /* part of stack in use */ + res = cast_int(lim - L->stack) + 1; /* part of stack in use */ + if (res < LUA_MINSTACK) + res = LUA_MINSTACK; /* ensure a minimum size */ + return res; } +/* +** If stack size is more than 3 times the current use, reduce that size +** to twice the current use. (So, the final stack size is at most 2/3 the +** previous size, and half of its entries are empty.) +** As a particular case, if stack was handling a stack overflow and now +** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than +** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack +** will be reduced to a "regular" size. +*/ void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); - int goodsize = inuse + BASIC_STACK_SIZE; - if (goodsize > LUAI_MAXSTACK) - goodsize = LUAI_MAXSTACK; /* respect stack limit */ + int nsize = inuse * 2; /* proposed new size */ + int max = inuse * 3; /* maximum "reasonable" size */ + if (max > LUAI_MAXSTACK) { + max = LUAI_MAXSTACK; /* respect stack limit */ + if (nsize > LUAI_MAXSTACK) + nsize = LUAI_MAXSTACK; + } /* if thread is currently not handling a stack overflow and its - good size is smaller than current size, shrink its stack */ - if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize) - luaD_reallocstack(L, goodsize, 0); /* ok if that fails */ + size is larger than maximum "reasonable" size, shrink it */ + if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) + luaD_reallocstack(L, nsize, 0); /* ok if that fails */ else /* don't change stack */ condmovestack(L,{},{}); /* (change only for debugging) */ luaE_shrinkCI(L); /* shrink CI list */ @@ -348,7 +368,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) { /* ** Check whether 'func' has a '__call' metafield. If so, put it in the -** stack, below original 'func', so that 'luaD_call' can call it. Raise +** stack, below original 'func', so that 'luaD_precall' can call it. Raise ** an error if there is no '__call' metafield. */ void luaD_tryfuncTM (lua_State *L, StkId func) { @@ -449,12 +469,14 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { /* -** Call a function (C or Lua). The function to be called is at *func. -** The arguments are on the stack, right after the function. -** When returns, all the results are on the stack, starting at the original -** function position. +** Prepares the call to a function (C or Lua). For C functions, also do +** the call. The function to be called is at '*func'. The arguments +** are on the stack, right after the function. Returns the CallInfo +** to be executed, if it was a Lua function. Otherwise (a C function) +** returns NULL, with all the results on the stack, starting at the +** original function position. */ -void luaD_call (lua_State *L, StkId func, int nresults) { +CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { lua_CFunction f; retry: switch (ttypetag(s2v(func))) { @@ -482,7 +504,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) { lua_lock(L); api_checknelems(L, n); luaD_poscall(L, ci, n); - break; + return NULL; } case LUA_VLCL: { /* Lua function */ CallInfo *ci; @@ -494,15 +516,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) { L->ci = ci = next_ci(L); ci->nresults = nresults; ci->u.l.savedpc = p->code; /* starting point */ - ci->callstatus = 0; ci->top = func + 1 + fsize; ci->func = func; L->ci = ci; for (; narg < nfixparams; narg++) setnilvalue(s2v(L->top++)); /* complete missing arguments */ lua_assert(ci->top <= L->stack_last); - luaV_execute(L, ci); /* run the function */ - break; + return ci; } default: { /* not a function */ checkstackGCp(L, 1, func); /* space for metamethod */ @@ -513,17 +533,37 @@ void luaD_call (lua_State *L, StkId func, int nresults) { } +/* +** Call a function (C or Lua) through C. 'inc' can be 1 (increment +** number of recursive invocations in the C stack) or nyci (the same +** plus increment number of non-yieldable calls). +*/ +static void ccall (lua_State *L, StkId func, int nResults, int inc) { + CallInfo *ci; + L->nCcalls += inc; + if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + luaE_checkcstack(L); + if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ + ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ + luaV_execute(L, ci); /* call it */ + } + L->nCcalls -= inc; +} + + +/* +** External interface for 'ccall' +*/ +void luaD_call (lua_State *L, StkId func, int nResults) { + ccall(L, func, nResults, 1); +} + + /* ** Similar to 'luaD_call', but does not allow yields during the call. */ void luaD_callnoyield (lua_State *L, StkId func, int nResults) { - incXCcalls(L); - if (getCcalls(L) <= CSTACKERR) { /* possible C stack overflow? */ - luaE_exitCcall(L); /* to compensate decrement in next call */ - luaE_enterCcall(L); /* check properly */ - } - luaD_call(L, func, nResults); - decXCcalls(L); + ccall(L, func, nResults, nyci); } @@ -601,12 +641,12 @@ static int recover (lua_State *L, int status) { if (ci == NULL) return 0; /* no recovery point */ /* "finish" luaD_pcall */ oldtop = restorestack(L, ci->u2.funcidx); - luaF_close(L, oldtop, status); /* may change the stack */ - oldtop = restorestack(L, ci->u2.funcidx); - luaD_seterrorobj(L, status, oldtop); L->ci = ci; L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ - luaD_shrinkstack(L); + status = luaF_close(L, oldtop, status); /* may change the stack */ + oldtop = restorestack(L, ci->u2.funcidx); + luaD_seterrorobj(L, status, oldtop); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ L->errfunc = ci->u.c.old_errfunc; return 1; /* continue running the coroutine */ } @@ -637,12 +677,12 @@ static void resume (lua_State *L, void *ud) { int n = *(cast(int*, ud)); /* number of arguments */ StkId firstArg = L->top - n; /* first argument */ CallInfo *ci = L->ci; - if (L->status == LUA_OK) { /* starting a coroutine? */ - luaD_call(L, firstArg - 1, LUA_MULTRET); - } + if (L->status == LUA_OK) /* starting a coroutine? */ + ccall(L, firstArg - 1, LUA_MULTRET, 1); /* just call its body */ else { /* resuming from previous yield */ lua_assert(L->status == LUA_YIELD); L->status = LUA_OK; /* mark that it is running (again) */ + luaE_incCstack(L); /* control the C stack */ if (isLua(ci)) /* yielded inside a hook? */ luaV_execute(L, ci); /* just continue running Lua code */ else { /* 'common' yield */ @@ -670,12 +710,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, } else if (L->status != LUA_YIELD) /* ended with errors? */ return resume_error(L, "cannot resume dead coroutine", nargs); - if (from == NULL) - L->nCcalls = CSTACKTHREAD; - else /* correct 'nCcalls' for this thread */ - L->nCcalls = getCcalls(from) - L->nci - CSTACKCF; - if (L->nCcalls <= CSTACKERR) - return resume_error(L, "C stack overflow", nargs); + L->nCcalls = (from) ? getCcalls(from) : 0; luai_userstateresume(L, nargs); api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); status = luaD_rawrunprotected(L, resume, &nargs); @@ -754,7 +789,7 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u, status = luaF_close(L, oldtop, status); oldtop = restorestack(L, old_top); /* previous call may change stack */ luaD_seterrorobj(L, status, oldtop); - luaD_shrinkstack(L); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ } L->errfunc = old_errfunc; return status; diff --git a/Lua/ldo.h b/Lua/ldo.h index 6c6cb285..4d30d072 100644 --- a/Lua/ldo.h +++ b/Lua/ldo.h @@ -59,6 +59,7 @@ LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, int fTransfer, int nTransfer); LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n); +LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); diff --git a/Lua/lfunc.c b/Lua/lfunc.c index 88d45328..c4360f09 100644 --- a/Lua/lfunc.c +++ b/Lua/lfunc.c @@ -53,7 +53,7 @@ void luaF_initupvals (lua_State *L, LClosure *cl) { uv->v = &uv->u.value; /* make it closed */ setnilvalue(uv->v); cl->upvals[i] = uv; - luaC_objbarrier(L, cl, o); + luaC_objbarrier(L, cl, uv); } } diff --git a/Lua/lgc.c b/Lua/lgc.c index 4a7bcaed..bab9beb1 100644 --- a/Lua/lgc.c +++ b/Lua/lgc.c @@ -161,18 +161,17 @@ static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) { /* -** Clear keys for empty entries in tables. If entry is empty -** and its key is not marked, mark its entry as dead. This allows the -** collection of the key, but keeps its entry in the table (its removal -** could break a chain). The main feature of a dead key is that it must -** be different from any other value, to do not disturb searches. -** Other places never manipulate dead keys, because its associated empty -** value is enough to signal that the entry is logically empty. +** Clear keys for empty entries in tables. If entry is empty, mark its +** entry as dead. This allows the collection of the key, but keeps its +** entry in the table: its removal could break a chain and could break +** a table traversal. Other places never manipulate dead keys, because +** its associated empty value is enough to signal that the entry is +** logically empty. */ static void clearkey (Node *n) { lua_assert(isempty(gval(n))); - if (keyiswhite(n)) - setdeadkey(n); /* unused and unmarked key; remove it */ + if (keyiscollectable(n)) + setdeadkey(n); /* unused key; remove it */ } @@ -301,7 +300,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { if (upisopen(uv)) set2gray(uv); /* open upvalues are kept gray */ else - set2black(o); /* closed upvalues are visited here */ + set2black(uv); /* closed upvalues are visited here */ markvalue(g, uv->v); /* mark its content */ break; } @@ -309,7 +308,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { Udata *u = gco2u(o); if (u->nuvalue == 0) { /* no user values? */ markobjectN(g, u->metatable); /* mark its metatable */ - set2black(o); /* nothing else to mark */ + set2black(u); /* nothing else to mark */ break; } /* else... */ @@ -633,9 +632,8 @@ static int traversethread (global_State *g, lua_State *th) { for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) markobject(g, uv); /* open upvalues cannot be collected */ if (g->gcstate == GCSatomic) { /* final traversal? */ - StkId lim = th->stack + th->stacksize; /* real end of stack */ - for (; o < lim; o++) /* clear not-marked stack slice */ - setnilvalue(s2v(o)); + for (; o < th->stack_last + EXTRA_STACK; o++) + setnilvalue(s2v(o)); /* clear dead stack slice */ /* 'remarkupvals' may have removed thread from 'twups' list */ if (!isintwups(th) && th->openupval != NULL) { th->twups = g->twups; /* link it back to the list */ @@ -644,7 +642,7 @@ static int traversethread (global_State *g, lua_State *th) { } else if (!g->gcemergency) luaD_shrinkstack(th); /* do not change stack in emergency cycle */ - return 1 + th->stacksize; + return 1 + stacksize(th); } @@ -771,12 +769,16 @@ static void freeobj (lua_State *L, GCObject *o) { case LUA_VUPVAL: freeupval(L, gco2upv(o)); break; - case LUA_VLCL: - luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); + case LUA_VLCL: { + LClosure *cl = gco2lcl(o); + luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); break; - case LUA_VCCL: - luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); + } + case LUA_VCCL: { + CClosure *cl = gco2ccl(o); + luaM_freemem(L, cl, sizeCclosure(cl->nupvalues)); break; + } case LUA_VTABLE: luaH_free(L, gco2t(o)); break; @@ -788,13 +790,17 @@ static void freeobj (lua_State *L, GCObject *o) { luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); break; } - case LUA_VSHRSTR: - luaS_remove(L, gco2ts(o)); /* remove it from hash table */ - luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); + case LUA_VSHRSTR: { + TString *ts = gco2ts(o); + luaS_remove(L, ts); /* remove it from hash table */ + luaM_freemem(L, ts, sizelstring(ts->shrlen)); break; - case LUA_VLNGSTR: - luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); + } + case LUA_VLNGSTR: { + TString *ts = gco2ts(o); + luaM_freemem(L, ts, sizelstring(ts->u.lnglen)); break; + } default: lua_assert(0); } } diff --git a/Lua/llex.c b/Lua/llex.c index 3d6b2b97..4b8dec99 100644 --- a/Lua/llex.c +++ b/Lua/llex.c @@ -254,9 +254,10 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) { /* -** reads a sequence '[=*[' or ']=*]', leaving the last bracket. -** If sequence is well formed, return its number of '='s + 2; otherwise, -** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...'). +** read a sequence '[=*[' or ']=*]', leaving the last bracket. If +** sequence is well formed, return its number of '='s + 2; otherwise, +** return 1 if it is a single bracket (no '='s and no 2nd bracket); +** otherwise (an unfinished '[==...') return 0. */ static size_t skip_sep (LexState *ls) { size_t count = 0; @@ -481,34 +482,34 @@ static int llex (LexState *ls, SemInfo *seminfo) { } case '=': { next(ls); - if (check_next1(ls, '=')) return TK_EQ; + if (check_next1(ls, '=')) return TK_EQ; /* '==' */ else return '='; } case '<': { next(ls); - if (check_next1(ls, '=')) return TK_LE; - else if (check_next1(ls, '<')) return TK_SHL; + if (check_next1(ls, '=')) return TK_LE; /* '<=' */ + else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */ else return '<'; } case '>': { next(ls); - if (check_next1(ls, '=')) return TK_GE; - else if (check_next1(ls, '>')) return TK_SHR; + if (check_next1(ls, '=')) return TK_GE; /* '>=' */ + else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */ else return '>'; } case '/': { next(ls); - if (check_next1(ls, '/')) return TK_IDIV; + if (check_next1(ls, '/')) return TK_IDIV; /* '//' */ else return '/'; } case '~': { next(ls); - if (check_next1(ls, '=')) return TK_NE; + if (check_next1(ls, '=')) return TK_NE; /* '~=' */ else return '~'; } case ':': { next(ls); - if (check_next1(ls, ':')) return TK_DBCOLON; + if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */ else return ':'; } case '"': case '\'': { /* short literal strings */ @@ -547,7 +548,7 @@ static int llex (LexState *ls, SemInfo *seminfo) { return TK_NAME; } } - else { /* single-char tokens (+ - / ...) */ + else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */ int c = ls->current; next(ls); return c; diff --git a/Lua/llimits.h b/Lua/llimits.h index 48c97f95..d0394831 100644 --- a/Lua/llimits.h +++ b/Lua/llimits.h @@ -234,6 +234,17 @@ typedef l_uint32 Instruction; #endif +/* +** Maximum depth for nested C calls, syntactical nested non-terminals, +** and other features implemented through recursion in C. (Value must +** fit in a 16-bit unsigned integer. It must also be compatible with +** the size of the C stack.) +*/ +#if !defined(LUAI_MAXCCALLS) +#define LUAI_MAXCCALLS 200 +#endif + + /* ** macros that are executed whenever program enters the Lua core ** ('lua_lock') and leaves the core ('lua_unlock') @@ -315,7 +326,8 @@ typedef l_uint32 Instruction; /* exponentiation */ #if !defined(luai_numpow) -#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b)) +#define luai_numpow(L,a,b) \ + ((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b)) #endif /* the others are quite standard operations */ @@ -344,7 +356,7 @@ typedef l_uint32 Instruction; #else /* realloc stack keeping its size */ #define condmovestack(L,pre,pos) \ - { int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_, 0); pos; } + { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; } #endif #if !defined(HARDMEMTESTS) diff --git a/Lua/lobject.c b/Lua/lobject.c index f8ea917a..0e504be0 100644 --- a/Lua/lobject.c +++ b/Lua/lobject.c @@ -258,7 +258,7 @@ static const char *l_str2d (const char *s, lua_Number *result) { if (endptr == NULL) { /* failed? may be a different locale */ char buff[L_MAXLENNUM + 1]; const char *pdot = strchr(s, '.'); - if (strlen(s) > L_MAXLENNUM || pdot == NULL) + if (pdot == NULL || strlen(s) > L_MAXLENNUM) return NULL; /* string too long or no dot; fail */ strcpy(buff, s); /* copy string to buffer */ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ diff --git a/Lua/lobject.h b/Lua/lobject.h index a9d45785..1cc8e757 100644 --- a/Lua/lobject.h +++ b/Lua/lobject.h @@ -21,10 +21,12 @@ */ #define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ #define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ +#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */ + /* -** number of all possible types (including LUA_TNONE) +** number of all possible types (including LUA_TNONE but excluding DEADKEY) */ #define LUA_TOTALTYPES (LUA_TPROTO + 2) @@ -555,7 +557,7 @@ typedef struct Proto { /* ** {================================================================== -** Closures +** Functions ** =================================================================== */ @@ -743,13 +745,13 @@ typedef struct Table { /* -** Use a "nil table" to mark dead keys in a table. Those keys serve -** to keep space for removed entries, which may still be part of -** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE -** set, so these values are considered not collectable and are different -** from any valid value. +** Dead keys in tables have the tag DEADKEY but keep their original +** gcvalue. This distinguishes them from regular keys but allows them to +** be found when searched in a special way. ('next' needs that to find +** keys removed from a table during a traversal.) */ -#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL) +#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY) +#define keyisdead(node) (keytt(node) == LUA_TDEADKEY) /* }================================================================== */ diff --git a/Lua/lopcodes.h b/Lua/lopcodes.h index 122e5d21..120cdd94 100644 --- a/Lua/lopcodes.h +++ b/Lua/lopcodes.h @@ -261,7 +261,7 @@ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ OP_UNM,/* A B R[A] := -R[B] */ OP_BNOT,/* A B R[A] := ~R[B] */ OP_NOT,/* A B R[A] := not R[B] */ -OP_LEN,/* A B R[A] := length of R[B] */ +OP_LEN,/* A B R[A] := #R[B] (length operator) */ OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ @@ -297,7 +297,7 @@ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */ OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ -OP_SETLIST,/* A B C k R[A][(C-1)*FPF+i] := R[A+i], 1 <= i <= B */ +OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ diff --git a/Lua/lparser.c b/Lua/lparser.c index bc7d9a4f..77813a74 100644 --- a/Lua/lparser.c +++ b/Lua/lparser.c @@ -489,12 +489,10 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { } -/* -** Macros to limit the maximum recursion depth while parsing -*/ -#define enterlevel(ls) luaE_enterCcall((ls)->L) +#define enterlevel(ls) luaE_incCstack(ls->L) + -#define leavelevel(ls) luaE_exitCcall((ls)->L) +#define leavelevel(ls) ((ls)->L->nCcalls--) /* @@ -947,7 +945,7 @@ static void setvararg (FuncState *fs, int nparams) { static void parlist (LexState *ls) { - /* parlist -> [ param { ',' param } ] */ + /* parlist -> [ {NAME ','} (NAME | '...') ] */ FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; @@ -955,12 +953,12 @@ static void parlist (LexState *ls) { if (ls->t.token != ')') { /* is 'parlist' not empty? */ do { switch (ls->t.token) { - case TK_NAME: { /* param -> NAME */ + case TK_NAME: { new_localvar(ls, str_checkname(ls)); nparams++; break; } - case TK_DOTS: { /* param -> '...' */ + case TK_DOTS: { luaX_next(ls); isvararg = 1; break; @@ -1625,59 +1623,21 @@ static void forstat (LexState *ls, int line) { } -/* -** Check whether next instruction is a single jump (a 'break', a 'goto' -** to a forward label, or a 'goto' to a backward label with no variable -** to close). If so, set the name of the 'label' it is jumping to -** ("break" for a 'break') or to where it is jumping to ('target') and -** return true. If not a single jump, leave input unchanged, to be -** handled as a regular statement. -*/ -static int issinglejump (LexState *ls, TString **label, int *target) { - if (testnext(ls, TK_BREAK)) { /* a break? */ - *label = luaS_newliteral(ls->L, "break"); - return 1; - } - else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME) - return 0; /* not a valid goto */ - else { - TString *lname = ls->lookahead.seminfo.ts; /* label's id */ - Labeldesc *lb = findlabel(ls, lname); - if (lb) { /* a backward jump? */ - /* does it need to close variables? */ - if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar)) - return 0; /* not a single jump; cannot optimize */ - *target = lb->pc; - } - else /* jump forward */ - *label = lname; - luaX_next(ls); /* skip goto */ - luaX_next(ls); /* skip name */ - return 1; - } -} - - static void test_then_block (LexState *ls, int *escapelist) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ BlockCnt bl; - int line; FuncState *fs = ls->fs; - TString *jlb = NULL; - int target = NO_JUMP; expdesc v; int jf; /* instruction to skip 'then' code (if condition is false) */ luaX_next(ls); /* skip IF or ELSEIF */ expr(ls, &v); /* read condition */ checknext(ls, TK_THEN); - line = ls->linenumber; - if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */ - luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ + if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */ + int line = ls->linenumber; + luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */ + luaX_next(ls); /* skip 'break' */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */ - if (jlb != NULL) /* forward jump? */ - newgotoentry(ls, jlb, line, v.t); /* will be resolved later */ - else /* backward jump */ - luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */ + newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t); while (testnext(ls, ';')) {} /* skip semicolons */ if (block_follow(ls, 0)) { /* jump is the entire block? */ leaveblock(fs); @@ -1686,7 +1646,7 @@ static void test_then_block (LexState *ls, int *escapelist) { else /* must skip over 'then' part if condition is false */ jf = luaK_jump(fs); } - else { /* regular case (not a jump) */ + else { /* regular case (not a break) */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ enterblock(fs, &bl, 0); jf = v.f; @@ -1754,7 +1714,7 @@ static void checktoclose (LexState *ls, int level) { static void localstat (LexState *ls) { - /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */ + /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */ FuncState *fs = ls->fs; int toclose = -1; /* index of to-be-closed variable (if any) */ Vardesc *var; /* last variable */ diff --git a/Lua/lparser.h b/Lua/lparser.h index 618cb010..2e6dae72 100644 --- a/Lua/lparser.h +++ b/Lua/lparser.h @@ -23,7 +23,7 @@ /* kinds of variables/expressions */ typedef enum { - VVOID, /* when 'expdesc' describes the last expression a list, + VVOID, /* when 'expdesc' describes the last expression of a list, this kind means an empty list (so, no expression) */ VNIL, /* constant nil */ VTRUE, /* constant true */ @@ -38,7 +38,8 @@ typedef enum { VLOCAL, /* local variable; var.sidx = stack index (local register); var.vidx = relative index in 'actvar.arr' */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ - VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */ + VCONST, /* compile-time variable; + info = absolute index in 'actvar.arr' */ VINDEXED, /* indexed variable; ind.t = table register; ind.idx = key's R index */ diff --git a/Lua/lstate.c b/Lua/lstate.c index 86b3761f..1c7b8791 100644 --- a/Lua/lstate.c +++ b/Lua/lstate.c @@ -76,7 +76,7 @@ static unsigned int luai_makeseed (lua_State *L) { addbuff(buff, p, &h); /* local variable */ addbuff(buff, p, &lua_newstate); /* public function */ lua_assert(p == sizeof(buff)); - return luaS_hash(buff, p, h, 1); + return luaS_hash(buff, p, h); } #endif @@ -97,66 +97,14 @@ void luaE_setdebt (global_State *g, l_mem debt) { LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { - global_State *g = G(L); - int ccalls; - luaE_freeCI(L); /* release unused CIs */ - ccalls = getCcalls(L); - if (limit >= 40000) - return 0; /* out of bounds */ - limit += CSTACKERR; - if (L != g-> mainthread) - return 0; /* only main thread can change the C stack */ - else if (ccalls <= CSTACKERR) - return 0; /* handling overflow */ - else { - int diff = limit - g->Cstacklimit; - if (ccalls + diff <= CSTACKERR) - return 0; /* new limit would cause an overflow */ - g->Cstacklimit = limit; /* set new limit */ - L->nCcalls += diff; /* correct 'nCcalls' */ - return limit - diff - CSTACKERR; /* success; return previous limit */ - } -} - - -/* -** Decrement count of "C calls" and check for overflows. In case of -** a stack overflow, check appropriate error ("regular" overflow or -** overflow while handling stack overflow). If 'nCcalls' is smaller -** than CSTACKERR but larger than CSTACKMARK, it means it has just -** entered the "overflow zone", so the function raises an overflow -** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is -** already handling an overflow) but larger than CSTACKERRMARK, does -** not report an error (to allow message handling to work). Otherwise, -** report a stack overflow while handling a stack overflow (probably -** caused by a repeating error in the message handling function). -*/ - -void luaE_enterCcall (lua_State *L) { - int ncalls = getCcalls(L); - L->nCcalls--; - if (ncalls <= CSTACKERR) { /* possible overflow? */ - luaE_freeCI(L); /* release unused CIs */ - ncalls = getCcalls(L); /* update call count */ - if (ncalls <= CSTACKERR) { /* still overflow? */ - if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */ - luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ - else if (ncalls >= CSTACKMARK) { - /* not in error-handling zone; raise the error now */ - L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */ - luaG_runerror(L, "C stack overflow"); - } - /* else stack is in the error-handling zone; - allow message handler to work */ - } - } + UNUSED(L); UNUSED(limit); + return LUAI_MAXCCALLS; /* warning?? */ } CallInfo *luaE_extendCI (lua_State *L) { CallInfo *ci; lua_assert(L->ci->next == NULL); - luaE_enterCcall(L); ci = luaM_new(L, CallInfo); lua_assert(L->ci->next == NULL); L->ci->next = ci; @@ -175,13 +123,11 @@ void luaE_freeCI (lua_State *L) { CallInfo *ci = L->ci; CallInfo *next = ci->next; ci->next = NULL; - L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */ while ((ci = next) != NULL) { next = ci->next; luaM_free(L, ci); L->nci--; } - L->nCcalls -= L->nci; /* adjust result */ } @@ -194,7 +140,6 @@ void luaE_shrinkCI (lua_State *L) { CallInfo *next; if (ci == NULL) return; /* no extra elements */ - L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */ while ((next = ci->next) != NULL) { /* two extra elements? */ CallInfo *next2 = next->next; /* next's next */ ci->next = next2; /* remove next from the list */ @@ -207,19 +152,39 @@ void luaE_shrinkCI (lua_State *L) { ci = next2; /* continue */ } } - L->nCcalls -= L->nci; /* adjust result */ +} + + +/* +** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS. +** If equal, raises an overflow error. If value is larger than +** LUAI_MAXCCALLS (which means it is handling an overflow) but +** not much larger, does not report an error (to allow overflow +** handling to work). +*/ +void luaE_checkcstack (lua_State *L) { + if (getCcalls(L) == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ +} + + +LUAI_FUNC void luaE_incCstack (lua_State *L) { + L->nCcalls++; + if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + luaE_checkcstack(L); } static void stack_init (lua_State *L1, lua_State *L) { int i; CallInfo *ci; /* initialize stack array */ - L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, StackValue); - L1->stacksize = BASIC_STACK_SIZE; - for (i = 0; i < BASIC_STACK_SIZE; i++) + L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); + for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) setnilvalue(s2v(L1->stack + i)); /* erase new stack */ L1->top = L1->stack; - L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK; + L1->stack_last = L1->stack + BASIC_STACK_SIZE; /* initialize first ci */ ci = &L1->base_ci; ci->next = ci->previous = NULL; @@ -240,7 +205,7 @@ static void freestack (lua_State *L) { L->ci = &L->base_ci; /* free the entire 'ci' list */ luaE_freeCI(L); lua_assert(L->nci == 0); - luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ + luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */ } @@ -290,7 +255,6 @@ static void preinit_thread (lua_State *L, global_State *g) { L->stack = NULL; L->ci = NULL; L->nci = 0; - L->stacksize = 0; L->twups = L; /* thread has no upvalues */ L->errorJmp = NULL; L->hook = NULL; @@ -335,7 +299,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { setthvalue2s(L, L->top, L1); api_incr_top(L); preinit_thread(L1, g); - L1->nCcalls = getCcalls(L); + L1->nCcalls = 0; L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; @@ -396,7 +360,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { preinit_thread(L, g); g->allgc = obj2gco(L); /* by now, only object is the main thread */ L->next = NULL; - g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR; + L->nCcalls = 0; incnny(L); /* main thread is always non yieldable */ g->frealloc = f; g->ud = ud; diff --git a/Lua/lstate.h b/Lua/lstate.h index c1c38204..cbcf07e2 100644 --- a/Lua/lstate.h +++ b/Lua/lstate.h @@ -87,49 +87,13 @@ /* -** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of -** how many "C calls" it still can do in the C stack, to avoid C-stack -** overflow. This count is very rough approximation; it considers only -** recursive functions inside the interpreter, as non-recursive calls -** can be considered using a fixed (although unknown) amount of stack -** space. -** -** The count has two parts: the lower part is the count itself; the -** higher part counts the number of non-yieldable calls in the stack. -** (They are together so that we can change both with one instruction.) -** -** Because calls to external C functions can use an unknown amount -** of space (e.g., functions using an auxiliary buffer), calls -** to these functions add more than one to the count (see CSTACKCF). -** -** The proper count excludes the number of CallInfo structures allocated -** by Lua, as a kind of "potential" calls. So, when Lua calls a function -** (and "consumes" one CallInfo), it needs neither to decrement nor to -** check 'nCcalls', as its use of C stack is already accounted for. +** About 'nCcalls': This count has two parts: the lower 16 bits counts +** the number of recursive invocations in the C stack; the higher +** 16 bits counts the number of non-yieldable calls in the stack. +** (They are together so that we can change and save both with one +** instruction.) */ -/* number of "C stack slots" used by an external C function */ -#define CSTACKCF 10 - - -/* -** The C-stack size is sliced in the following zones: -** - larger than CSTACKERR: normal stack; -** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow; -** - [CSTACKCF, CSTACKERRMARK]: error-handling zone; -** - below CSTACKERRMARK: buffer zone to signal overflow during overflow; -** (Because the counter can be decremented CSTACKCF at once, we need -** the so called "buffer zones", with at least that size, to properly -** detect a change from one zone to the next.) -*/ -#define CSTACKERR (8 * CSTACKCF) -#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2)) -#define CSTACKERRMARK (CSTACKCF + 2) - - -/* initial limit for the C-stack of threads */ -#define CSTACKTHREAD (2 * CSTACKERR) - /* true if this thread does not have non-yieldable calls in the stack */ #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) @@ -144,13 +108,8 @@ /* Decrement the number of non-yieldable calls */ #define decnny(L) ((L)->nCcalls -= 0x10000) -/* Increment the number of non-yieldable calls and decrement nCcalls */ -#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF) - -/* Decrement the number of non-yieldable calls and increment nCcalls */ -#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF) - - +/* Non-yieldable call increment */ +#define nyci (0x10000 | 1) @@ -168,12 +127,20 @@ struct lua_longjmp; /* defined in ldo.c */ #endif -/* extra stack space to handle TM calls and some other extras */ +/* +** Extra stack space to handle TM calls and some other extras. This +** space is not included in 'stack_last'. It is used only to avoid stack +** checks, either because the element will be promptly popped or because +** there will be a stack check soon after the push. Function frames +** never use this extra space, so it does not need to be kept clean. +*/ #define EXTRA_STACK 5 #define BASIC_STACK_SIZE (2*LUA_MINSTACK) +#define stacksize(th) cast_int((th)->stack_last - (th)->stack) + /* kinds of Garbage Collection */ #define KGC_INC 0 /* incremental gc */ @@ -224,14 +191,15 @@ typedef struct CallInfo { */ #define CIST_OAH (1<<0) /* original value of 'allowhook' */ #define CIST_C (1<<1) /* call is running a C function */ -#define CIST_HOOKED (1<<2) /* call is running a debug hook */ -#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */ -#define CIST_TAIL (1<<4) /* call was tail called */ -#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */ -#define CIST_FIN (1<<6) /* call is running a finalizer */ -#define CIST_TRAN (1<<7) /* 'ci' has transfer information */ +#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ +#define CIST_HOOKED (1<<3) /* call is running a debug hook */ +#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ +#define CIST_TAIL (1<<5) /* call was tail called */ +#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ +#define CIST_FIN (1<<7) /* call is running a finalizer */ +#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ #if defined(LUA_COMPAT_LT_LE) -#define CIST_LEQ (1<<8) /* using __lt for __le */ +#define CIST_LEQ (1<<9) /* using __lt for __le */ #endif /* active function is a Lua function */ @@ -296,7 +264,6 @@ typedef struct global_State { TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ lua_WarnFunction warnf; /* warning function */ void *ud_warn; /* auxiliary data to 'warnf' */ - unsigned int Cstacklimit; /* current limit for the C stack */ } global_State; @@ -311,7 +278,7 @@ struct lua_State { StkId top; /* first free slot in the stack */ global_State *l_G; CallInfo *ci; /* call info for current function */ - StkId stack_last; /* last free slot in the stack */ + StkId stack_last; /* end of stack (last element + 1) */ StkId stack; /* stack base */ UpVal *openupval; /* list of open upvalues in this stack */ GCObject *gclist; @@ -320,9 +287,8 @@ struct lua_State { CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ volatile lua_Hook hook; ptrdiff_t errfunc; /* current error handling function (stack index) */ - l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */ + l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */ int oldpc; /* last pc traced */ - int stacksize; int basehookcount; int hookcount; volatile l_signalT hookmask; @@ -389,12 +355,11 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L); -LUAI_FUNC void luaE_enterCcall (lua_State *L); +LUAI_FUNC void luaE_checkcstack (lua_State *L); +LUAI_FUNC void luaE_incCstack (lua_State *L); LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); -#define luaE_exitCcall(L) ((L)->nCcalls++) - #endif diff --git a/Lua/lstring.c b/Lua/lstring.c index 6f157473..138871c7 100644 --- a/Lua/lstring.c +++ b/Lua/lstring.c @@ -22,16 +22,6 @@ #include "lstring.h" -/* -** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a long string to -** compute its hash -*/ -#if !defined(LUAI_HASHLIMIT) -#define LUAI_HASHLIMIT 5 -#endif - - - /* ** Maximum size for string table. */ @@ -50,10 +40,9 @@ int luaS_eqlngstr (TString *a, TString *b) { } -unsigned int luaS_hash (const char *str, size_t l, unsigned int seed, - size_t step) { +unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { unsigned int h = seed ^ cast_uint(l); - for (; l >= step; l -= step) + for (; l > 0; l--) h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); return h; } @@ -63,8 +52,7 @@ unsigned int luaS_hashlongstr (TString *ts) { lua_assert(ts->tt == LUA_VLNGSTR); if (ts->extra == 0) { /* no hash? */ size_t len = ts->u.lnglen; - size_t step = (len >> LUAI_HASHLIMIT) + 1; - ts->hash = luaS_hash(getstr(ts), len, ts->hash, step); + ts->hash = luaS_hash(getstr(ts), len, ts->hash); ts->extra = 1; /* now it has its hash */ } return ts->hash; @@ -201,7 +189,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) { TString *ts; global_State *g = G(L); stringtable *tb = &g->strt; - unsigned int h = luaS_hash(str, l, g->seed, 1); + unsigned int h = luaS_hash(str, l, g->seed); TString **list = &tb->hash[lmod(h, tb->size)]; lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ for (ts = *list; ts != NULL; ts = ts->u.hnext) { diff --git a/Lua/lstring.h b/Lua/lstring.h index a413a9d3..450c2390 100644 --- a/Lua/lstring.h +++ b/Lua/lstring.h @@ -41,8 +41,7 @@ #define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) -LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, - unsigned int seed, size_t step); +LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); LUAI_FUNC void luaS_resize (lua_State *L, int newsize); diff --git a/Lua/lstrlib.c b/Lua/lstrlib.c index 2ba8bde4..940a14ca 100644 --- a/Lua/lstrlib.c +++ b/Lua/lstrlib.c @@ -1365,7 +1365,6 @@ typedef union Ftypes { float f; double d; lua_Number n; - char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ } Ftypes; @@ -1535,12 +1534,10 @@ static void packint (luaL_Buffer *b, lua_Unsigned n, ** Copy 'size' bytes from 'src' to 'dest', correcting endianness if ** given 'islittle' is different from native endianness. */ -static void copywithendian (volatile char *dest, volatile const char *src, +static void copywithendian (char *dest, const char *src, int size, int islittle) { - if (islittle == nativeendian.little) { - while (size-- != 0) - *(dest++) = *(src++); - } + if (islittle == nativeendian.little) + memcpy(dest, src, size); else { dest += size - 1; while (size-- != 0) @@ -1584,14 +1581,14 @@ static int str_pack (lua_State *L) { break; } case Kfloat: { /* floating-point options */ - volatile Ftypes u; + Ftypes u; char *buff = luaL_prepbuffsize(&b, size); lua_Number n = luaL_checknumber(L, arg); /* get argument */ if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ else if (size == sizeof(u.d)) u.d = (double)n; else u.n = n; /* move 'u' to final result, correcting endianness if needed */ - copywithendian(buff, u.buff, size, h.islittle); + copywithendian(buff, (char *)&u, size, h.islittle); luaL_addsize(&b, size); break; } @@ -1717,9 +1714,9 @@ static int str_unpack (lua_State *L) { break; } case Kfloat: { - volatile Ftypes u; + Ftypes u; lua_Number num; - copywithendian(u.buff, data + pos, size, h.islittle); + copywithendian((char *)&u, data + pos, size, h.islittle); if (size == sizeof(u.f)) num = (lua_Number)u.f; else if (size == sizeof(u.d)) num = (lua_Number)u.d; else num = u.n; @@ -1738,7 +1735,7 @@ static int str_unpack (lua_State *L) { break; } case Kzstr: { - size_t len = (int)strlen(data + pos); + size_t len = strlen(data + pos); luaL_argcheck(L, pos + len < ld, 2, "unfinished string for format 'z'"); lua_pushlstring(L, data + pos, len); diff --git a/Lua/ltable.c b/Lua/ltable.c index 5a0d066f..7e7cbed9 100644 --- a/Lua/ltable.c +++ b/Lua/ltable.c @@ -166,17 +166,30 @@ static Node *mainpositionTV (const Table *t, const TValue *key) { /* -** Check whether key 'k1' is equal to the key in node 'n2'. -** This equality is raw, so there are no metamethods. Floats -** with integer values have been normalized, so integers cannot -** be equal to floats. It is assumed that 'eqshrstr' is simply -** pointer equality, so that short strings are handled in the -** default case. -*/ -static int equalkey (const TValue *k1, const Node *n2) { - if (rawtt(k1) != keytt(n2)) /* not the same variants? */ +** Check whether key 'k1' is equal to the key in node 'n2'. This +** equality is raw, so there are no metamethods. Floats with integer +** values have been normalized, so integers cannot be equal to +** floats. It is assumed that 'eqshrstr' is simply pointer equality, so +** that short strings are handled in the default case. +** A true 'deadok' means to accept dead keys as equal to their original +** values. All dead keys are compared in the default case, by pointer +** identity. (Only collectable objects can produce dead keys.) Note that +** dead long strings are also compared by identity. +** Once a key is dead, its corresponding value may be collected, and +** then another value can be created with the same address. If this +** other value is given to 'next', 'equalkey' will signal a false +** positive. In a regular traversal, this situation should never happen, +** as all keys given to 'next' came from the table itself, and therefore +** could not have been collected. Outside a regular traversal, we +** have garbage in, garbage out. What is relevant is that this false +** positive does not break anything. (In particular, 'next' will return +** some other valid item on the table or nil.) +*/ +static int equalkey (const TValue *k1, const Node *n2, int deadok) { + if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */ + !(deadok && keyisdead(n2) && iscollectable(k1))) return 0; /* cannot be same key */ - switch (ttypetag(k1)) { + switch (keytt(n2)) { case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; case LUA_VNUMINT: @@ -187,7 +200,7 @@ static int equalkey (const TValue *k1, const Node *n2) { return pvalue(k1) == pvalueraw(keyval(n2)); case LUA_VLCF: return fvalue(k1) == fvalueraw(keyval(n2)); - case LUA_VLNGSTR: + case ctb(LUA_VLNGSTR): return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); default: return gcvalue(k1) == gcvalueraw(keyval(n2)); @@ -251,11 +264,12 @@ static unsigned int setlimittosize (Table *t) { /* ** "Generic" get version. (Not that generic: not valid for integers, ** which may be in array part, nor for floats with integral values.) +** See explanation about 'deadok' in function 'equalkey'. */ -static const TValue *getgeneric (Table *t, const TValue *key) { +static const TValue *getgeneric (Table *t, const TValue *key, int deadok) { Node *n = mainpositionTV(t, key); for (;;) { /* check whether 'key' is somewhere in the chain */ - if (equalkey(key, n)) + if (equalkey(key, n, deadok)) return gval(n); /* that's it */ else { int nx = gnext(n); @@ -292,7 +306,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key, if (i - 1u < asize) /* is 'key' inside array part? */ return i; /* yes; that's the index */ else { - const TValue *n = getgeneric(t, key); + const TValue *n = getgeneric(t, key, 1); if (unlikely(isabstkey(n))) luaG_runerror(L, "invalid key to 'next'"); /* key not found */ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ @@ -730,7 +744,7 @@ const TValue *luaH_getstr (Table *t, TString *key) { else { /* for long strings, use generic case */ TValue ko; setsvalue(cast(lua_State *, NULL), &ko, key); - return getgeneric(t, &ko); + return getgeneric(t, &ko, 0); } } @@ -750,7 +764,7 @@ const TValue *luaH_get (Table *t, const TValue *key) { /* else... */ } /* FALLTHROUGH */ default: - return getgeneric(t, key); + return getgeneric(t, key, 0); } } diff --git a/Lua/lua.h b/Lua/lua.h index 08c6a64a..c9d64d7f 100644 --- a/Lua/lua.h +++ b/Lua/lua.h @@ -18,7 +18,7 @@ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "4" -#define LUA_VERSION_RELEASE "1" +#define LUA_VERSION_RELEASE "2" #define LUA_VERSION_NUM 504 #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0) diff --git a/Lua/luaconf.h b/Lua/luaconf.h index bdf927e7..d9cf18ca 100644 --- a/Lua/luaconf.h +++ b/Lua/luaconf.h @@ -36,21 +36,6 @@ ** ===================================================================== */ -/* -@@ LUAI_MAXCSTACK defines the maximum depth for nested calls and -** also limits the maximum depth of other recursive algorithms in -** the implementation, such as syntactic analysis. A value too -** large may allow the interpreter to crash (C-stack overflow). -** The default value seems ok for regular machines, but may be -** too high for restricted hardware. -** The test file 'cstack.lua' may help finding a good limit. -** (It will crash with a limit too high.) -*/ -#if !defined(LUAI_MAXCSTACK) -#define LUAI_MAXCSTACK 2000 -#endif - - /* @@ LUA_USE_C89 controls the use of non-ISO-C89 features. ** Define it if you want Lua to avoid the use of a few C99 features diff --git a/Lua/lvm.c b/Lua/lvm.c index 08681af1..aa3b22bf 100644 --- a/Lua/lvm.c +++ b/Lua/lvm.c @@ -229,7 +229,7 @@ static int forprep (lua_State *L, StkId ra) { count /= l_castS2U(-(step + 1)) + 1u; } /* store the counter in place of the limit (which won't be - needed anymore */ + needed anymore) */ setivalue(plimit, l_castU2S(count)); } } @@ -1092,15 +1092,11 @@ void luaV_finishOp (lua_State *L) { #define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) /* -** Protect code that will finish the loop (returns) or can only raise -** errors. (That is, it will not return to the interpreter main loop -** after changing the stack or hooks.) +** Protect code that can only raise errors. (That is, it cannnot change +** the stack or hooks.) */ #define halfProtect(exp) (savestate(L,ci), (exp)) -/* idem, but without changing the stack */ -#define halfProtectNT(exp) (savepc(L), (exp)) - /* 'c' is the limit of live values in the stack */ #define checkGC(L,c) \ { luaC_condGC(L, (savepc(L), L->top = (c)), \ @@ -1132,17 +1128,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) { #if LUA_USE_JUMPTABLE #include "ljumptab.h" #endif - tailcall: + startfunc: trap = L->hookmask; + returning: /* trap already set */ cl = clLvalue(s2v(ci->func)); k = cl->p->k; pc = ci->u.l.savedpc; if (trap) { - if (cl->p->is_vararg) - trap = 0; /* hooks will start after VARARGPREP instruction */ - else if (pc == cl->p->code) /* first instruction (not resuming)? */ - luaD_hookcall(L, ci); - ci->u.l.trap = 1; /* there may be other hooks */ + if (pc == cl->p->code) { /* first instruction (not resuming)? */ + if (cl->p->is_vararg) + trap = 0; /* hooks will start after VARARGPREP instruction */ + else /* check 'call' hook */ + luaD_hookcall(L, ci); + } + ci->u.l.trap = 1; /* assume trap is on, for now */ } base = ci->func + 1; /* main loop of interpreter */ @@ -1151,7 +1150,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { StkId ra; /* instruction's A register */ vmfetch(); lua_assert(base == ci->func + 1); - lua_assert(base <= L->top && L->top < L->stack + L->stacksize); + lua_assert(base <= L->top && L->top < L->stack_last); /* invalidate top for instructions not expecting it */ lua_assert(isIT(i) || (cast_void(L->top = base), 1)); vmdispatch (GET_OPCODE(i)) { @@ -1606,24 +1605,32 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_CALL) { + CallInfo *newci; int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) /* fixed number of arguments? */ L->top = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ - ProtectNT(luaD_call(L, ra, nresults)); + savepc(L); /* in case of errors */ + if ((newci = luaD_precall(L, ra, nresults)) == NULL) + updatetrap(ci); /* C call; nothing else to be done */ + else { /* Lua call: run function in this same C frame */ + ci = newci; + ci->callstatus = 0; /* call re-uses 'luaV_execute' */ + goto startfunc; + } vmbreak; } vmcase(OP_TAILCALL) { int b = GETARG_B(i); /* number of arguments + 1 (function) */ int nparams1 = GETARG_C(i); - /* delat is virtual 'func' - real 'func' (vararg functions) */ + /* delta is virtual 'func' - real 'func' (vararg functions) */ int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; if (b != 0) L->top = ra + b; else /* previous instruction set top */ b = cast_int(L->top - ra); - savepc(ci); /* some calls here can raise errors */ + savepc(ci); /* several calls here can raise errors */ if (TESTARG_k(i)) { /* close upvalues from current call; the compiler ensures that there are no to-be-closed variables here, so this @@ -1637,16 +1644,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) { checkstackGCp(L, 1, ra); } if (!ttisLclosure(s2v(ra))) { /* C function? */ - luaD_call(L, ra, LUA_MULTRET); /* call it */ + luaD_precall(L, ra, LUA_MULTRET); /* call it */ updatetrap(ci); updatestack(ci); /* stack may have been relocated */ - ci->func -= delta; - luaD_poscall(L, ci, cast_int(L->top - ra)); - return; + ci->func -= delta; /* restore 'func' (if vararg) */ + luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */ + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; /* caller returns after the tail call */ } - ci->func -= delta; + ci->func -= delta; /* restore 'func' (if vararg) */ luaD_pretailcall(L, ci, ra, b); /* prepare call frame */ - goto tailcall; + goto startfunc; /* execute the callee */ } vmcase(OP_RETURN) { int n = GETARG_B(i) - 1; /* number of results */ @@ -1665,12 +1673,15 @@ void luaV_execute (lua_State *L, CallInfo *ci) { ci->func -= ci->u.l.nextraargs + nparams1; L->top = ra + n; /* set call for 'luaD_poscall' */ luaD_poscall(L, ci, n); - return; + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; } vmcase(OP_RETURN0) { if (L->hookmask) { L->top = ra; - halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */ + savepc(ci); + luaD_poscall(L, ci, 0); /* no hurry... */ + trap = 1; } else { /* do the 'poscall' here */ int nres = ci->nresults; @@ -1679,12 +1690,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { while (nres-- > 0) setnilvalue(s2v(L->top++)); /* all results are nil */ } - return; + goto ret; } vmcase(OP_RETURN1) { if (L->hookmask) { L->top = ra + 1; - halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */ + savepc(ci); + luaD_poscall(L, ci, 1); /* no hurry... */ + trap = 1; } else { /* do the 'poscall' here */ int nres = ci->nresults; @@ -1698,7 +1711,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) { setnilvalue(s2v(L->top++)); } } - return; + ret: /* return from a Lua function */ + if (ci->callstatus & CIST_FRESH) + return; /* end this frame */ + else { + ci = ci->previous; + goto returning; /* continue running caller in this frame */ + } } vmcase(OP_FORLOOP) { if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ From f8f40e0604cb2516a0d6ea58accce8080cdbf616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 9 Mar 2021 15:52:53 +0800 Subject: [PATCH 117/171] [skip ci] Update Security Analysis --- .github/workflows/codacy-analysis.yml | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/codacy-analysis.yml diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml new file mode 100644 index 00000000..3721152e --- /dev/null +++ b/.github/workflows/codacy-analysis.yml @@ -0,0 +1,49 @@ +# This workflow checks out code, performs a Codacy security scan +# and integrates the results with the +# GitHub Advanced Security code scanning feature. For more information on +# the Codacy security scan action usage and parameters, see +# https://github.com/codacy/codacy-analysis-cli-action. +# For more information on Codacy Analysis CLI in general, see +# https://github.com/codacy/codacy-analysis-cli. + +name: Codacy Security Scan + +on: + push: + branches: [ Shiki ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ Shiki ] + schedule: + - cron: '36 20 * * 4' + +jobs: + codacy-security-scan: + name: Codacy Security Scan + runs-on: ubuntu-latest + steps: + # Checkout the repository to the GitHub Actions runner + - name: Checkout code + uses: actions/checkout@v2 + + # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis + - name: Run Codacy Analysis CLI + uses: codacy/codacy-analysis-cli-action@1.1.0 + with: + # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository + # You can also omit the token and run the tools that support default configurations + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + verbose: true + output: results.sarif + format: sarif + # Adjust severity of non-security issues + gh-code-scanning-compat: true + # Force 0 exit code to allow SARIF file generation + # This will handover control about PR rejection to the GitHub side + max-allowed-issues: 2147483647 + + # Upload the SARIF file generated in the previous step + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: results.sarif From 8b423a7a696b003afb4830285c861027f7daa176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 9 Mar 2021 16:05:54 +0800 Subject: [PATCH 118/171] Disable ARM64 Windows build --- .github/workflows/cmake.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 266bdd49..2710a5ae 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -16,8 +16,6 @@ jobs: triplet: x86-windows-static - type: x64 triplet: x64-windows-static - - type: ARM64 - triplet: arm64-windows-static runs-on: windows-2019 From 05beee4717e1019da6e11aa61e1ac2f43acabc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 9 Mar 2021 16:57:43 +0800 Subject: [PATCH 119/171] [skip ci] Explicitly set build type --- .github/workflows/cmake.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 2710a5ae..fb5eaae2 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -2,12 +2,10 @@ name: CMake on: [push, pull_request] -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - jobs: build-windows: + env: + BUILD_TYPE: Release strategy: fail-fast: false matrix: @@ -73,6 +71,8 @@ jobs: path: "**/w4123.Dice.dll" build-macos: + env: + BUILD_TYPE: Release strategy: fail-fast: false matrix: @@ -135,6 +135,8 @@ jobs: path: "**/w4123.Dice.dll" build-linux: + env: + BUILD_TYPE: Release strategy: fail-fast: false matrix: @@ -222,6 +224,7 @@ jobs: build-android: env: ANDROID_NDK: $ANDROID_NDK_HOME + BUILD_TYPE: Release strategy: fail-fast: false matrix: @@ -264,7 +267,7 @@ jobs: - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=21 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake -DVCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=21 - name: Build working-directory: ${{github.workspace}}/build From b31c1951b56d68986c2ce229f552a8bd5e769642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Tue, 9 Mar 2021 16:57:56 +0800 Subject: [PATCH 120/171] Delete codacy-analysis.yml --- .github/workflows/codacy-analysis.yml | 49 --------------------------- 1 file changed, 49 deletions(-) delete mode 100644 .github/workflows/codacy-analysis.yml diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml deleted file mode 100644 index 3721152e..00000000 --- a/.github/workflows/codacy-analysis.yml +++ /dev/null @@ -1,49 +0,0 @@ -# This workflow checks out code, performs a Codacy security scan -# and integrates the results with the -# GitHub Advanced Security code scanning feature. For more information on -# the Codacy security scan action usage and parameters, see -# https://github.com/codacy/codacy-analysis-cli-action. -# For more information on Codacy Analysis CLI in general, see -# https://github.com/codacy/codacy-analysis-cli. - -name: Codacy Security Scan - -on: - push: - branches: [ Shiki ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ Shiki ] - schedule: - - cron: '36 20 * * 4' - -jobs: - codacy-security-scan: - name: Codacy Security Scan - runs-on: ubuntu-latest - steps: - # Checkout the repository to the GitHub Actions runner - - name: Checkout code - uses: actions/checkout@v2 - - # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - - name: Run Codacy Analysis CLI - uses: codacy/codacy-analysis-cli-action@1.1.0 - with: - # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository - # You can also omit the token and run the tools that support default configurations - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - verbose: true - output: results.sarif - format: sarif - # Adjust severity of non-security issues - gh-code-scanning-compat: true - # Force 0 exit code to allow SARIF file generation - # This will handover control about PR rejection to the GitHub side - max-allowed-issues: 2147483647 - - # Upload the SARIF file generated in the previous step - - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v1 - with: - sarif_file: results.sarif From 78b563998db2a9a6982afd66c279706590b24688 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 11 Mar 2021 17:12:17 +0800 Subject: [PATCH 121/171] Strip Release Binary --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a463182d..d51c5246 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,3 +53,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL Android) target_include_directories(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/include) target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/lib/libiconv.a) endif() + +if(NOT (CMAKE_CXX_COMPILER_ID STREUQAL "MSVC") + add_link_options($<$:-s>) +endif() From 179f0bb42687515d94a64882a48501582a7fc020 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 11 Mar 2021 17:16:57 +0800 Subject: [PATCH 122/171] Strip Release Binary --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d51c5246..f666f160 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL Android) target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/lib/libiconv.a) endif() -if(NOT (CMAKE_CXX_COMPILER_ID STREUQAL "MSVC") +if(NOT (CMAKE_CXX_COMPILER_ID STREUQAL "MSVC")) add_link_options($<$:-s>) endif() From f591fb999a060296b5ac52b6c6e39cf068c04e15 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 11 Mar 2021 17:25:05 +0800 Subject: [PATCH 123/171] Strip Release Binary --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f666f160..55cecfc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL Android) target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/lib/libiconv.a) endif() -if(NOT (CMAKE_CXX_COMPILER_ID STREUQAL "MSVC")) +if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")) add_link_options($<$:-s>) endif() From 6f8ea51cbb5f39d6fd7fad2b2169f4b0acd7c9a9 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 11 Mar 2021 17:57:30 +0800 Subject: [PATCH 124/171] Strip Release Binary --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55cecfc7..34edaff0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,5 +55,5 @@ if(CMAKE_SYSTEM_NAME STREQUAL Android) endif() if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")) - add_link_options($<$:-s>) + set_target_properties(w4123.Dice PROPERTIES LINK_FLAGS_RELEASE -s) endif() From c82258e4c9fb4ca4f68988b862f4ac986a80404b Mon Sep 17 00:00:00 2001 From: Tsubasahane Date: Thu, 11 Mar 2021 20:56:31 +0800 Subject: [PATCH 125/171] Update RDConstant.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HP=体力 => HP=生命 --- Dice/RDConstant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index 4c1b0efe..b35e3230 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -67,7 +67,7 @@ static std::map SkillNameReplace = { {"edu", ""}, {"mov", "ƶ"}, {"san", ""}, - {"hp", ""}, + {"hp", ""}, {"mp", "ħ"}, {"", ""}, {"", "ʹ"}, From 399fa24190e5a972c41ad70d4fedfb328792e318 Mon Sep 17 00:00:00 2001 From: Tsubasahane Date: Thu, 11 Mar 2021 21:10:41 +0800 Subject: [PATCH 126/171] Update RDConstant.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 体力=生命 --- Dice/RDConstant.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index b35e3230..bce6c1dc 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -68,6 +68,7 @@ static std::map SkillNameReplace = { {"mov", "ƶ"}, {"san", ""}, {"hp", ""}, + {"", ""} {"mp", "ħ"}, {"", ""}, {"", "ʹ"}, From 14f8cf1945370cd02bd5bff3b4c317d794c68c08 Mon Sep 17 00:00:00 2001 From: Tsubasahane Date: Thu, 11 Mar 2021 21:17:33 +0800 Subject: [PATCH 127/171] Update RDConstant.h --- Dice/RDConstant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/RDConstant.h b/Dice/RDConstant.h index bce6c1dc..d0719f0d 100644 --- a/Dice/RDConstant.h +++ b/Dice/RDConstant.h @@ -68,7 +68,7 @@ static std::map SkillNameReplace = { {"mov", "ƶ"}, {"san", ""}, {"hp", ""}, - {"", ""} + {"", ""}, {"mp", "ħ"}, {"", ""}, {"", "ʹ"}, From a62ceb191efe5d57d9c8e916c233c178c8464517 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sun, 14 Mar 2021 23:58:56 +0800 Subject: [PATCH 128/171] Use filesystem instead of raw string for crossplatform support --- Dice/BlackListManager.cpp | 26 +-- Dice/BlackListManager.h | 7 +- Dice/Dice.cpp | 166 +++++++++-------- Dice/DiceCensor.cpp | 7 +- Dice/DiceCensor.h | 3 +- Dice/DiceCloud.cpp | 16 +- Dice/DiceCloud.h | 4 +- Dice/DiceConsole.cpp | 10 +- Dice/DiceConsole.h | 17 +- Dice/DiceEvent.cpp | 6 +- Dice/DiceEvent.h | 2 +- Dice/DiceFile.cpp | 41 +++- Dice/DiceFile.hpp | 383 +++++++++++++++++++++++++++++++++++--- Dice/DiceGUI.cpp | 2 +- Dice/DiceJob.cpp | 6 +- Dice/DiceLua.cpp | 18 +- Dice/DiceMod.cpp | 8 +- Dice/DiceSchedule.cpp | 2 +- Dice/DiceSchedule.h | 4 +- Dice/DiceSession.cpp | 30 +-- Dice/DiceSession.h | 7 +- Dice/Jsonio.cpp | 11 +- Dice/Jsonio.h | 44 ++++- Dice/ManagerSystem.cpp | 5 +- vcpkg | 2 +- 25 files changed, 638 insertions(+), 189 deletions(-) diff --git a/Dice/BlackListManager.cpp b/Dice/BlackListManager.cpp index 6216481e..2165e984 100644 --- a/Dice/BlackListManager.cpp +++ b/Dice/BlackListManager.cpp @@ -638,7 +638,7 @@ bool DDBlackManager::insert(DDBlackMark& ex_mark) up_qq_danger(mark.ownerQQ.first, mark); } } - if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "/conf/BlackList.json"); + if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir / "conf" / "BlackList.json"); return !mark.isClear; } @@ -797,7 +797,7 @@ bool DDBlackManager::update(DDBlackMark& mark, unsigned int id, int credit = 5) else if (old_mark.comment.empty()) { old_mark.comment = printSTNow() + " "; } - if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir + "/conf/BlackList.json"); + if (Enabled && !isLoadingExtern)blacklist->saveJson(DiceDir / "conf" / "BlackList.json"); } return isUpdated; } @@ -930,7 +930,7 @@ void DDBlackManager::rm_black_group(long long llgroup, FromMsg* msg) } mGroupDanger.erase(llgroup); msg->note("ע" + printGroup(llgroup) + "ĺ¼"); - blacklist->saveJson(DiceDir + "/conf/BlackList.json"); + blacklist->saveJson(DiceDir / "conf" / "BlackList.json"); } void DDBlackManager::rm_black_qq(long long llqq, FromMsg* msg) @@ -954,7 +954,7 @@ void DDBlackManager::rm_black_qq(long long llqq, FromMsg* msg) } reset_qq_danger(llqq); msg->note("ע" + printQQ(llqq) + "ĺ¼"); - blacklist->saveJson(DiceDir + "/conf/BlackList.json"); + blacklist->saveJson(DiceDir / "conf" / "BlackList.json"); } void DDBlackManager::isban(FromMsg* msg) @@ -1256,9 +1256,9 @@ void DDBlackManager::create(DDBlackMark& mark) insert(mark); } -int DDBlackManager::loadJson(string strPath, bool isExtern) +int DDBlackManager::loadJson(const std::filesystem::path& fpPath, bool isExtern) { - json j = freadJson(strPath); + json j = freadJson(fpPath); if (j.is_null())return -1; if (j.size() > vBlackList.capacity())vBlackList.reserve(j.size() * 2); int cnt(0); @@ -1284,17 +1284,17 @@ int DDBlackManager::loadJson(string strPath, bool isExtern) isLoadingExtern = false; } if (isExtern) { - filesystem::remove(strPath); + filesystem::remove(fpPath); console.log("Դ¼Ŀ" + to_string(cnt) + "", 1, printSTNow()); - blacklist->saveJson(DiceDir + "/conf/BlackList.json"); + blacklist->saveJson(DiceDir / "conf" / "BlackList.json"); } return cnt; } -int DDBlackManager::loadHistory(const string& strLoc) +int DDBlackManager::loadHistory(const std::filesystem::path& fpLoc) { long long id; - std::ifstream fgroup(strLoc + "BlackGroup.RDconf"); + std::ifstream fgroup(fpLoc / "BlackGroup.RDconf"); if (fgroup) { while (fgroup >> id) @@ -1305,7 +1305,7 @@ int DDBlackManager::loadHistory(const string& strLoc) } } fgroup.close(); - std::ifstream fqq(strLoc + "BlackQQ.RDconf"); + std::ifstream fqq(fpLoc / "BlackQQ.RDconf"); if (fqq) { while (fqq >> id) @@ -1319,9 +1319,9 @@ int DDBlackManager::loadHistory(const string& strLoc) return vBlackList.size(); } -void DDBlackManager::saveJson(const string& strPath) const +void DDBlackManager::saveJson(const std::filesystem::path& fpPath) const { - std::ofstream fout(strPath); + std::ofstream fout(fpPath); JsonList jary; for (auto& mark : vBlackList) { diff --git a/Dice/BlackListManager.h b/Dice/BlackListManager.h index e19027dc..da09e24f 100644 --- a/Dice/BlackListManager.h +++ b/Dice/BlackListManager.h @@ -14,6 +14,7 @@ #include #include #include +#include using std::pair; using std::string; @@ -118,10 +119,10 @@ class DDBlackManager void verify(void*, long long); void create(DDBlackMark&); //ȡjsonʽ¼ - int loadJson(string strPath, bool isExtern = false); + int loadJson(const std::filesystem::path& fpPath, bool isExtern = false); //ȡɰ汾б - int loadHistory(const string& strLoc); - void saveJson(const string& strPath) const; + int loadHistory(const std::filesystem::path& fpLoc); + void saveJson(const std::filesystem::path& fpPath) const; }; extern std::unique_ptr blacklist; diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 7a5e7ee3..717c96be 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "APPINFO.h" #include "DiceFile.hpp" @@ -50,6 +51,7 @@ #include "DiceGUI.h" #include "S3PutObject.h" #include "DiceCensor.h" +#include "EncodingConvert.h" #ifndef _WIN32 #include @@ -62,7 +64,7 @@ using namespace std; unordered_map UserList{}; ThreadFactory threads; -string strFileLoc; +std::filesystem::path fpFileLoc; constexpr auto msgInit{ R"(ӭʹDice!ˣ 뷢.system guiĺ̨ @@ -73,28 +75,29 @@ string strFileLoc; // void loadData() { - mkDir(DiceDir); + std::error_code ec; + std::filesystem::create_directory(DiceDir, ec); ResList logList; - loadDir(loadXML, string(DiceDir + "/CardTemp/"), mCardTemplet, logList, true); - if (loadJMap(DiceDir + "/conf/CustomReply.json", CardDeck::mReplyDeck) < 0 && loadJMap( - strFileLoc + "ReplyDeck.json", CardDeck::mReplyDeck) > 0) + loadDir(loadXML, DiceDir / "CardTemp", mCardTemplet, logList, true); + if (loadJMap(DiceDir / "conf" / "CustomReply.json", CardDeck::mReplyDeck) < 0 && loadJMap( + fpFileLoc / "ReplyDeck.json", CardDeck::mReplyDeck) > 0) { logList << "ǨԶظ" + to_string(CardDeck::mReplyDeck.size()) + ""; - saveJMap(DiceDir + "/conf/CustomReply.json", CardDeck::mReplyDeck); + saveJMap(DiceDir / "conf" / "CustomReply.json", CardDeck::mReplyDeck); } fmt->set_help("ظб", "ظб:{list_reply_deck}"); - if (loadDir(loadJMap, string(DiceDir + "/PublicDeck/"), CardDeck::mExternPublicDeck, logList) < 1) + if (loadDir(loadJMap, DiceDir / "PublicDeck", CardDeck::mExternPublicDeck, logList) < 1) { - loadJMap(strFileLoc + "PublicDeck.json", CardDeck::mExternPublicDeck); - loadJMap(strFileLoc + "ExternDeck.json", CardDeck::mExternPublicDeck); + loadJMap(fpFileLoc / "PublicDeck.json", CardDeck::mExternPublicDeck); + loadJMap(fpFileLoc / "ExternDeck.json", CardDeck::mExternPublicDeck); } map_merge(CardDeck::mPublicDeck, CardDeck::mExternPublicDeck); //ȡĵ fmt->load(&logList); - if (int cnt; (cnt = loadJMap(DiceDir + "/conf/CustomHelp.json", CustomHelp)) < 0) + if (int cnt; (cnt = loadJMap(DiceDir / "conf" / "CustomHelp.json", CustomHelp)) < 0) { - if (cnt == -1)logList << DiceDir + "/conf/CustomHelp.jsonʧܣ"; - ifstream ifstreamHelpDoc(strFileLoc + "HelpDoc.txt"); + if (cnt == -1)logList << UTF8toGBK((DiceDir / "conf" / "CustomHelp.json").u8string()) + "ʧܣ"; + ifstream ifstreamHelpDoc(fpFileLoc / "HelpDoc.txt"); if (ifstreamHelpDoc) { string strName, strMsg; @@ -107,7 +110,7 @@ void loadData() } if (!CustomHelp.empty()) { - saveJMap(DiceDir + "/conf/CustomHelp.json", CustomHelp); + saveJMap(DiceDir / "conf" / "CustomHelp.json", CustomHelp); logList << "ʼԶ" + to_string(CustomHelp.size()) + ""; } } @@ -115,8 +118,8 @@ void loadData() } map_merge(fmt->helpdoc, CustomHelp); //ȡдʿ - loadDir(load_words, DiceDir + "/conf/censor/", censor, logList, true); - loadJMap(DiceDir + "/conf/CustomCensor.json", censor.CustomWords); + loadDir(load_words, DiceDir / "conf" / "censor", censor, logList, true); + loadJMap(DiceDir / "conf" / "CustomCensor.json", censor.CustomWords); censor.build(); if (!logList.empty()) { @@ -132,14 +135,14 @@ void dataInit() if (gm->load() < 0) { multimap ObserveGroup; - loadFile(strFileLoc + "ObserveDiscuss.RDconf", ObserveGroup); - loadFile(strFileLoc + "ObserveGroup.RDconf", ObserveGroup); + loadFile(fpFileLoc / "ObserveDiscuss.RDconf", ObserveGroup); + loadFile(fpFileLoc / "ObserveGroup.RDconf", ObserveGroup); for (auto [grp, qq] : ObserveGroup) { gm->session(grp).sOB.insert(qq); gm->session(grp).update(); } - ifstream ifINIT(strFileLoc + "INIT.DiceDB"); + ifstream ifINIT(fpFileLoc / "INIT.DiceDB"); if (ifINIT) { long long Group(0); @@ -154,21 +157,22 @@ void dataInit() ifINIT.close(); if(gm->mSession.size())console.log("ʼԹȹ¼" + to_string(gm->mSession.size()) + "", 1); } - today = make_unique(DiceDir + "/user/DiceToday.json"); + today = make_unique(DiceDir / "user" / "DiceToday.json"); } // void dataBackUp() { - mkDir(DiceDir + "/conf"); - mkDir(DiceDir + "/user"); - mkDir(DiceDir + "/audit"); + std::error_code ec; + std::filesystem::create_directory(DiceDir / "conf", ec); + std::filesystem::create_directory(DiceDir / "user", ec); + std::filesystem::create_directory(DiceDir / "audit", ec); //б - saveBFile(DiceDir + "/user/PlayerCards.RDconf", PList); - saveFile(DiceDir + "/user/ChatList.txt", ChatList); - saveBFile(DiceDir + "/user/ChatConf.RDconf", ChatList); - saveFile(DiceDir + "/user/UserList.txt", UserList); - saveBFile(DiceDir + "/user/UserConf.RDconf", UserList); + saveBFile(DiceDir / "user" / "PlayerCards.RDconf", PList); + saveFile(DiceDir / "user" / "ChatList.txt", ChatList); + saveBFile(DiceDir / "user" / "ChatConf.RDconf", ChatList); + saveFile(DiceDir / "user" / "UserList.txt", UserList); + saveBFile(DiceDir / "user" / "UserConf.RDconf", UserList); } bool isIniting{ false }; @@ -185,15 +189,19 @@ EVE_Enable(eventEnable) console.log(": libcurlʧܣ", 1); } #endif - if ((dirExe = DD::getRootDir()).empty()) { - char path[260]; + std::string RootDir = DD::getRootDir(); + if (RootDir.empty()) { #ifdef _WIN32 + char path[MAX_PATH]; GetModuleFileNameA(nullptr, path, MAX_PATH); + dirExe = std::filesystem::path(path).parent_path(); #else - *path = 0; + dirExe = {}; #endif - string strPath(path); - dirExe = strPath.substr(0, strPath.rfind("\\") + 1); + } + else + { + dirExe = RootDir; } Dice_Full_Ver_On = Dice_Full_Ver + " on\n" + DD::getDriVer(); DD::debugLog(Dice_Full_Ver_On); @@ -242,27 +250,27 @@ EVE_Enable(eventEnable) }; if ((console.DiceMaid = DD::getLoginQQ())) { - DiceDir = dirExe + "Dice" + to_string(console.DiceMaid); - filesystem::path pathDir(DiceDir); - if (!exists(pathDir)) { - filesystem::path pathDirOld(dirExe + "DiceData"); - if (exists(pathDirOld))rename(pathDirOld, pathDir); - else filesystem::create_directory(pathDir); + DiceDir = dirExe / ("Dice" + to_string(console.DiceMaid)); + if (!exists(DiceDir)) { + filesystem::path DiceDirOld(dirExe / "DiceData"); + if (exists(DiceDirOld))rename(DiceDirOld, DiceDir); + else filesystem::create_directory(DiceDir); } } - console.setPath(DiceDir + "/conf/Console.xml"); - strFileLoc = DiceDir + "/com.w4123.dice/"; + console.setPath(DiceDir / "conf" / "Console.xml"); + fpFileLoc = DiceDir / "com.w4123.dice"; GlobalMsg["strSelfName"] = DD::getLoginNick(); if (GlobalMsg["strSelfName"].empty()) { GlobalMsg["strSelfName"] = "[" + toString(console.DiceMaid % 1000, 4) + "]"; } - mkDir(DiceDir + "/conf"); - mkDir(DiceDir + "/user"); - mkDir(DiceDir + "/audit"); + std::error_code ec; + std::filesystem::create_directory(DiceDir / "conf", ec); + std::filesystem::create_directory(DiceDir / "user", ec); + std::filesystem::create_directory(DiceDir / "audit", ec); if (!console.load()) { - ifstream ifstreamMaster(strFileLoc + "Master.RDconf"); + ifstream ifstreamMaster(fpFileLoc / "Master.RDconf"); if (ifstreamMaster) { std::pair ClockToWork{}, ClockOffWork{}; @@ -284,7 +292,7 @@ EVE_Enable(eventEnable) } ifstreamMaster.close(); std::map boolConsole; - loadJMap(strFileLoc + "boolConsole.json", boolConsole); + loadJMap(fpFileLoc / "boolConsole.json", boolConsole); for (auto& [key, val] : boolConsole) { console.set(key, val); @@ -294,22 +302,22 @@ EVE_Enable(eventEnable) console.save(); } //ȡб - if (loadBFile(DiceDir + "/user/UserConf.RDconf", UserList) < 1) + if (loadBFile(DiceDir / "user"/ "UserConf.RDconf", UserList) < 1) { map DefaultDice; - if (loadFile(strFileLoc + "Default.RDconf", DefaultDice) > 0) + if (loadFile(fpFileLoc / "Default.RDconf", DefaultDice) > 0) for (auto p : DefaultDice) { getUser(p.first).create(NEWYEAR).intConf["Ĭ"] = p.second; } map DefaultRule; - if (loadFile(strFileLoc + "DefaultRule.RDconf", DefaultRule) > 0) + if (loadFile(fpFileLoc / "DefaultRule.RDconf", DefaultRule) > 0) for (auto p : DefaultRule) { if (isdigit(static_cast(p.second[0])))break; getUser(p.first).create(NEWYEAR).strConf["ĬϹ"] = p.second; } - ifstream ifName(strFileLoc + "Name.dicedb"); + ifstream ifName(fpFileLoc / "Name.dicedb"); if (ifName) { long long GroupID = 0, QQ = 0; @@ -321,17 +329,17 @@ EVE_Enable(eventEnable) } } } - if (loadFile(DiceDir + "/user/UserList.txt", UserList) < 1) + if (loadFile(DiceDir / "user" / "UserList.txt", UserList) < 1) { set WhiteQQ; - if (loadFile(strFileLoc + "WhiteQQ.RDconf", WhiteQQ) > 0) + if (loadFile(fpFileLoc / "WhiteQQ.RDconf", WhiteQQ) > 0) for (auto qq : WhiteQQ) { getUser(qq).create(NEWYEAR).trust(1); } //ȡԱб set AdminQQ; - if (loadFile(strFileLoc + "AdminQQ.RDconf", AdminQQ) > 0) + if (loadFile(fpFileLoc / "AdminQQ.RDconf", AdminQQ) > 0) for (auto qq : AdminQQ) { getUser(qq).create(NEWYEAR).trust(4); @@ -339,101 +347,101 @@ EVE_Enable(eventEnable) if (console.master())getUser(console.master()).create(NEWYEAR).trust(5); if (UserList.size()){ console.log("ʼû¼" + to_string(UserList.size()) + "", 1); - saveFile(DiceDir + "/user/UserList.txt", UserList); + saveFile(DiceDir / "user" / "UserList.txt", UserList); } } - if (loadBFile(DiceDir + "/user/ChatConf.RDconf", ChatList) < 1) + if (loadBFile(DiceDir / "user" / "ChatConf.RDconf", ChatList) < 1) { set GroupList; - if (loadFile(strFileLoc + "DisabledDiscuss.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledDiscuss.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).discuss().set("ָͣ"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledJRRPDiscuss.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledJRRPDiscuss.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).discuss().set("jrrp"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledMEGroup.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledMEGroup.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).discuss().set("me"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledHELPDiscuss.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledHELPDiscuss.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).discuss().set("help"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledOBDiscuss.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledOBDiscuss.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).discuss().set("ob"); } GroupList.clear(); map mDefault; - if (loadFile(strFileLoc + "DefaultCOC.MYmap", mDefault) > 0) + if (loadFile(fpFileLoc / "DefaultCOC.MYmap", mDefault) > 0) for (const auto& it : mDefault) { if (it.first.second == msgtype::Private)getUser(it.first.first) .create(NEWYEAR).setConf("rc", it.second); else chat(it.first.first).create(NEWYEAR).setConf("rc", it.second); } - if (loadFile(strFileLoc + "DisabledGroup.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledGroup.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).group().set("ָͣ"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledJRRPGroup.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledJRRPGroup.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).group().set("jrrp"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledMEDiscuss.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledMEDiscuss.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).group().set("me"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledHELPGroup.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledHELPGroup.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).group().set("help"); } GroupList.clear(); - if (loadFile(strFileLoc + "DisabledOBGroup.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "DisabledOBGroup.RDconf", GroupList) > 0) for (auto p : GroupList) { chat(p).group().set("ob"); } GroupList.clear(); map WelcomeMsg; - if (loadFile(strFileLoc + "WelcomeMsg.RDconf", WelcomeMsg) > 0) + if (loadFile(fpFileLoc / "WelcomeMsg.RDconf", WelcomeMsg) > 0) { for (const auto& p : WelcomeMsg) { chat(p.first).group().setText("Ⱥӭ", p.second); } } - if (loadFile(strFileLoc + "WhiteGroup.RDconf", GroupList) > 0) + if (loadFile(fpFileLoc / "WhiteGroup.RDconf", GroupList) > 0) { for (auto g : GroupList) { chat(g).group().set("ʹ").set(""); } } - saveBFile(DiceDir + "/user/ChatConf.RDconf", ChatList); + saveBFile(DiceDir / "user" / "ChatConf.RDconf", ChatList); } - if (loadFile(DiceDir + "/user/ChatList.txt", ChatList) < 1) + if (loadFile(DiceDir / "user" / "ChatList.txt", ChatList) < 1) { std::map mGroupInviter; - if (loadFile(strFileLoc + "GroupInviter.RDconf", mGroupInviter) < 1) + if (loadFile(fpFileLoc / "GroupInviter.RDconf", mGroupInviter) < 1) { for (const auto& it : mGroupInviter) { @@ -442,7 +450,7 @@ EVE_Enable(eventEnable) } if(ChatList.size()){ console.log("ʼȺ¼" + to_string(ChatList.size()) + "", 1); - saveFile(DiceDir + "/user/ChatList.txt", ChatList); + saveFile(DiceDir / "user" / "ChatList.txt", ChatList); } } for (auto gid : DD::getGroupIDList()) @@ -450,20 +458,20 @@ EVE_Enable(eventEnable) chat(gid).group().reset("δ").reset(""); } blacklist = make_unique(); - if (blacklist->loadJson(DiceDir + "/conf/BlackList.json") < 0) + if (blacklist->loadJson(DiceDir / "conf" / "BlackList.json") < 0) { - blacklist->loadJson(strFileLoc + "BlackMarks.json"); - int cnt = blacklist->loadHistory(strFileLoc); + blacklist->loadJson(fpFileLoc / "BlackMarks.json"); + int cnt = blacklist->loadHistory(fpFileLoc); if (cnt) { - blacklist->saveJson(DiceDir + "/conf/BlackList.json"); + blacklist->saveJson(DiceDir / "conf" / "BlackList.json"); console.log("ʼ¼" + to_string(cnt) + "", 1); } } else { - blacklist->loadJson(DiceDir + "/conf/BlackListEx.json", true); + blacklist->loadJson(DiceDir / "conf" / "BlackListEx.json", true); } fmt = make_unique(); - if (loadJMap(DiceDir + "/conf/CustomMsg.json", EditedMsg) < 0)loadJMap(strFileLoc + "CustomMsg.json", EditedMsg); + if (loadJMap(DiceDir / "conf" / "CustomMsg.json", EditedMsg) < 0)loadJMap(fpFileLoc / "CustomMsg.json", EditedMsg); //Ԥ޸ijظı if (EditedMsg.count("strSelfName"))GlobalMsg["strSelfName"] = EditedMsg["strSelfName"]; for (auto it : EditedMsg) @@ -474,9 +482,9 @@ EVE_Enable(eventEnable) } DD::debugLog("Dice.loadData"); loadData(); - if (loadBFile(DiceDir + "/user/PlayerCards.RDconf", PList) < 1) + if (loadBFile(DiceDir / "user" / "PlayerCards.RDconf", PList) < 1) { - ifstream ifstreamCharacterProp(strFileLoc + "CharacterProp.RDconf"); + ifstream ifstreamCharacterProp(fpFileLoc / "CharacterProp.RDconf"); if (ifstreamCharacterProp) { long long QQ, GrouporDiscussID; diff --git a/Dice/DiceCensor.cpp b/Dice/DiceCensor.cpp index 08037ff1..0a60df09 100644 --- a/Dice/DiceCensor.cpp +++ b/Dice/DiceCensor.cpp @@ -7,14 +7,15 @@ #include "EncodingConvert.h" #include #include +#include TrieG wordG; enumap sens{ "Ignore","Notice","Caution","Warning","Danger" }; -int load_words(const string& filename, Censor& cens) { +int load_words(const std::filesystem::path& path, Censor& cens) { int cnt(0); - ifstream fin(filename); + ifstream fin(path); if (!fin)return -1; bool isUTF8{ false }; string word; @@ -80,7 +81,7 @@ void Censor::build() { wordG.build(words); } void Censor::save() { - saveJMap(DiceDir + "/conf/CustomCensor.json", censor.CustomWords); + saveJMap(DiceDir / "conf" / "CustomCensor.json", censor.CustomWords); } int Censor::search(const string& text, unordered_set& res) { diff --git a/Dice/DiceCensor.h b/Dice/DiceCensor.h index f4a7a415..323bd30f 100644 --- a/Dice/DiceCensor.h +++ b/Dice/DiceCensor.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "STLExtern.hpp" #include using std::string; @@ -31,4 +32,4 @@ class Censor { inline Censor censor; -int load_words(const string& filename, Censor& cens); \ No newline at end of file +int load_words(const std::filesystem::path& path, Censor& cens); \ No newline at end of file diff --git a/Dice/DiceCloud.cpp b/Dice/DiceCloud.cpp index 54059f37..c420d612 100644 --- a/Dice/DiceCloud.cpp +++ b/Dice/DiceCloud.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2019 String.Empty */ #include +#include #include "json.hpp" #include "DiceCloud.h" #include "GlobalVar.h" @@ -58,7 +59,7 @@ namespace Cloud return 0; } - int DownloadFile(const char* url, const char* downloadPath) + [[DEPRECATED]] int DownloadFile(const char* url, const char* downloadPath) { #ifdef _WIN32 DeleteUrlCacheEntryA(url); @@ -69,6 +70,19 @@ namespace Cloud return -1; #endif } + + int DownloadFile(const char* url, const std::filesystem::path& downloadPath) + { +#ifdef _WIN32 + DeleteUrlCacheEntryA(url); + if (URLDownloadToFileA(nullptr, url, downloadPath.string().c_str(), 0, nullptr) != S_OK) return -1; + std::error_code ec; + if (!std::filesystem::exists(downloadPath, ec) || ec)return -2; + return 0; +#else + return -1; +#endif + } int checkUpdate(FromMsg* msg) { diff --git a/Dice/DiceCloud.h b/Dice/DiceCloud.h index b88002c4..0d127192 100644 --- a/Dice/DiceCloud.h +++ b/Dice/DiceCloud.h @@ -5,6 +5,7 @@ * Copyright (C) 2019 String.Empty */ +#include class FromMsg; @@ -12,6 +13,7 @@ namespace Cloud { void heartbeat(); int checkWarning(const char* warning); - int DownloadFile(const char* url, const char* downloadPath); + [[DEPRECATED]] int DownloadFile(const char* url, const char* downloadPath); + int DownloadFile(const char* url, const std::filesystem::path& downloadPath); int checkUpdate(FromMsg* msg); } diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 8c92a81d..4bef9129 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -150,7 +150,7 @@ void Console::rmNotice(chatType ct) int Console::log(const std::string& strMsg, int note_lv, const string& strTime) { - ofstream fout(string(DiceDir + "/audit/log") + to_string(DiceMaid) + "_" + printDate() + ".txt", + ofstream fout(DiceDir / "audit" / "log" / (to_string(DiceMaid) + "_" + printDate() + ".txt"), ios::out | ios::app); fout << strTime << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); @@ -188,16 +188,16 @@ void Console::reset() void Console::loadNotice() { - if (loadFile(DiceDir + "/conf/NoticeList.txt", NoticeList) < 1) + if (loadFile(DiceDir / "conf" / "NoticeList.txt", NoticeList) < 1) { std::set sChat; - if (loadFile(DiceDir + "com.w4123.dice\\MonitorList.RDconf", sChat) > 0) + if (loadFile(DiceDir / "com.w4123.dice" / "MonitorList.RDconf", sChat) > 0) for (const auto& it : sChat) { console.setNotice(it, 0b100000); } sChat.clear(); - if (loadFile(DiceDir + "/conf/RecorderList.RDconf", sChat) > 0) + if (loadFile(DiceDir / "conf" / "RecorderList.RDconf", sChat) > 0) for (const auto& it : sChat) { console.setNotice(it, 0b11011); @@ -217,7 +217,7 @@ void Console::loadNotice() void Console::saveNotice() const { - saveFile(DiceDir + "/conf/NoticeList.txt", NoticeList); + saveFile(DiceDir / "conf" / "NoticeList.txt", NoticeList); } Console console; diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index ef15de11..17212cc3 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -24,8 +24,8 @@ using namespace std::literals::chrono_literals; using std::string; using std::to_string; -extern string dirExe; -extern string DiceDir; +extern std::filesystem::path dirExe; +extern std::filesystem::path DiceDir; enum class ClockEvent { off, on, save, clear }; @@ -87,7 +87,7 @@ class Console [[nodiscard]] ResList listClock() const; [[nodiscard]] ResList listNotice() const; [[nodiscard]] int showNotice(chatType ct) const; - void setPath(std::string path) { strPath = std::move(path); } + void setPath(const std::filesystem::path& path) { fpPath = path; } void set(const std::string& key, int val) { @@ -105,7 +105,7 @@ class Console { string s; //DSens.build({ {"nnϹ",2 } }); - if (!rdbuf(strPath, s))return false; + if (!rdbuf(fpPath, s))return false; DDOM xml(s); if (xml.count("mode"))isMasterMode = stoi(xml["mode"].strValue); if (xml.count("master"))masterQQ = stoll(xml["master"].strValue); @@ -128,7 +128,8 @@ class Console } void save() { - mkDir(DiceDir + "/conf"); + std::error_code ec; + std::filesystem::create_directories(DiceDir / "conf", ec); DDOM xml("console",""); xml.push(DDOM("mode", to_string(isMasterMode))); xml.push(DDOM("master", to_string(masterQQ))); @@ -150,14 +151,14 @@ class Console } xml.push(conf); } - std::ofstream fout(strPath); - fout << xml.dump(); + std::ofstream fout(fpPath); + if (fout) fout << xml.dump(); } void loadNotice(); void saveNotice() const; private: - string strPath; + std::filesystem::path fpPath; std::map intConf; std::multimap mWorkClock{}; std::map NoticeList{}; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 026a662a..932b1433 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1073,7 +1073,7 @@ int FromMsg::BasicOrder() fmt->set_help(strVar["key"], strHelpdoc); reply(format(GlobalMsg["strHlpSet"], {strVar["key"]})); } - saveJMap(DiceDir + "/conf/CustomHelp.json", CustomHelp); + saveJMap(DiceDir / "conf" / "CustomHelp.json", CustomHelp); return true; } if (strLowerMessage.substr(intMsgCnt, 4) == "help") @@ -1744,7 +1744,7 @@ int FromMsg::InnerOrder() { CardDeck::mReplyDeck.erase(strVar["key"]); } else reply(GlobalMsg["strReplySet"], {strVar["key"]}); - saveJMap(DiceDir + "/conf/CustomReply.json", CardDeck::mReplyDeck); + saveJMap(DiceDir / "conf" / "CustomReply.json", CardDeck::mReplyDeck); return 1; } else if (strLowerMessage.substr(intMsgCnt, 5) == "rules") { @@ -2369,7 +2369,7 @@ int FromMsg::InnerOrder() { GlobalMsg[strName] = strMessage; note("Զ" + strName + "ı", 0b1); } - saveJMap(DiceDir + "/conf/CustomMsg.json", EditedMsg); + saveJMap(DiceDir / "conf" / "CustomMsg.json", EditedMsg); return 1; } else if (strLowerMessage.substr(intMsgCnt, 2) == "en") { diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 38cc6405..0dd08f5a 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -50,7 +50,7 @@ class FromMsg : public DiceJobDetail { void note(std::string strMsg, int note_lv = 0b1) { strMsg = format(strMsg, GlobalMsg, strVar); - ofstream fout(string(DiceDir + "/audit/log") + to_string(console.DiceMaid) + "_" + printDate() + ".txt", + ofstream fout(DiceDir / "audit" / "log" / (to_string(console.DiceMaid) + "_" + printDate() + ".txt"), ios::out | ios::app); fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); diff --git a/Dice/DiceFile.cpp b/Dice/DiceFile.cpp index 82e549ca..d6f4bc2b 100644 --- a/Dice/DiceFile.cpp +++ b/Dice/DiceFile.cpp @@ -102,7 +102,7 @@ bool fscan(std::ifstream& fin, std::string& t) return false; } -bool rdbuf(const string& strPath, string& s) +[[DEPRECATED]] bool rdbuf(const string& strPath, string& s) { const std::ifstream fin(strPath); if (!fin)return false; @@ -112,6 +112,16 @@ bool rdbuf(const string& strPath, string& s) return true; } +bool rdbuf(const std::filesystem::path& fpPath, string& s) +{ + const std::ifstream fin(fpPath); + if (!fin)return false; + stringstream ss; + ss << fin.rdbuf(); + s = ss.str(); + return true; +} + void fprint(std::ofstream& fout, std::string s) { while (s.find(' ') != std::string::npos)s.replace(s.find(' '), 1, "{space}"); @@ -212,7 +222,7 @@ ofstream& operator<<(ofstream& fout, const Chat& grp) } template -int _listDir(const string& dir, vector& files) +[[DEPRECATED]] int _listDir(const string& dir, vector& files) { int intFile = 0; std::error_code err; @@ -227,7 +237,32 @@ int _listDir(const string& dir, vector& files) return err ? -1 : intFile; } -int listDir(const string& dir, vector& files, bool isSub) +template +int _listDir(const std::filesystem::path& dir, vector& files) +{ + int intFile = 0; + std::error_code err; + for (const auto& file : T(dir, err)) + { + if (file.is_regular_file()) + { + intFile++; + files.push_back(file.path()); + } + } + return err ? -1 : intFile; +} + +[[DEPRECATED]] int listDir(const string& dir, vector& files, bool isSub) +{ + if (isSub) + { + return _listDir(dir, files); + } + return _listDir(dir, files); +} + +int listDir(const std::filesystem::path& dir, vector& files, bool isSub) { if (isSub) { diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index 13fee3c4..6f0f8629 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -212,7 +213,7 @@ void readini(string s, std::vector& v) } template -int loadFile(std::string strPath, std::set& setTmp) +[[DEPRECATED]] int loadFile(const std::string& strPath, std::set& setTmp) { std::ifstream fin(strPath); if (fin) @@ -231,7 +232,26 @@ int loadFile(std::string strPath, std::set& setTmp) } template -int loadFile(std::string strPath, std::unordered_set& setTmp) +int loadFile(const std::filesystem::path& fpPath, std::set& setTmp) +{ + std::ifstream fin(fpPath); + if (fin) + { + int Cnt = 0; + T item; + while (fscan(fin, item)) + { + setTmp.insert(item); + Cnt++; + } + return Cnt; + } + fin.close(); + return -1; +} + +template +[[DEPRECATED]] int loadFile(const std::string& strPath, std::unordered_set& setTmp) { std::ifstream fin(strPath); if (fin) @@ -249,8 +269,27 @@ int loadFile(std::string strPath, std::unordered_set& setTmp) return -1; } +template +int loadFile(const std::filesystem::path& fpPath, std::unordered_set& setTmp) +{ + std::ifstream fin(fpPath); + if (fin) + { + int Cnt = 0; + T item; + while (fscan(fin, item)) + { + setTmp.insert(item); + Cnt++; + } + return Cnt; + } + fin.close(); + return -1; +} + template -int loadFile(std::string strPath, std::map& mapTmp) +[[DEPRECATED]] int loadFile(const std::string& strPath, std::map& mapTmp) { std::ifstream fin(strPath); if (fin) @@ -269,7 +308,26 @@ int loadFile(std::string strPath, std::map& mapTmp) } template -int loadFile(std::string strPath, std::unordered_map& mapTmp) +int loadFile(const std::filesystem::path& fpPath, std::map& mapTmp) +{ + std::ifstream fin(fpPath); + if (fin) + { + int Cnt = 0; + T1 key; + while (fin >> key) + { + fscan(fin, mapTmp[key]); + Cnt++; + } + return Cnt; + } + fin.close(); + return -1; +} + +template +[[DEPRECATED]] int loadFile(const std::string& strPath, std::unordered_map& mapTmp) { std::ifstream fin(strPath); if (fin) @@ -288,7 +346,26 @@ int loadFile(std::string strPath, std::unordered_map& mapTmp) } template -void loadFile(std::string strPath, std::multimap& mapTmp) +int loadFile(const std::filesystem::path& fpPath, std::unordered_map& mapTmp) +{ + std::ifstream fin(fpPath); + if (fin) + { + int Cnt = 0; + T1 key; + while (fin >> key) + { + fscan(fin, mapTmp[key]); + Cnt++; + } + return Cnt; + } + fin.close(); + return -1; +} + +template +[[DEPRECATED]] void loadFile(const std::string& strPath, std::multimap& mapTmp) { std::ifstream fin(strPath); if (fin) @@ -303,8 +380,24 @@ void loadFile(std::string strPath, std::multimap& mapTmp) fin.close(); } +template +void loadFile(const std::filesystem::path& fpPath, std::multimap& mapTmp) +{ + std::ifstream fin(fpPath); + if (fin) + { + T1 key; + T2 Val; + while (fin >> key >> Val) + { + mapTmp.insert({key, Val}); + } + } + fin.close(); +} + template -int loadBFile(std::string strPath, std::map& m) +[[DEPRECATED]] int loadBFile(const std::string& strPath, std::map& m) { std::ifstream fin(strPath, std::ios::in | std::ios::binary); if (!fin)return -1; @@ -322,7 +415,25 @@ int loadBFile(std::string strPath, std::map& m) } template -int loadBFile(std::string strPath, std::unordered_map& m) +int loadBFile(const std::filesystem::path& fpPath, std::map& m) +{ + std::ifstream fin(fpPath, std::ios::in | std::ios::binary); + if (!fin)return -1; + const int len = fread(fin); + int Cnt = 0; + T key; + C val; + while (fin.peek() != EOF && len > Cnt++) + { + key = fread(fin); + m[key].readb(fin); + } + fin.close(); + return Cnt; +} + +template +[[DEPRECATED]] int loadBFile(const std::string& strPath, std::unordered_map& m) { std::ifstream fin(strPath, std::ios::in | std::ios::binary); if (!fin)return -1; @@ -339,9 +450,27 @@ int loadBFile(std::string strPath, std::unordered_map& m) return Cnt; } +template +int loadBFile(const std::filesystem::path& fpPath, std::unordered_map& m) +{ + std::ifstream fin(fpPath, std::ios::in | std::ios::binary); + if (!fin)return -1; + const int len = fread(fin); + int Cnt = 0; + T key; + C val; + while (fin.peek() != EOF && len > Cnt++) + { + key = fread(fin); + m[key].readb(fin); + } + fin.close(); + return Cnt; +} + //ȡαini template -int loadINI(std::string strPath, std::map& m) +[[DEPRECATED]] int loadINI(const std::string& strPath, std::map& m) { std::ifstream fin(strPath, std::ios::in | std::ios::binary); if (!fin)return -1; @@ -355,11 +484,27 @@ int loadINI(std::string strPath, std::map& m) return 1; } -bool rdbuf(const string& strPath, string& s); +template +int loadINI(const std::filesystem::path& fpPath, std::map& m) +{ + std::ifstream fin(fpPath, std::ios::in | std::ios::binary); + if (!fin)return -1; + std::string s, name; + C val; + getline(fin, s); + readini(fin, name); + val.readi(fin); + m[name] = val; + fin.close(); + return 1; +} + +[[DEPRECATED]] bool rdbuf(const string& strPath, string& s); +bool rdbuf(const std::filesystem::path& fpPath, string& s); //ȡαxml template -int loadXML(const std::string& strPath, std::map& m) +[[DEPRECATED]] int loadXML(const std::string& strPath, std::map& m) { string s; if (!rdbuf(strPath, s))return -1; @@ -369,12 +514,24 @@ int loadXML(const std::string& strPath, std::map& m) return 1; } +template +int loadXML(const std::filesystem::path& fpPath, std::map& m) +{ + string s; + if (!rdbuf(fpPath, s))return -1; + DDOM xml(s); + C obj(xml); + m[obj.getName()].readt(xml); + return 1; +} + //ļ -int listDir(const string& dir, vector& files, bool isSub = false); +[[DEPRECATED]] int listDir(const string& dir, vector& files, bool isSub = false); +int listDir(const std::filesystem::path& dir, vector& files, bool isSub = false); template -int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2& tmp, int& intFile, int& intFailure, - int& intItem, std::vector& files) +[[DEPRECATED]] int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2& tmp, int& intFile, int& intFailure, + int& intItem, std::vector& failureFiles) { std::error_code err; for (const auto& p : T1(strDir, err)) @@ -386,7 +543,30 @@ int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2 const int Cnt = load(strDir + path, tmp); if (Cnt < 0) { - files.push_back(path); + failureFiles.push_back(path); + intFailure++; + } + else intItem += Cnt; + } + } + if (err) return -1; + return 0; +} + +template +int _loadDir(int (*load)(const std::filesystem::path&, T2&), const std::filesystem::path& fpDir, T2& tmp, int& intFile, int& intFailure, + int& intItem, std::vector& failureFiles) +{ + std::error_code err; + for (const auto& p : T1(fpDir, err)) + { + if (p.is_regular_file()) + { + intFile++; + const int Cnt = load(p, tmp); + if (Cnt < 0) + { + failureFiles.push_back(UTF8toGBK(p.path().filename().u8string())); intFailure++; } else intItem += Cnt; @@ -398,7 +578,7 @@ int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2 //ȡļ template -int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& tmp, ResList& logList, +[[DEPRECATED]] int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& tmp, ResList& logList, bool isSubdir = false) { int intFile = 0, intFailure = 0, intItem = 0; @@ -427,6 +607,37 @@ int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& t return intFile; } + +template +int loadDir(int (*load)(const std::filesystem::path&, T&), const std::filesystem::path& fpDir, T& tmp, ResList& logList, + bool isSubdir = false) +{ + int intFile = 0, intFailure = 0, intItem = 0; + std::vector files; + if (isSubdir) + { + if (_loadDir(load, fpDir, tmp, intFile, intFailure, intItem, + files) == -1) return 0; + } + else + { + if (_loadDir(load, fpDir, tmp, intFile, intFailure, intItem, files) == -1) + return 0; + } + + if (!intFile)return 0; + logList << "ȡ" + UTF8toGBK(fpDir.u8string()) + "е" + std::to_string(intFile) + "ļ, " + std::to_string(intItem) + "Ŀ"; + if (intFailure) + { + logList << "ȡʧ" + std::to_string(intFailure) + ":"; + for (auto& it : files) + { + logList << it; + } + } + return intFile; +} + //save template void fprint(std::ofstream& fout, const T& t) @@ -451,7 +662,7 @@ void fprint(std::ofstream& fout, std::pair t) } template -bool clrEmpty(std::string strPath, const T& tmp) +[[DEPRECATED]] bool clrEmpty(const std::string& strPath, const T& tmp) { if (tmp.empty()) { @@ -466,6 +677,22 @@ bool clrEmpty(std::string strPath, const T& tmp) return false; } +template +bool clrEmpty(const std::filesystem::path& fpPath, const T& tmp) +{ + if (tmp.empty()) + { + std::ifstream fin(fpPath); + if (fin) + { + fin.close(); + remove(fpPath); + } + return true; + } + return false; +} + template typename std::enable_if::value, void>::type fwrite(ofstream& fout, T t) { @@ -514,7 +741,7 @@ void fwrite(ofstream& fout, const std::set& s) } template -void saveFile(std::string strPath, const T& setTmp) +[[DEPRECATED]] void saveFile(const std::string& strPath, const T& setTmp) { if (clrEmpty(strPath, setTmp))return; std::ofstream fout(strPath); @@ -526,8 +753,21 @@ void saveFile(std::string strPath, const T& setTmp) fout.close(); } +template +void saveFile(const std::filesystem::path& fpPath, const T& setTmp) +{ + if (clrEmpty(fpPath, setTmp))return; + std::ofstream fout(fpPath); + for (const auto& it : setTmp) + { + fprint(fout, it); + fout << std::endl; + } + fout.close(); +} + template -void saveFile(std::string strPath, const map& mTmp) +[[DEPRECATED]] void saveFile(const std::string& strPath, const map& mTmp) { if (clrEmpty(strPath, mTmp))return; std::ofstream fout(strPath); @@ -540,9 +780,23 @@ void saveFile(std::string strPath, const map& mTmp) fout.close(); } +template +void saveFile(const std::filesystem::path& fpPath, const map& mTmp) +{ + if (clrEmpty(fpPath, mTmp))return; + std::ofstream fout(fpPath); + for (const auto& [key,val] : mTmp) + { + fout << key << "\t"; + fprint(fout, val); + fout << std::endl; + } + fout.close(); +} + template -void saveFile(std::string strPath, const unordered_map& mTmp) +[[DEPRECATED]] void saveFile(const std::string& strPath, const unordered_map& mTmp) { if (clrEmpty(strPath, mTmp))return; std::ofstream fout(strPath); @@ -555,8 +809,22 @@ void saveFile(std::string strPath, const unordered_map& mTmp) fout.close(); } +template +void saveFile(const std::filesystem::path& fpPath, const unordered_map& mTmp) +{ + if (clrEmpty(fpPath, mTmp))return; + std::ofstream fout(fpPath); + for (const auto& [key, val] : mTmp) + { + fout << key << "\t"; + fprint(fout, val); + fout << std::endl; + } + fout.close(); +} + template -void saveBFile(std::string strPath, std::map& m) +[[DEPRECATED]] void saveBFile(const std::string& strPath, std::map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); @@ -570,8 +838,23 @@ void saveBFile(std::string strPath, std::map& m) fout.close(); } +template +void saveBFile(const std::filesystem::path& fpPath, std::map& m) +{ + if (clrEmpty(fpPath, m))return; + std::ofstream fout(fpPath, ios::out | ios::trunc | ios::binary); + const int len = m.size(); + fwrite(fout, len); + for (auto& [key,val] : m) + { + fwrite(fout, key); + fwrite(fout, val); + } + fout.close(); +} + template -void saveBFile(std::string strPath, std::map& m) +[[DEPRECATED]] void saveBFile(const std::string& strPath, std::map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); @@ -585,8 +868,23 @@ void saveBFile(std::string strPath, std::map& m) fout.close(); } +template +void saveBFile(const std::filesystem::path& fpPath, std::map& m) +{ + if (clrEmpty(fpPath, m))return; + std::ofstream fout(fpPath, ios::out | ios::trunc | ios::binary); + const int len = m.size(); + fwrite(fout, len); + for (auto& [key, val] : m) + { + fwrite(fout, key); + fwrite(fout, val); + } + fout.close(); +} + template -void saveBFile(std::string strPath, std::unordered_map& m) +[[DEPRECATED]] void saveBFile(const std::string& strPath, std::unordered_map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); @@ -600,8 +898,23 @@ void saveBFile(std::string strPath, std::unordered_map& m) fout.close(); } +template +void saveBFile(const std::filesystem::path& fpPath, std::unordered_map& m) +{ + if (clrEmpty(fpPath, m))return; + std::ofstream fout(fpPath, ios::out | ios::trunc | ios::binary); + const int len = m.size(); + fwrite(fout, len); + for (auto& [key, val] : m) + { + fwrite(fout, key); + fwrite(fout, val); + } + fout.close(); +} + template -void saveBFile(std::string strPath, std::unordered_map& m) +[[DEPRECATED]] void saveBFile(const std::string& strPath, std::unordered_map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); @@ -615,10 +928,32 @@ void saveBFile(std::string strPath, std::unordered_map& m) fout.close(); } +template +void saveBFile(const std::filesystem::path& fpPath, std::unordered_map& m) +{ + if (clrEmpty(fpPath, m))return; + std::ofstream fout(fpPath, ios::out | ios::trunc | ios::binary); + const int len = m.size(); + fwrite(fout, len); + for (auto& [key, val] : m) + { + fwrite(fout, key); + fwrite(fout, val); + } + fout.close(); +} + //ȡαxml template -void saveXML(std::string strPath, C& obj) +void saveXML(const std::string& strPath, C& obj) { std::ofstream fout(strPath); fout << obj.writet(); } + +template +void saveXML(const std::filesystem::path& fpPath, C& obj) +{ + std::ofstream fout(fpPath); + fout << obj.writet(); +} diff --git a/Dice/DiceGUI.cpp b/Dice/DiceGUI.cpp index 3eb56ddc..9ff6a80a 100644 --- a/Dice/DiceGUI.cpp +++ b/Dice/DiceGUI.cpp @@ -636,7 +636,7 @@ LRESULT DiceGUI::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) GlobalMsg[curr] = str; EditedMsg[curr] = str; ListViewCustomMsg.SetItemText(str, ListViewCustomMsgCurrentActivated, 1); - saveJMap(DiceDir + "/conf/CustomMsg.json", EditedMsg); + saveJMap(DiceDir / "conf" / "CustomMsg.json", EditedMsg); } return 0; case ID_MASTER_BUTTONMASTER: diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 43fec6b9..b004b866 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -349,11 +349,11 @@ void dice_cloudblack(DiceJob& job) { bool isSuccess(false); job.echo("ʼȡƶ˼¼"); string strURL("https://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); - switch (Cloud::DownloadFile(strURL.c_str(), (DiceDir + "/conf/CloudBlackList.json").c_str())) { + switch (Cloud::DownloadFile(strURL.c_str(), DiceDir / "conf" / "CloudBlackList.json")) { case -1: { string des; if (Network::GET("shiki.stringempty.xyz", "/blacklist/checked.json", 80, des)) { - ofstream fout(DiceDir + "/conf/CloudBlackList.json"); + ofstream fout(DiceDir / "conf" / "CloudBlackList.json"); fout << des << endl; isSuccess = true; } @@ -370,7 +370,7 @@ void dice_cloudblack(DiceJob& job) { } if (isSuccess) { if (job.fromQQ)job.note("ͬƲ¼ɹ" + getMsg("self") + "ʼȡ", 1); - blacklist->loadJson(DiceDir + "/conf/CloudBlackList.json", true); + blacklist->loadJson(DiceDir / "conf" / "CloudBlackList.json", true); } if (console["CloudBlackShare"]) sch.add_job_for(24 * 60 * 60, "cloudblack"); diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index e5789d90..1836f520 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -105,18 +105,22 @@ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { //luaű int loadLua(lua_State* L) { +#ifdef _WIN32 string nameFile{ UTF8toGBK(lua_tostring(L, -1),true) }; - string pathFile = DiceDir + "\\plugin\\" + nameFile + ".lua"; +#else + string nameFile{ lua_tostring(L, -1) }; +#endif + std::filesystem::path pathFile = DiceDir / "plugin" / (nameFile + ".lua"); if (!std::filesystem::exists(pathFile) && nameFile.find('/') == string::npos && nameFile.find('\\') == string::npos) - pathFile = DiceDir + "\\plugin\\" + nameFile + "\\init.lua"; - if (luaL_loadfile(L, pathFile.c_str())) { + pathFile = DiceDir / "plugin" / nameFile / "init.lua"; + if (luaL_loadfile(L, pathFile.string().c_str())) { const char* pErrorMsg = lua_tostring(L, -1); - console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + pathFile + "ʧ:"+ pErrorMsg, 0b10); + console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + UTF8toGBK(pathFile.u8string()) + "ʧ:"+ pErrorMsg, 0b10); return 0; } if (lua_pcall(L, 0, 1, 0)) { const char* pErrorMsg = lua_tostring(L, -1); - console.log(GlobalMsg["strSelfName"] + "luaļ" + pathFile + "ʧ:"+ pErrorMsg, 0b10); + console.log(GlobalMsg["strSelfName"] + "luaļ" + UTF8toGBK(pathFile.u8string()) + "ʧ:"+ pErrorMsg, 0b10); return 1; } return 1; @@ -128,7 +132,7 @@ int getDiceQQ(lua_State* L) { } //ȡDiceDir浵Ŀ¼ int getDiceDir(lua_State* L) { - lua_push_string(L, DiceDir); + lua_push_string(L, DiceDir.u8string()); return 1; } int mkDirs(lua_State* L) { @@ -300,7 +304,7 @@ void LuaState::regist() { } lua_getglobal(state, "package"); lua_getfield(state, -1, "path"); - string strPath(DiceDir + "\\plugin\\?.lua;" + lua_tostring(state, -1)); + string strPath((DiceDir / "plugin" / "?.lua").u8string() + lua_tostring(state, -1)); lua_pushstring(state, strPath.c_str()); lua_setfield(state, -3, "path"); lua_pop(state, 2); diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 7a81bb33..2cdeef68 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -117,7 +117,7 @@ void DiceModManager::_help(const shared_ptr& job) { } else job->reply("{strHelpNotFound}"); cntHelp[(*job)["help_word"]] += 1; - saveJMap(DiceDir + "/user/HelpStatic.json",cntHelp); + saveJMap(DiceDir / "user" / "HelpStatic.json",cntHelp); } void DiceModManager::set_help(const string& key, const string& val) @@ -149,7 +149,7 @@ int DiceModManager::load(ResList* resLog) vector sModFile; //ȡmod vector sModErr; - int cntFile = listDir(DiceDir + "/mod/", sModFile); + int cntFile = listDir(DiceDir / "mod" , sModFile); int cntItem{0}; if (cntFile > 0) { for (auto& pathFile : sModFile) { @@ -181,7 +181,7 @@ int DiceModManager::load(ResList* resLog) } //ȡplugin vector sLuaFile; - int cntLuaFile = listDir(DiceDir + "/plugin/", sLuaFile); + int cntLuaFile = listDir(DiceDir / "plugin", sLuaFile); int cntOrder{ 0 }; if (cntLuaFile <= 0)return cntLuaFile; vector sLuaErr; @@ -215,7 +215,7 @@ int DiceModManager::load(ResList* resLog) factory.detach(); if (cntHelp.empty()) { cntHelp.reserve(helpdoc.size()); - loadJMap(DiceDir + "/user/HelpStatic.json", cntHelp); + loadJMap(DiceDir / "user" / "HelpStatic.json", cntHelp); } return cntFile; } diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index c9d82081..56ecc37f 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -55,7 +55,7 @@ void DiceJob::reply(const std::string& msg) { AddMsgToQueue(format(msg, GlobalMsg, strVar), fromChat); } void DiceJob::note(const std::string& strMsg, int note_lv = 0b1) { - ofstream fout(DiceDir + "/audit/log" + to_string(console.DiceMaid) + "_" + printDate() + ".txt", ios::out | ios::app); + ofstream fout(DiceDir / "audit" / "log" / (to_string(console.DiceMaid) + "_" + printDate() + ".txt"), ios::out | ios::app); fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); echo(strMsg); diff --git a/Dice/DiceSchedule.h b/Dice/DiceSchedule.h index e8b5aeeb..5af7d93e 100644 --- a/Dice/DiceSchedule.h +++ b/Dice/DiceSchedule.h @@ -73,11 +73,11 @@ typedef void (*cmd)(DiceJob&); //ռ¼ class DiceToday { tm stToday; - string pathFile; + std::filesystem::path pathFile; unordered_mapcntGlobal; unordered_map>cntUser; public: - DiceToday(const string& path) :pathFile(path) { + DiceToday(const std::filesystem::path& path) :pathFile(path) { load(); } void load(); diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index a44af2c4..d5e8f8c3 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -100,13 +100,14 @@ void DiceSession::ob_clr(FromMsg* msg) } void DiceSession::log_new(FromMsg* msg) { - mkDir(DiceDir + logger.dirLog); + std::error_code ec; + std::filesystem::create_directory(DiceDir / logger.dirLog, ec); logger.tStart = time(nullptr); logger.isLogging = true; logger.fileLog = (type == "solo") ? ("qq_" + to_string(msg->fromQQ) + "_" + to_string(logger.tStart) + ".txt") : ("group_" + to_string(msg->fromGroup) + "_" + to_string(logger.tStart) + ".txt"); - logger.pathLog = DiceDir + LogInfo::dirLog + "/" + logger.fileLog; + logger.pathLog = DiceDir / logger.dirLog / logger.fileLog; //ȷϢ msg->reply(GlobalMsg["strLogNew"]); LogList.insert(room); @@ -154,13 +155,13 @@ void DiceSession::log_end(FromMsg* msg) { return; } msg->strVar["log_file"] = logger.fileLog; - msg->strVar["log_path"] = log_path(); + msg->strVar["log_path"] = UTF8toGBK(log_path().u8string()); msg->reply(GlobalMsg["strLogEnd"]); update(); msg->cmd_key = "uplog"; sch.push_job(*msg); } -string DiceSession::log_path()const { +std::filesystem::path DiceSession::log_path()const { return logger.pathLog; } @@ -500,10 +501,11 @@ std::mutex exSessionSave; void DiceSession::save() const { - mkDir(DiceDir + "/user/session"); - string pathFile = (type == "solo") - ? (DiceDir + R"(/user/session/Q)" + to_string(~room) + ".json" ) - : (DiceDir + R"(/user/session/)" + to_string(room) + ".json"); + std::error_code ec; + std::filesystem::create_directories(DiceDir / "user" / "session", ec); + std::filesystem::path fpFile = (type == "solo") + ? (DiceDir / "user" / "session" / ("Q" + to_string(~room) + ".json") ) + : (DiceDir / "user" / "session" / (to_string(room) + ".json")); nlohmann::json jData; if (!sOB.empty())jData["observer"] = sOB; if (!mTable.empty()) @@ -543,16 +545,16 @@ void DiceSession::save() const } std::lock_guard lock(exSessionSave); if (jData.empty()) { - remove(pathFile.c_str()); + remove(fpFile); return; } jData["type"] = type; jData["room"] = room; jData["create_time"] = tCreate; jData["update_time"] = tUpdate; - ofstream fout(pathFile); + ofstream fout(fpFile); if (!fout) { - console.log("Ϣʧ:" + pathFile, 1); + console.log("Ϣʧ:" + UTF8toGBK(fpFile.u8string()), 1); return; } fout << jData.dump(1); @@ -576,7 +578,7 @@ Session& DiceTableMaster::session(long long group) void DiceTableMaster::session_end(long long group) { std::unique_lock lock(sessionMutex); - remove((DiceDir + R"(/user/session/)" + to_string(group)).c_str()); + remove(DiceDir / "user" / "session" / to_string(group)); mSession.erase(group); } @@ -599,7 +601,7 @@ int DiceTableMaster::load() string strLog; std::unique_lock lock(sessionMutex); vector sFile; - int cnt = listDir(DiceDir + "/user/session/", sFile); + int cnt = listDir(DiceDir / "user" / "session", sFile); if (cnt <= 0)return cnt; for (auto& filename : sFile) { @@ -619,7 +621,7 @@ int DiceTableMaster::load() jLog["file"].get_to(pSession->logger.fileLog); jLog["logging"].get_to(pSession->logger.isLogging); pSession->logger.update(); - pSession->logger.pathLog = DiceDir + LogInfo::dirLog + "/" + pSession->logger.fileLog; + pSession->logger.pathLog = DiceDir / pSession->logger.dirLog / pSession->logger.fileLog; if (pSession->logger.isLogging) { LogList.insert(pSession->room); } diff --git a/Dice/DiceSession.h b/Dice/DiceSession.h index 0af3a0da..8790ee6d 100644 --- a/Dice/DiceSession.h +++ b/Dice/DiceSession.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "STLExtern.hpp" @@ -18,14 +19,14 @@ class FromMsg; class DiceTableMaster; struct LogInfo{ - static constexpr auto dirLog{ "/user/log" }; + const std::filesystem::path dirLog = std::filesystem::path("user") / "log"; bool isLogging{ false }; //ʱ䣬Ϊ0򲻴 time_t tStart{ 0 }; time_t tLastMsg{ 0 }; string fileLog; //·棬ʼʱ - string pathLog; + std::filesystem::path pathLog; void update() { tLastMsg = time(nullptr); } @@ -124,7 +125,7 @@ class DiceSession void log_on(FromMsg*); void log_off(FromMsg*); void log_end(FromMsg*); - [[nodiscard]] string log_path()const; + [[nodiscard]] std::filesystem::path log_path()const; [[nodiscard]] bool is_logging() const { return logger.isLogging; } //linkָ diff --git a/Dice/Jsonio.cpp b/Dice/Jsonio.cpp index 28ee2993..012bc87d 100644 --- a/Dice/Jsonio.cpp +++ b/Dice/Jsonio.cpp @@ -3,7 +3,7 @@ #include "StrExtern.hpp" #include "Jsonio.h" -nlohmann::json freadJson(const std::string& strPath) +[[DEPRECATED]] nlohmann::json freadJson(const std::string& strPath) { std::ifstream fin(strPath); if (!fin)return nlohmann::json(); @@ -35,8 +35,15 @@ nlohmann::json freadJson(const std::filesystem::path& path) return j; } -void fwriteJson(std::string strPath, const json& j) +[[DEPRECATED]] void fwriteJson(const std::string& strPath, const json& j) { std::ofstream fout(strPath); fout << std::setw(2) << j; } + +void fwriteJson(const std::filesystem::path& fpPath, const json& j) +{ + std::ofstream fout(fpPath); + fout << std::setw(2) << j; +} + diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index 7182529b..697f21dc 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -59,9 +59,10 @@ std::enable_if_t, T> readJKey(const std::string& strJson return stoll(strJson); } -nlohmann::json freadJson(const std::string& strPath); +[[DEPRECATED]] nlohmann::json freadJson(const std::string& strPath); nlohmann::json freadJson(const std::filesystem::path& path); -void fwriteJson(std::string strPath, const json& j); +[[DEPRECATED]] void fwriteJson(const std::string& strPath, const json& j); +void fwriteJson(const std::filesystem::path& strPath, const json& j); template int readJMap(const nlohmann::json& j, Map& mapTmp) @@ -101,7 +102,7 @@ int readJson(const std::string& strJson, std::map& mapTmp) } template -int loadJMap(const std::string& strLoc, Map& mapTmp) { +[[DEPRECATED]] int loadJMap(const std::string& strLoc, Map& mapTmp) { nlohmann::json j = freadJson(strLoc); if (j.is_null())return -2; try @@ -114,9 +115,24 @@ int loadJMap(const std::string& strLoc, Map& mapTmp) { } } +template +int loadJMap(const std::filesystem::path& fpLoc, Map& mapTmp) { + nlohmann::json j = freadJson(fpLoc); + if (j.is_null())return -2; + try + { + return readJMap(j, mapTmp); + } + catch (...) + { + return -1; + } +} + + //template template -void saveJMap(const std::string& strLoc, const C& mapTmp) +[[DEPRECATED]] void saveJMap(const std::string& strLoc, const C& mapTmp) { if (mapTmp.empty()) { remove(strLoc.c_str()); @@ -134,3 +150,23 @@ void saveJMap(const std::string& strLoc, const C& mapTmp) fout.close(); } } + +template +void saveJMap(const std::filesystem::path& fpLoc, const C& mapTmp) +{ + if (mapTmp.empty()) { + remove(fpLoc); + return; + } + std::ofstream fout(fpLoc); + if (fout) + { + nlohmann::json j; + for (auto& [key,val] : mapTmp) + { + j[GBKtoUTF8(key)] = GBKtoUTF8(val); + } + fout << j.dump(2); + fout.close(); + } +} diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index c961d100..e93130ce 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -7,6 +7,7 @@ #include #endif #include +#include #include "ManagerSystem.h" #include "CardDeck.h" @@ -14,8 +15,8 @@ #include "DDAPI.h" #include "CQTools.h" -string dirExe; -string DiceDir = "DiceData"; +std::filesystem::path dirExe; +std::filesystem::path DiceDir("DiceData"); //õͼƬб unordered_set sReferencedImage; diff --git a/vcpkg b/vcpkg index 371e41d8..f439a8e9 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 371e41d82165f5e74aa4ffc83c0e54a7e5d10209 +Subproject commit f439a8e96a5e3648adc7205eb102960bc44f4149 From 3124a6af4290cb92529a3b6594273a653f349e9c Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Mon, 15 Mar 2021 00:08:09 +0800 Subject: [PATCH 129/171] Fix Compilation --- Dice/Dice.cpp | 2 -- Dice/DiceCloud.cpp | 2 +- Dice/DiceCloud.h | 2 +- Dice/DiceFile.cpp | 6 +++--- Dice/DiceFile.hpp | 43 ++++++++++++++++++++++--------------------- Dice/Jsonio.cpp | 4 ++-- Dice/Jsonio.h | 8 ++++---- 7 files changed, 33 insertions(+), 34 deletions(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 717c96be..8d2bedaf 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -195,8 +195,6 @@ EVE_Enable(eventEnable) char path[MAX_PATH]; GetModuleFileNameA(nullptr, path, MAX_PATH); dirExe = std::filesystem::path(path).parent_path(); -#else - dirExe = {}; #endif } else diff --git a/Dice/DiceCloud.cpp b/Dice/DiceCloud.cpp index c420d612..23d3b1b9 100644 --- a/Dice/DiceCloud.cpp +++ b/Dice/DiceCloud.cpp @@ -59,7 +59,7 @@ namespace Cloud return 0; } - [[DEPRECATED]] int DownloadFile(const char* url, const char* downloadPath) + [[deprecated]] int DownloadFile(const char* url, const char* downloadPath) { #ifdef _WIN32 DeleteUrlCacheEntryA(url); diff --git a/Dice/DiceCloud.h b/Dice/DiceCloud.h index 0d127192..126e5f22 100644 --- a/Dice/DiceCloud.h +++ b/Dice/DiceCloud.h @@ -13,7 +13,7 @@ namespace Cloud { void heartbeat(); int checkWarning(const char* warning); - [[DEPRECATED]] int DownloadFile(const char* url, const char* downloadPath); + [[deprecated]] int DownloadFile(const char* url, const char* downloadPath); int DownloadFile(const char* url, const std::filesystem::path& downloadPath); int checkUpdate(FromMsg* msg); } diff --git a/Dice/DiceFile.cpp b/Dice/DiceFile.cpp index d6f4bc2b..2455f400 100644 --- a/Dice/DiceFile.cpp +++ b/Dice/DiceFile.cpp @@ -102,7 +102,7 @@ bool fscan(std::ifstream& fin, std::string& t) return false; } -[[DEPRECATED]] bool rdbuf(const string& strPath, string& s) +[[deprecated]] bool rdbuf(const string& strPath, string& s) { const std::ifstream fin(strPath); if (!fin)return false; @@ -222,7 +222,7 @@ ofstream& operator<<(ofstream& fout, const Chat& grp) } template -[[DEPRECATED]] int _listDir(const string& dir, vector& files) +[[deprecated]] int _listDir(const string& dir, vector& files) { int intFile = 0; std::error_code err; @@ -253,7 +253,7 @@ int _listDir(const std::filesystem::path& dir, vector& fi return err ? -1 : intFile; } -[[DEPRECATED]] int listDir(const string& dir, vector& files, bool isSub) +[[deprecated]] int listDir(const string& dir, vector& files, bool isSub) { if (isSub) { diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index 6f0f8629..4212a772 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -21,6 +21,7 @@ #include "StrExtern.hpp" #include "DiceMsgSend.h" #include "MsgFormat.h" +#include "EncodingConvert.h" using std::ifstream; using std::ofstream; @@ -213,7 +214,7 @@ void readini(string s, std::vector& v) } template -[[DEPRECATED]] int loadFile(const std::string& strPath, std::set& setTmp) +[[deprecated]] int loadFile(const std::string& strPath, std::set& setTmp) { std::ifstream fin(strPath); if (fin) @@ -251,7 +252,7 @@ int loadFile(const std::filesystem::path& fpPath, std::set& setTmp) } template -[[DEPRECATED]] int loadFile(const std::string& strPath, std::unordered_set& setTmp) +[[deprecated]] int loadFile(const std::string& strPath, std::unordered_set& setTmp) { std::ifstream fin(strPath); if (fin) @@ -289,7 +290,7 @@ int loadFile(const std::filesystem::path& fpPath, std::unordered_set& setTmp) } template -[[DEPRECATED]] int loadFile(const std::string& strPath, std::map& mapTmp) +[[deprecated]] int loadFile(const std::string& strPath, std::map& mapTmp) { std::ifstream fin(strPath); if (fin) @@ -327,7 +328,7 @@ int loadFile(const std::filesystem::path& fpPath, std::map& mapTmp) } template -[[DEPRECATED]] int loadFile(const std::string& strPath, std::unordered_map& mapTmp) +[[deprecated]] int loadFile(const std::string& strPath, std::unordered_map& mapTmp) { std::ifstream fin(strPath); if (fin) @@ -365,7 +366,7 @@ int loadFile(const std::filesystem::path& fpPath, std::unordered_map& ma } template -[[DEPRECATED]] void loadFile(const std::string& strPath, std::multimap& mapTmp) +[[deprecated]] void loadFile(const std::string& strPath, std::multimap& mapTmp) { std::ifstream fin(strPath); if (fin) @@ -397,7 +398,7 @@ void loadFile(const std::filesystem::path& fpPath, std::multimap& mapTmp } template -[[DEPRECATED]] int loadBFile(const std::string& strPath, std::map& m) +[[deprecated]] int loadBFile(const std::string& strPath, std::map& m) { std::ifstream fin(strPath, std::ios::in | std::ios::binary); if (!fin)return -1; @@ -433,7 +434,7 @@ int loadBFile(const std::filesystem::path& fpPath, std::map& m) } template -[[DEPRECATED]] int loadBFile(const std::string& strPath, std::unordered_map& m) +[[deprecated]] int loadBFile(const std::string& strPath, std::unordered_map& m) { std::ifstream fin(strPath, std::ios::in | std::ios::binary); if (!fin)return -1; @@ -470,7 +471,7 @@ int loadBFile(const std::filesystem::path& fpPath, std::unordered_map& m) //ȡαini template -[[DEPRECATED]] int loadINI(const std::string& strPath, std::map& m) +[[deprecated]] int loadINI(const std::string& strPath, std::map& m) { std::ifstream fin(strPath, std::ios::in | std::ios::binary); if (!fin)return -1; @@ -499,12 +500,12 @@ int loadINI(const std::filesystem::path& fpPath, std::map& m) return 1; } -[[DEPRECATED]] bool rdbuf(const string& strPath, string& s); +[[deprecated]] bool rdbuf(const string& strPath, string& s); bool rdbuf(const std::filesystem::path& fpPath, string& s); //ȡαxml template -[[DEPRECATED]] int loadXML(const std::string& strPath, std::map& m) +[[deprecated]] int loadXML(const std::string& strPath, std::map& m) { string s; if (!rdbuf(strPath, s))return -1; @@ -526,11 +527,11 @@ int loadXML(const std::filesystem::path& fpPath, std::map& m) } //ļ -[[DEPRECATED]] int listDir(const string& dir, vector& files, bool isSub = false); +[[deprecated]] int listDir(const string& dir, vector& files, bool isSub = false); int listDir(const std::filesystem::path& dir, vector& files, bool isSub = false); template -[[DEPRECATED]] int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2& tmp, int& intFile, int& intFailure, +[[deprecated]] int _loadDir(int (*load)(const std::string&, T2&), const std::string& strDir, T2& tmp, int& intFile, int& intFailure, int& intItem, std::vector& failureFiles) { std::error_code err; @@ -578,7 +579,7 @@ int _loadDir(int (*load)(const std::filesystem::path&, T2&), const std::filesyst //ȡļ template -[[DEPRECATED]] int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& tmp, ResList& logList, +[[deprecated]] int loadDir(int (*load)(const std::string&, T&), const std::string& strDir, T& tmp, ResList& logList, bool isSubdir = false) { int intFile = 0, intFailure = 0, intItem = 0; @@ -662,7 +663,7 @@ void fprint(std::ofstream& fout, std::pair t) } template -[[DEPRECATED]] bool clrEmpty(const std::string& strPath, const T& tmp) +[[deprecated]] bool clrEmpty(const std::string& strPath, const T& tmp) { if (tmp.empty()) { @@ -741,7 +742,7 @@ void fwrite(ofstream& fout, const std::set& s) } template -[[DEPRECATED]] void saveFile(const std::string& strPath, const T& setTmp) +[[deprecated]] void saveFile(const std::string& strPath, const T& setTmp) { if (clrEmpty(strPath, setTmp))return; std::ofstream fout(strPath); @@ -767,7 +768,7 @@ void saveFile(const std::filesystem::path& fpPath, const T& setTmp) } template -[[DEPRECATED]] void saveFile(const std::string& strPath, const map& mTmp) +[[deprecated]] void saveFile(const std::string& strPath, const map& mTmp) { if (clrEmpty(strPath, mTmp))return; std::ofstream fout(strPath); @@ -796,7 +797,7 @@ void saveFile(const std::filesystem::path& fpPath, const map& mTmp) template -[[DEPRECATED]] void saveFile(const std::string& strPath, const unordered_map& mTmp) +[[deprecated]] void saveFile(const std::string& strPath, const unordered_map& mTmp) { if (clrEmpty(strPath, mTmp))return; std::ofstream fout(strPath); @@ -824,7 +825,7 @@ void saveFile(const std::filesystem::path& fpPath, const unordered_map -[[DEPRECATED]] void saveBFile(const std::string& strPath, std::map& m) +[[deprecated]] void saveBFile(const std::string& strPath, std::map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); @@ -854,7 +855,7 @@ void saveBFile(const std::filesystem::path& fpPath, std::map& m) } template -[[DEPRECATED]] void saveBFile(const std::string& strPath, std::map& m) +[[deprecated]] void saveBFile(const std::string& strPath, std::map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); @@ -884,7 +885,7 @@ void saveBFile(const std::filesystem::path& fpPath, std::map& m) } template -[[DEPRECATED]] void saveBFile(const std::string& strPath, std::unordered_map& m) +[[deprecated]] void saveBFile(const std::string& strPath, std::unordered_map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); @@ -914,7 +915,7 @@ void saveBFile(const std::filesystem::path& fpPath, std::unordered_map& m) } template -[[DEPRECATED]] void saveBFile(const std::string& strPath, std::unordered_map& m) +[[deprecated]] void saveBFile(const std::string& strPath, std::unordered_map& m) { if (clrEmpty(strPath, m))return; std::ofstream fout(strPath, ios::out | ios::trunc | ios::binary); diff --git a/Dice/Jsonio.cpp b/Dice/Jsonio.cpp index 012bc87d..280cefa0 100644 --- a/Dice/Jsonio.cpp +++ b/Dice/Jsonio.cpp @@ -3,7 +3,7 @@ #include "StrExtern.hpp" #include "Jsonio.h" -[[DEPRECATED]] nlohmann::json freadJson(const std::string& strPath) +[[deprecated]] nlohmann::json freadJson(const std::string& strPath) { std::ifstream fin(strPath); if (!fin)return nlohmann::json(); @@ -35,7 +35,7 @@ nlohmann::json freadJson(const std::filesystem::path& path) return j; } -[[DEPRECATED]] void fwriteJson(const std::string& strPath, const json& j) +[[deprecated]] void fwriteJson(const std::string& strPath, const json& j) { std::ofstream fout(strPath); fout << std::setw(2) << j; diff --git a/Dice/Jsonio.h b/Dice/Jsonio.h index 697f21dc..ff06873e 100644 --- a/Dice/Jsonio.h +++ b/Dice/Jsonio.h @@ -59,9 +59,9 @@ std::enable_if_t, T> readJKey(const std::string& strJson return stoll(strJson); } -[[DEPRECATED]] nlohmann::json freadJson(const std::string& strPath); +[[deprecated]] nlohmann::json freadJson(const std::string& strPath); nlohmann::json freadJson(const std::filesystem::path& path); -[[DEPRECATED]] void fwriteJson(const std::string& strPath, const json& j); +[[deprecated]] void fwriteJson(const std::string& strPath, const json& j); void fwriteJson(const std::filesystem::path& strPath, const json& j); template @@ -102,7 +102,7 @@ int readJson(const std::string& strJson, std::map& mapTmp) } template -[[DEPRECATED]] int loadJMap(const std::string& strLoc, Map& mapTmp) { +[[deprecated]] int loadJMap(const std::string& strLoc, Map& mapTmp) { nlohmann::json j = freadJson(strLoc); if (j.is_null())return -2; try @@ -132,7 +132,7 @@ int loadJMap(const std::filesystem::path& fpLoc, Map& mapTmp) { //template template -[[DEPRECATED]] void saveJMap(const std::string& strLoc, const C& mapTmp) +[[deprecated]] void saveJMap(const std::string& strLoc, const C& mapTmp) { if (mapTmp.empty()) { remove(strLoc.c_str()); From 9e2777d5e0ed4cf3c468a1a2dbe41f7aa7598b72 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Mon, 15 Mar 2021 00:23:54 +0800 Subject: [PATCH 130/171] Fix random android regression --- vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index f439a8e9..371e41d8 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit f439a8e96a5e3648adc7205eb102960bc44f4149 +Subproject commit 371e41d82165f5e74aa4ffc83c0e54a7e5d10209 From 04df850ee075b5953abe119a44395dd0fd4d61ba Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Wed, 17 Mar 2021 11:52:23 +0800 Subject: [PATCH 131/171] Reduce the number of symbols exported --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34edaff0..0dd1d1ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,11 +49,14 @@ find_package(AWSSDK CONFIG COMPONENTS core s3 REQUIRED) target_include_directories(w4123.Dice PRIVATE ${AWSSDK_INCLUDE_DIRS}) target_link_libraries(w4123.Dice PRIVATE ${AWSSDK_LIBRARIES}) +set_target_properties(w4123.Dice PROPERTIES CXX_VISIBILITY_PRESET hidden) +set_target_properties(w4123.Dice PROPERTIES C_VISIBILITY_PRESET hidden) + if(CMAKE_SYSTEM_NAME STREQUAL Android) target_include_directories(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/include) target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/lib/libiconv.a) endif() if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")) - set_target_properties(w4123.Dice PROPERTIES LINK_FLAGS_RELEASE -s) + set_target_properties(w4123.Dice PROPERTIES LINK_FLAGS_RELEASE "-s -Wl,--exclude-libs,ALL") endif() From 00deeae8e0d3c74e99b932a4e0a47dc96fc7255c Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Wed, 17 Mar 2021 12:13:16 +0800 Subject: [PATCH 132/171] Fix OSX Compilation --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dd1d1ff..d20a1039 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL Android) target_link_libraries(w4123.Dice PRIVATE ${CMAKE_SOURCE_DIR}/libiconv/${ANDROID_ABI}/lib/libiconv.a) endif() -if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")) +if(NOT ((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))) set_target_properties(w4123.Dice PROPERTIES LINK_FLAGS_RELEASE "-s -Wl,--exclude-libs,ALL") endif() From 055ec88da45edc3a50d877aec002ae690700de03 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Wed, 17 Mar 2021 12:54:27 +0800 Subject: [PATCH 133/171] Reduce the number of dependencies --- vcpkg.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vcpkg.json b/vcpkg.json index b67dacb6..a34edf25 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -5,6 +5,7 @@ "openssl", { "name": "aws-sdk-cpp", + "default-features": false, "features": [ "s3" ] }, { @@ -13,6 +14,7 @@ }, { "name": "curl", + "default-features": false, "features": [ "openssl" ], "platform" : "!windows" }, From b40daa1a84126e03e8cec24625a9b65e983347d1 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 24 Mar 2021 17:24:43 +0800 Subject: [PATCH 134/171] fix strSelfName --- Dice/Dice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 8d2bedaf..381422a3 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -260,7 +260,7 @@ EVE_Enable(eventEnable) GlobalMsg["strSelfName"] = DD::getLoginNick(); if (GlobalMsg["strSelfName"].empty()) { - GlobalMsg["strSelfName"] = "[" + toString(console.DiceMaid % 1000, 4) + "]"; + GlobalMsg["strSelfName"] = "[" + toString(console.DiceMaid % 10000, 4) + "]"; } std::error_code ec; std::filesystem::create_directory(DiceDir / "conf", ec); From 1b5619fa33e69abb5c5397c0d9d9d932674434fb Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sat, 27 Mar 2021 18:47:39 +0800 Subject: [PATCH 135/171] fix dismiss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复对admin响应2次dismiss的bug --- Dice/DiceEvent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 932b1433..d830268d 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -905,6 +905,7 @@ int FromMsg::BasicOrder() if (trusted > 2) { pGrp->leave(getMsg("strAdminDismiss", strVar)); + return 1; } if (pGrp->isset("ЭЧ"))return 0; if (isAuth) From e553d95fd94b71fdf33ae05500ff092cace52de0 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 29 Mar 2021 23:16:17 +0800 Subject: [PATCH 136/171] Update notice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 扩展通知窗口槽位,允许主动广播窗口通知 --- Dice/DiceEvent.cpp | 23 ++++++++++++++++++++++- Dice/DiceJob.cpp | 2 +- Dice/MsgFormat.cpp | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index d830268d..9ade10c0 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -359,7 +359,7 @@ int FromMsg::AdminEvent(const string& strOption) bool isReduce = strMsg[intMsgCnt] == '-'; string strNum = readDigit(); if (strNum.empty() || strNum.length() > 1)break; - if (int intNum = stoi(strNum); intNum > 5)continue; + if (int intNum = stoi(strNum); intNum > 9)continue; else { if (isReduce)intReduce |= (1 << intNum); @@ -2108,6 +2108,27 @@ int FromMsg::InnerOrder() { } return 1; } + else if (strLowerMessage.substr(intMsgCnt, 6) == "notice" && trusted > 3) { + intMsgCnt += 6; + int intLv = 0; + string strNum{ readDigit(false) }; + while (!strNum.empty()){ + if (strNum.length() > 1)break; + if (int intNum = stoi(strNum); intNum > 9)continue; + else { + intLv |= (1 << intNum); + } + if (strLowerMessage[intMsgCnt] == '+')++intMsgCnt; + strNum = readDigit(false); + } + string strNotice(readRest()); + if (intLv && !strNotice.empty()){ + console.log(strNotice, intLv); + reply(GlobalMsg["strSendMsg"]); + } + else reply(GlobalMsg["strParaEmpty"]); + return 1; + } readSkipColon(); } else if (!console) { diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index b004b866..021d95f4 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -140,7 +140,7 @@ void check_system(DiceJob& job) { void auto_save(DiceJob& job) { if (sch.is_job_cold("autosave"))return; dataBackUp(); - console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); + //console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); if (console["AutoSaveInterval"] > 0) { sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"]); sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 1dd9f136..32c4cb27 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -82,7 +82,7 @@ string strip(std::string origin) std::string to_binary(int b) { ResList res; - for (int i = 0; i < 6; i++) + for (int i = 0; i < 14; i++) { if (b & (1 << i))res << std::to_string(i); } From 366341825eb6e4df76ed3a342fdbc57f1f14d897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Thu, 1 Apr 2021 12:20:54 +0800 Subject: [PATCH 137/171] Use a lower glibc version in Linux Builds (#52) * Update cmake.yml * Fix * Update cmake.yml --- .github/workflows/cmake.yml | 26 +++++++++++++------------- Dice/Jsonio.cpp | 1 + 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index fb5eaae2..642e361b 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -141,27 +141,27 @@ jobs: fail-fast: false matrix: include: - - apt: g++-9 - cc: gcc-9 - cxx: g++-9 + - apt: g++-8 + cc: gcc-8 + cxx: g++-8 triplet: x64-linux - path: /usr/i686-linux-gnu - apt: g++-i686-linux-gnu - cc: i686-linux-gnu-gcc - cxx: i686-linux-gnu-g++ + apt: g++-8-i686-linux-gnu + cc: i686-linux-gnu-gcc-8 + cxx: i686-linux-gnu-g++-8 triplet: x86-linux - path: /usr/aarch64-linux-gnu - apt: g++-aarch64-linux-gnu - cc: aarch64-linux-gnu-gcc - cxx: aarch64-linux-gnu-g++ + apt: g++-8-aarch64-linux-gnu g++-aarch64-linux-gnu + cc: aarch64-linux-gnu-gcc-8 + cxx: aarch64-linux-gnu-g++-8 triplet: arm64-linux - path: /usr/arm-linux-gnueabihf - apt: g++-arm-linux-gnueabihf - cc: arm-linux-gnueabihf-gcc - cxx: arm-linux-gnueabihf-g++ + apt: g++-8-arm-linux-gnueabihf g++-arm-linux-gnueabihf + cc: arm-linux-gnueabihf-gcc-8 + cxx: arm-linux-gnueabihf-g++-8 triplet: arm-linux - runs-on: ubuntu-20.04 + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 diff --git a/Dice/Jsonio.cpp b/Dice/Jsonio.cpp index 280cefa0..7c60a5e3 100644 --- a/Dice/Jsonio.cpp +++ b/Dice/Jsonio.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "StrExtern.hpp" #include "Jsonio.h" From d19260a261224684b97e9e4c89a3371b5fbb4b9b Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Fri, 2 Apr 2021 17:01:15 +0800 Subject: [PATCH 138/171] Link fs library on GCC 8 and Clang 7-8 --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d20a1039..472944ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,11 +23,13 @@ set_property(TARGET w4123.Dice PROPERTY SUFFIX ".dll") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "-fpermissive ${CMAKE_CXX_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ -static-libgcc ${CMAKE_SHARED_LINKER_FLAGS}") + target_link_libraries(w4123.Dice PRIVATE $<$,9.0>:stdc++fs>) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding ${CMAKE_CXX_FLAGS}") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding ${CMAKE_CXX_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ ${CMAKE_SHARED_LINKER_FLAGS}") + target_link_libraries(w4123.Dice PRIVATE $<$,9.0>:c++fs>) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") string(REGEX MATCH static IS_STATIC ${VCPKG_TARGET_TRIPLET}) if(IS_STATIC STREQUAL static) From f66dbf98eac12e89685cb6ad2040f1901c3f4190 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 2 Apr 2021 23:38:38 +0800 Subject: [PATCH 139/171] update ConsoleTime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 允许定时任务调用lua脚本 --- .gitignore | 5 +++ Dice/Dice.cpp | 6 ++-- Dice/DiceConsole.cpp | 79 +++---------------------------------------- Dice/DiceConsole.h | 12 +++---- Dice/DiceEvent.cpp | 13 ++++--- Dice/DiceLua.cpp | 11 ++++++ Dice/DiceLua.h | 1 + Dice/DiceMod.cpp | 51 +++++++++++++++++++++++++++- Dice/DiceMod.h | 3 ++ Dice/DiceSchedule.cpp | 50 +++++++++++++++++++++++++++ 10 files changed, 141 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index 9c11a4ff..2de31c91 100644 --- a/.gitignore +++ b/.gitignore @@ -40,5 +40,10 @@ /packages /docs/html /Debug +/Win32 /x64 /Dice-cppcheck-build-dir +/w4123.Dice.dir + +/CMakeFiles +/vcpkg_installed \ No newline at end of file diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 381422a3..f748add6 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -281,8 +281,8 @@ EVE_Enable(eventEnable) console.set("Private", iPrivate); console.set("DisabledJrrp", iDisabledJrrp); console.set("LeaveDiscuss", iLeaveDiscuss); - console.setClock(ClockToWork, ClockEvent::on); - console.setClock(ClockOffWork, ClockEvent::off); + console.setClock(ClockToWork, "on"); + console.setClock(ClockOffWork, "off"); } else { @@ -295,7 +295,7 @@ EVE_Enable(eventEnable) { console.set(key, val); } - console.setClock({ 11, 45 }, ClockEvent::clear); + console.setClock({ 11, 45 }, "clear"); console.loadNotice(); console.save(); } diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 4bef9129..b4c6659a 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -59,18 +59,17 @@ const std::mapConsole::intDefault{ //ȺԼϢԼ˽Ϣ {"ListenGroupEcho",0},{"ListenSelfEcho",0}, }; -const enumap Console::mClockEvent{"off", "on", "save", "clear"}; +const enumap Console::mClockEvent{ "off", "on", "save", "clear" }; -int Console::setClock(Clock c, ClockEvent e) +int Console::setClock(Clock c, const string& e) { if (c.first > 23 || c.second > 59)return -1; - if (static_cast(e) > 3)return -2; mWorkClock.emplace(c, e); save(); return 0; } -int Console::rmClock(Clock c, ClockEvent e) +int Console::rmClock(Clock c, const string& e) { if (const auto it = match(mWorkClock, c, e); it != mWorkClock.end()) { @@ -86,23 +85,7 @@ ResList Console::listClock() const ResList list; for (const auto& [clock, eve] : mWorkClock) { - string strClock = printClock(clock); - switch (eve) - { - case ClockEvent::on: - strClock += " ʱ"; - break; - case ClockEvent::off: - strClock += " ʱر"; - break; - case ClockEvent::save: - strClock += " ʱ"; - break; - case ClockEvent::clear: - strClock += " ʱȺ"; - break; - default: break; - } + string strClock = printClock(clock) + " " + eve; list << strClock; } return list; @@ -233,7 +216,6 @@ long long llStartTime = time(nullptr); //ǰʱ tm stNow{}; -tm stTmp{}; std::string printSTNow() { @@ -341,59 +323,6 @@ bool operator<(const Console::Clock clock, const tm& st) return st.tm_hour == clock.first && st.tm_hour == clock.second; } -//׼ʱ - void ConsoleTimer() - { - Console::Clock clockNow{stNow.tm_hour,stNow.tm_min}; - while (Enabled) - { - time_t tt = time(nullptr); -#ifdef _MSC_VER - localtime_s(&stNow, &tt); -#else - localtime_r(&tt, &stNow); -#endif - //ʱ䶯 - if (stTmp.tm_min != stNow.tm_min) - { - stTmp = stNow; - clockNow = {stNow.tm_hour, stNow.tm_min}; - for (const auto& [clock,eve_type] : multi_range(console.mWorkClock, clockNow)) - { - switch (eve_type) - { - case ClockEvent::on: - if (console["DisabledGlobal"]) - { - console.set("DisabledGlobal", 0); - console.log(getMsg("strClockToWork"), 0b10000, ""); - } - break; - case ClockEvent::off: - if (!console["DisabledGlobal"]) - { - console.set("DisabledGlobal", 1); - console.log(getMsg("strClockOffWork"), 0b10000, ""); - } - break; - case ClockEvent::save: - dataBackUp(); - console.log(GlobalMsg["strSelfName"] + "ʱɡ", 1, printSTime(stTmp)); - break; - case ClockEvent::clear: - sch.push_job("clrgroup", true, { - {"clear_mode","black"} - }); - break; - default: break; - } - } - } - this_thread::sleep_for(100ms); - } - } - - void ThreadFactory::exit() { rear = 0; for (auto& th : vTh) { diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index 17212cc3..472834b9 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -27,7 +27,7 @@ using std::to_string; extern std::filesystem::path dirExe; extern std::filesystem::path DiceDir; -enum class ClockEvent { off, on, save, clear }; +//enum class ClockEvent { off, on, save, clear }; class Console { @@ -82,8 +82,8 @@ class Console return 0; } - int setClock(Clock c, ClockEvent e); - int rmClock(Clock c, ClockEvent e); + int setClock(Clock c, const string&); + int rmClock(Clock c, const string&); [[nodiscard]] ResList listClock() const; [[nodiscard]] ResList listNotice() const; [[nodiscard]] int showNotice(chatType ct) const; @@ -113,7 +113,7 @@ class Console for (auto& child : xml["clock"].vChild) { if (mClockEvent.count(child.tag))mWorkClock.insert({ - scanClock(child.strValue), static_cast(mClockEvent[child.tag]) + scanClock(child.strValue), child.tag }); } if (xml.count("conf")) @@ -138,7 +138,7 @@ class Console DDOM clocks("clock", ""); for (auto& [clock, type] : mWorkClock) { - clocks.push(DDOM(mClockEvent[static_cast(type)], printClock(clock))); + clocks.push(DDOM(type, printClock(clock))); } xml.push(clocks); } @@ -160,7 +160,7 @@ class Console private: std::filesystem::path fpPath; std::map intConf; - std::multimap mWorkClock{}; + std::multimap mWorkClock{}; std::map NoticeList{}; }; extern Console console; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 9ade10c0..5a946b51 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -66,7 +66,8 @@ void FromMsg::replyHidden() { while (isspace(static_cast(strReply[0]))) strReply.erase(strReply.begin()); formatReply(); - if (LogList.count(fromSession) && gm->session(fromSession).is_logging()) { + if (LogList.count(fromSession) && gm->session(fromSession).is_logging() + && (fromChat.second == msgtype::Private || !console["ListenGroupEcho"])) { filter_CQcode(strReply, fromGroup); ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); logout << GBKtoUTF8(getMsg("strSelfName")) + "(" + to_string(console.DiceMaid) + ") " + printTTime(fromTime) << endl @@ -101,7 +102,8 @@ void FromMsg::fwdMsg() string msg = strMsg; filter_CQcode(msg, fromGroup); ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); - logout << GBKtoUTF8(printQQ(fromQQ)) + " " + printTTime(fromTime) << endl + if (!strVar.count("pc") || strVar["pc"].empty())getPCName(*this); + logout << GBKtoUTF8(strVar["pc"]) + " " + printTTime(fromTime) << endl << GBKtoUTF8(msg) << endl << endl; } } @@ -294,7 +296,7 @@ int FromMsg::AdminEvent(const string& strOption) intMsgCnt++; } string strType = readPara(); - if (strType.empty() || !Console::mClockEvent.count(strType)) + if (strType.empty()) { reply(GlobalMsg["strSelfName"] + "Ķʱб" + console.listClock().show()); return 1; @@ -305,13 +307,13 @@ int FromMsg::AdminEvent(const string& strOption) case 0: if (isErase) { - if (console.rmClock(cc, ClockEvent(Console::mClockEvent[strType])))reply( + if (console.rmClock(cc, strType))reply( GlobalMsg["strSelfName"] + "޴˶ʱĿ"); else note("Ƴ" + GlobalMsg["strSelfName"] + "" + printClock(cc) + "Ķʱ" + strType, 0b10); } else { - console.setClock(cc, ClockEvent(Console::mClockEvent[strType])); + console.setClock(cc, strType); note("" + GlobalMsg["strSelfName"] + "" + printClock(cc) + "Ķʱ" + strType, 0b10); } break; @@ -1870,6 +1872,7 @@ int FromMsg::InnerOrder() { intMsgCnt += 4; bool isPrivate(false); if (strMsg[intMsgCnt] == 'h' && isspace(static_cast(strMsg[intMsgCnt + 1]))) { + strVar["hidden"]; isPrivate = true; ++intMsgCnt; } diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 1836f520..7271d92e 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -98,6 +98,17 @@ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { } return true; } +bool lua_call_task(const char* file, const char* func) { + LuaState L(file); + if (!L)return false; + lua_getglobal(L, func); + if (lua_pcall(L, 0, 0, 0)) { + const char* pErrorMsg = lua_tostring(L, -1); + console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ʧ!\n" + pErrorMsg, 1); + return false; + } + return true; +} /** * luaõĺ diff --git a/Dice/DiceLua.h b/Dice/DiceLua.h index f1b09bae..4aed1ac8 100644 --- a/Dice/DiceLua.h +++ b/Dice/DiceLua.h @@ -10,4 +10,5 @@ class Lua_State; class FromMsg; bool lua_msg_order(FromMsg*, const char*, const char*); +bool lua_call_task(const char*, const char*); int lua_readStringTable(const char*, const char*, std::unordered_map&); \ No newline at end of file diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 2cdeef68..e06c9abf 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -1,3 +1,26 @@ +/* + * _______ ________ ________ ________ __ + * | __ \ |__ __| | _____| | _____| | | + * | | | | | | | | | |_____ | | + * | | | | | | | | | _____| |__| + * | |__| | __| |__ | |_____ | |_____ __ + * |_______/ |________| |________| |________| |__| + * + * Dice! QQ Dice Robot for TRPG + * Copyright (C) 2018-2021 w4123 + * Copyright (C) 2019-2021 String.Empty + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this + * program. If not, see . + */ #include #include "DiceMod.h" #include "GlobalVar.h" @@ -17,6 +40,15 @@ bool DiceMsgOrder::exec(FromMsg* msg) { } return false; } +bool DiceMsgOrder::exec() { + if (type == OrderType::Lua) { + std::thread th(lua_call_task, fileLua.c_str(), funcLua.c_str()); + th.detach(); + //lua_call_task(fileLua.c_str(), funcLua.c_str()); + return true; + } + return false; +} DiceModManager::DiceModManager() : helpdoc(HelpDoc) { @@ -140,10 +172,14 @@ bool DiceModManager::listen_order(DiceJobDetail* msg) { msgorder[nameOrder].exec((FromMsg*)msg); return true; } -string DiceModManager::list_order() { +string DiceModManager::list_order() { return "չָ:" + listKey(msgorder); } +bool DiceModManager::call_task(const string& task) { + return taskcall[task].exec(); +} + int DiceModManager::load(ResList* resLog) { vector sModFile; @@ -203,6 +239,18 @@ int DiceModManager::load(ResList* resLog) else if (cnt < 0) { sLuaErr.push_back(pathFile.filename().string()); } + std::unordered_map mJob; + cnt = lua_readStringTable(fileLua.c_str(), "task_call", mJob); + if (cnt > 0) { + for (auto& [key, func] : mJob) { + taskcall[key] = { fileLua,func }; + } + cntOrder += mJob.size(); + } + else if (cnt < 0 + && *sLuaErr.rbegin() != pathFile.filename().string()) { + sLuaErr.push_back(pathFile.filename().string()); + } } *resLog << "ȡ/plugin/е" + std::to_string(cntLuaFile) + "ű, " + std::to_string(cntOrder) + "ָ"; if (!sLuaErr.empty()) { @@ -231,4 +279,5 @@ void DiceModManager::clear() { helpdoc.clear(); msgorder.clear(); + taskcall.clear(); } diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index c0f26426..c12fe886 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -49,6 +49,7 @@ class DiceMsgOrder { type = OrderType::Lua; } bool exec(FromMsg*); + bool exec(); }; class DiceMod @@ -92,6 +93,7 @@ class DiceModManager map mNameIndex; map helpdoc; map msgorder; + map taskcall; WordQuerier querier; TrieG gOrder; public: @@ -106,6 +108,7 @@ class DiceModManager void rm_help(const string&); bool listen_order(DiceJobDetail*); + bool call_task(const string&); string list_order(); int load(ResList*); void init(); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 56ecc37f..18d54dd0 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -9,8 +9,10 @@ #include "Jsonio.h" #include "DiceSchedule.h" #include "DiceNetwork.h" +#include "DiceMod.h" #include "RandomGenerator.h" #include +#include unordered_map mCommand = { {"syscheck",check_system}, @@ -251,4 +253,52 @@ string printTTime(time_t tt) { #endif strftime(tm_buffer, 20, "%Y-%m-%d %H:%M:%S", &t); return tm_buffer; +} + +//׼ʱ +tm stTmp{}; +void ConsoleTimer() { + Console::Clock clockNow{ stNow.tm_hour,stNow.tm_min }; + while (Enabled) { + time_t tt = time(nullptr); +#ifdef _MSC_VER + localtime_s(&stNow, &tt); +#else + localtime_r(&tt, &stNow); +#endif + //ʱ䶯 + if (stTmp.tm_min != stNow.tm_min) { + stTmp = stNow; + clockNow = { stNow.tm_hour, stNow.tm_min }; + for (const auto& [clock, eve_type] : multi_range(console.mWorkClock, clockNow)){ + switch (Console::mClockEvent[eve_type]) { + case 1: + if (console["DisabledGlobal"]) { + console.set("DisabledGlobal", 0); + console.log(getMsg("strClockToWork"), 0b10000, ""); + } + break; + case 0: + if (!console["DisabledGlobal"]) { + console.set("DisabledGlobal", 1); + console.log(getMsg("strClockOffWork"), 0b10000, ""); + } + break; + case 2: + dataBackUp(); + console.log(GlobalMsg["strSelfName"] + "ʱɡ", 1, printSTime(stTmp)); + break; + case 3: + sch.push_job("clrgroup", true, { + {"clear_mode","black"} + }); + break; + default: + fmt->call_task(eve_type); + break; + } + } + } + std::this_thread::sleep_for(100ms); + } } \ No newline at end of file From 38ca2cee83c1d9b2e666f31824f199c930af4406 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sat, 3 Apr 2021 15:31:03 +0800 Subject: [PATCH 140/171] Use dynamic CXX library and use -Bsymbolic to force use local symbols --- CMakeLists.txt | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 472944ad..3c02ee38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,13 +22,11 @@ set_property(TARGET w4123.Dice PROPERTY SUFFIX ".dll") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "-fpermissive ${CMAKE_CXX_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ -static-libgcc ${CMAKE_SHARED_LINKER_FLAGS}") target_link_libraries(w4123.Dice PRIVATE $<$,9.0>:stdc++fs>) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding ${CMAKE_CXX_FLAGS}") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding ${CMAKE_CXX_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ ${CMAKE_SHARED_LINKER_FLAGS}") target_link_libraries(w4123.Dice PRIVATE $<$,9.0>:c++fs>) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") string(REGEX MATCH static IS_STATIC ${VCPKG_TARGET_TRIPLET}) @@ -38,6 +36,25 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") endif() endif() +include(CheckCSourceCompiles) + +list(APPEND CMAKE_REQUIRED_LIBRARIES "-Wl,-Bsymbolic-functions -Wl,-Bsymbolic") + +check_c_source_compiles( +[=[ +int main () +{ + return 0; +} +]=] +BSYMBOLIC_WORKS +) +list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "-Wl,-Bsymbolic-functions -Wl,-Bsymbolic") + +if (BSYMBOLIC_WORKS) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic-functions -Wl,-Bsymbolic") +endif() + if(NOT ((CMAKE_SYSTEM_NAME STREQUAL Windows) OR (CMAKE_SYSTEM_NAME STREQUAL Android))) find_package(CURL REQUIRED) target_link_libraries(w4123.Dice PRIVATE CURL::libcurl) From 8d7247a524f65a940e825f603a6c4f3eb93ca35c Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Sat, 3 Apr 2021 19:10:06 +0800 Subject: [PATCH 141/171] Enable LTO --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c02ee38..e1c06273 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,14 @@ if (BSYMBOLIC_WORKS) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic-functions -Wl,-Bsymbolic") endif() +include(CheckIPOSupported) +check_ipo_supported(RESULT supported) + +if(supported) + set_property(TARGET w4123.Dice PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + + if(NOT ((CMAKE_SYSTEM_NAME STREQUAL Windows) OR (CMAKE_SYSTEM_NAME STREQUAL Android))) find_package(CURL REQUIRED) target_link_libraries(w4123.Dice PRIVATE CURL::libcurl) From 825ff98394416bb42a3fa946600d2e640ebd9164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=AF=E6=B4=84?= <1840686745@qq.com> Date: Sun, 4 Apr 2021 17:42:03 +0800 Subject: [PATCH 142/171] Workaround CMake Bugs (#53) --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e1c06273..93b2cfca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "-fpermissive -Wno-invalid-source-encoding ${CMAKE_CXX_FLAGS}") target_link_libraries(w4123.Dice PRIVATE $<$,9.0>:c++fs>) + if(CMAKE_SYSTEM_NAME STREQUAL Android) + set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ ${CMAKE_SHARED_LINKER_FLAGS}") + endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") string(REGEX MATCH static IS_STATIC ${VCPKG_TARGET_TRIPLET}) if(IS_STATIC STREQUAL static) @@ -60,6 +63,9 @@ check_ipo_supported(RESULT supported) if(supported) set_property(TARGET w4123.Dice PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + if(CMAKE_SYSTEM_NAME STREQUAL Android) + STRING(REGEX REPLACE "-fuse-ld=gold" "" CMAKE_CXX_LINK_OPTIONS_IPO ${CMAKE_CXX_LINK_OPTIONS_IPO}) + endif() endif() From fceaeb5b7dd42af7341c9341394352b9f2c585c2 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 9 Apr 2021 00:49:51 +0800 Subject: [PATCH 143/171] fix MsgMonitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复自我响应监测的误判 --- Dice/DiceConsole.cpp | 29 ++++++++++++++++++++++++++--- Dice/DiceConsole.h | 28 ++-------------------------- Dice/DiceEvent.cpp | 2 +- Dice/GlobalVar.h | 10 +++++----- Dice/MsgMonitor.cpp | 8 ++++---- 5 files changed, 38 insertions(+), 39 deletions(-) diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index b4c6659a..225bdfcf 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -7,8 +7,8 @@ * |_______/ |________| |________| |________| |__| * * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2020 w4123 - * Copyright (C) 2019-2020 String.Empty + * Copyright (C) 2018-2021 w4123 + * Copyright (C) 2019-2021 String.Empty * * This program is free software: you can redistribute it and/or modify it under the terms * of the GNU Affero General Public License as published by the Free Software Foundation, @@ -145,7 +145,8 @@ int Console::log(const std::string& strMsg, int note_lv, const string& strTime) { if (!(level & note_lv))continue; AddMsgToQueue(note, ct); - Cnt++; + Cnt++; + if(strTime.empty())this_thread::sleep_for(chrono::milliseconds(console["SendIntervalIdle"])); } if (!Cnt)DD::sendPrivateMsg(DiceMaid, note); } @@ -169,6 +170,28 @@ void Console::reset() NoticeList.clear(); } +bool Console::load() { + string s; + //DSens.build({ {"nnϹ",2 } }); + if (!rdbuf(fpPath, s))return false; + DDOM xml(s); + if (xml.count("mode"))isMasterMode = stoi(xml["mode"].strValue); + if (xml.count("master"))masterQQ = stoll(xml["master"].strValue); + if (xml.count("clock")) + for (auto& child : xml["clock"].vChild) { + mWorkClock.insert({ + scanClock(child.strValue), child.tag + }); + } + if (xml.count("conf")) + for (auto& child : xml["conf"].vChild) { + std::pair conf; + readini(child.strValue, conf); + if (intDefault.count(conf.first))intConf.insert(conf); + } + loadNotice(); + return true; +} void Console::loadNotice() { if (loadFile(DiceDir / "conf" / "NoticeList.txt", NoticeList) < 1) diff --git a/Dice/DiceConsole.h b/Dice/DiceConsole.h index 472834b9..f6aaa769 100644 --- a/Dice/DiceConsole.h +++ b/Dice/DiceConsole.h @@ -1,7 +1,7 @@ #pragma once /* - * Copyright (C) 2019-2020 String.Empty + * Copyright (C) 2019-2021 String.Empty */ #ifndef Dice_Console @@ -101,31 +101,7 @@ class Console void rmNotice(chatType ct); void reset(); - bool load() - { - string s; - //DSens.build({ {"nnϹ",2 } }); - if (!rdbuf(fpPath, s))return false; - DDOM xml(s); - if (xml.count("mode"))isMasterMode = stoi(xml["mode"].strValue); - if (xml.count("master"))masterQQ = stoll(xml["master"].strValue); - if (xml.count("clock")) - for (auto& child : xml["clock"].vChild) - { - if (mClockEvent.count(child.tag))mWorkClock.insert({ - scanClock(child.strValue), child.tag - }); - } - if (xml.count("conf")) - for (auto& child : xml["conf"].vChild) - { - std::pair conf; - readini(child.strValue, conf); - if (intDefault.count(conf.first))intConf.insert(conf); - } - loadNotice(); - return true; - } + bool load(); void save() { std::error_code ec; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 5a946b51..4a8d2720 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1591,7 +1591,7 @@ int FromMsg::InnerOrder() { : reply("DZˮԱб:" + res.show(1)); return 1; } - if (bool isAdmin = DD::isGroupAdmin(llGroup, fromQQ, false); Command == "pause") { + if (bool isAdmin = DD::isGroupAdmin(llGroup, fromQQ, true); Command == "pause") { if (!isAdmin && trusted < 4) { reply(GlobalMsg["strPermissionDeniedErr"]); return 1; diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index f6a4c53e..522dfd3b 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -9,8 +9,8 @@ * |_______/ |________| |________| |________| |__| * * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2020 w4123 - * Copyright (C) 2019-2020 String.Empty + * Copyright (C) 2018-2021 w4123 + * Copyright (C) 2019-2021 String.Empty * * This program is free software: you can redistribute it and/or modify it under the terms * of the GNU Affero General Public License as published by the Free Software Foundation, @@ -39,9 +39,9 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 575u; -inline const std::string Dice_Ver_Without_Build = "2.5.0"; -constexpr auto DiceRequestHeader = "Dice/2.5.0"; +const unsigned short Dice_Build = 576u; +inline const std::string Dice_Ver_Without_Build = "2.5.1"; +constexpr auto DiceRequestHeader = "Dice/2.5.1"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index 374e0cd3..803e3549 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -81,14 +81,14 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT if (mFrequence.count(fromQQ)) { mFrequence[fromQQ] += 10; mCntOrder[fromQQ] += 1; - if ((!console["ListenSpam"] || trustedQQ(fromQQ) > 1) && !console.is_self(QQ))return; + if ((!console["ListenSpam"] || trustedQQ(fromQQ) > 1) && QQ!=console.DiceMaid)return; if (mFrequence[fromQQ] > 60 && mWarnLevel[fromQQ] < 60 && QQ) { mWarnLevel[fromQQ] = mFrequence[fromQQ]; const std::string strMsg = "ѣ\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 18 ? "/5min" : (mCntOrder[fromQQ] > 8 ? "/min" : "/30s")); - if(!console.is_self(QQ))AddMsgToQueue(getMsg("strSpamFirstWarning"), CT); + if(QQ!=console.DiceMaid)AddMsgToQueue(getMsg("strSpamFirstWarning"), CT); console.log(strMsg, 1, printSTNow()); } else if (mFrequence[fromQQ] > 120 && mWarnLevel[fromQQ] < 120 && QQ) { @@ -97,7 +97,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 36 ? "/5min" : (mCntOrder[fromQQ] > 15 ? "/min" : "/30s")); - if (!console.is_self(QQ))AddMsgToQueue(getMsg("strSpamFinalWarning"), CT); + if (QQ!=console.DiceMaid)AddMsgToQueue(getMsg("strSpamFinalWarning"), CT); console.log(strMsg, 0b10, printSTNow()); } else if (mFrequence[fromQQ] > 200 && mWarnLevel[fromQQ] < 200) { @@ -112,7 +112,7 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT } std::string strNote = (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + strFrq; - if (console.is_self(QQ)) { + if (QQ==console.DiceMaid) { console.set("ListenSelfEcho", 0); console.set("ListenGroupEcho", 0); console.log(strNote + "\nǿֹͣջ", 0b1000, strNow); From e3ec810dd06c72492a84ec66827de5e3bf1b48c1 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 9 Apr 2021 13:40:47 +0800 Subject: [PATCH 144/171] update DiceLua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增函数sendMsg --- Dice/DiceEvent.cpp | 2 +- Dice/DiceLua.cpp | 64 +++++++++++++++++++++++++++++----------------- Dice/DiceMod.cpp | 2 +- Dice/GlobalVar.cpp | 1 + 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 4a8d2720..8eeef471 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -2179,7 +2179,7 @@ int FromMsg::InnerOrder() { return 1; } long long llTarget = stoll(strTarget); - if (trustedQQ(llTarget) >= trusted && fromQQ != console.master()) { + if (trustedQQ(llTarget) >= trusted && !console.is_self(fromQQ) && fromQQ != llTarget) { reply(GlobalMsg["strUserTrustDenied"]); return 1; } diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 7271d92e..4e5c3785 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -26,11 +26,19 @@ class LuaState { } void regist(); }; - +string lua_to_string(lua_State* L, int idx) { +#ifdef _WIN32 + return UTF8toGBK(lua_tostring(L, idx), true); +#else + return lua_tostring(L, -1); +#endif +} void lua_push_string(lua_State* L, const string& str) { +#ifdef _WIN32 if (UTF8Luas.count(L)) lua_pushstring(L, GBKtoUTF8(str).c_str()); else +#endif // _WIN32 lua_pushstring(L, str.c_str()); } void lua_set_field(lua_State* L, int idx, const string& str) { @@ -83,7 +91,7 @@ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { msg->reply(GlobalMsg["strOrderLuaErr"]); return false; } - if (!(msg->strVar["msg_reply"] = UTF8toGBK(lua_tostring(L, 1), true)).empty()) { + if (!(msg->strVar["msg_reply"] = lua_to_string(L, 1)).empty()) { msg->reply(msg->strVar["msg_reply"]); } if (lua_type(L, 2) != LUA_TNIL) { @@ -91,7 +99,7 @@ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ֵʽ!", 1); return false; } - if (!(msg->strVar["msg_hidden"] = UTF8toGBK(lua_tostring(L, 2), true)).empty()) { + if (!(msg->strVar["msg_hidden"] = lua_to_string(L, 2)).empty()) { msg->replyHidden(msg->strVar["msg_hidden"]); } } @@ -116,11 +124,7 @@ bool lua_call_task(const char* file, const char* func) { //luaű int loadLua(lua_State* L) { -#ifdef _WIN32 - string nameFile{ UTF8toGBK(lua_tostring(L, -1),true) }; -#else - string nameFile{ lua_tostring(L, -1) }; -#endif + string nameFile{ lua_to_string(L, -1) }; std::filesystem::path pathFile = DiceDir / "plugin" / (nameFile + ".lua"); if (!std::filesystem::exists(pathFile) && nameFile.find('/') == string::npos && nameFile.find('\\') == string::npos) pathFile = DiceDir / "plugin" / nameFile / "init.lua"; @@ -147,15 +151,18 @@ int getDiceDir(lua_State* L) { return 1; } int mkDirs(lua_State* L) { - string dir{ UTF8toGBK(lua_tostring(L, -1),true) }; + string dir{ lua_to_string(L, -1) }; mkDir(dir); return 0; } int getUserConf(lua_State* L) { long long qq{ (long long)lua_tonumber(L, 1) }; - string item{ UTF8toGBK(lua_tostring(L, 2),true) }; + string item{ lua_to_string(L, 2) }; User& user{ getUser(qq) }; - if (user.intConf.count(item)) { + if (item == "trust") { + lua_pushnumber(L, trustedQQ(qq)); + } + else if (user.intConf.count(item)) { lua_pushnumber(L, (double)user.intConf[item]); } else if (user.strConf.count(item)) { @@ -169,18 +176,18 @@ int getUserConf(lua_State* L) { } int setUserConf(lua_State* L) { long long qq{ (long long)lua_tonumber(L, -3) }; - string item{ UTF8toGBK(lua_tostring(L, -2),true) }; + string item{ lua_to_string(L, -2) }; if (lua_isnumber(L, -1)) { getUser(qq).setConf(item, (int)lua_tonumber(L, -1)); } else if (lua_isstring(L, -1)) { - getUser(qq).setConf(item, UTF8toGBK(lua_tostring(L, -1), true)); + getUser(qq).setConf(item, lua_to_string(L, -1)); } return 0; } int getUserToday(lua_State* L) { long long qq{ (long long)lua_tonumber(L, 1) }; - string item{ UTF8toGBK(lua_tostring(L, 2),true) }; + string item{ lua_to_string(L, 2) }; if (item == "jrrp") lua_pushnumber(L, today->getJrrp(qq)); else @@ -189,7 +196,7 @@ int getUserToday(lua_State* L) { } int setUserToday(lua_State* L) { long long qq{ (long long)lua_tonumber(L, -3) }; - string item{ UTF8toGBK(lua_tostring(L, -2),true) }; + string item{ lua_to_string(L, -2) }; int val{ (int)lua_tonumber(L, -1) }; today->set(qq, item, val); return 0; @@ -198,7 +205,7 @@ int setUserToday(lua_State* L) { int getPlayerCardAttr(lua_State* L) { long long plQQ{ (long long)lua_tonumber(L, 1) }; long long group{ (long long)lua_tonumber(L, 2) }; - string key{ UTF8toGBK(lua_tostring(L, 3),true) }; + string key{ lua_to_string(L, 3) }; CharaCard& pc = getPlayer(plQQ)[group]; if (pc.Info.count(key)) { lua_push_string(L, pc.Info.find(key)->second); @@ -232,15 +239,15 @@ int getPlayerCard(lua_State* L) { int setPlayerCardAttr(lua_State* L) { long long plQQ{ (long long)lua_tonumber(L, 1) }; long long group{ (long long)lua_tonumber(L, 2) }; - string item{ UTF8toGBK(lua_tostring(L, 3),true) }; + string item{ lua_to_string(L, 3) }; CharaCard& pc = getPlayer(plQQ)[group]; if (lua_isnumber(L, -1)) { pc.set(item, (int)lua_tonumber(L, -1)); } else if (lua_isstring(L, -1)) { if (item.empty())return 0; - else if (item[0] == '&')pc.setExp(item.substr(1), UTF8toGBK(lua_tostring(L, -1), true)); - else pc.setInfo(item, UTF8toGBK(lua_tostring(L, -1), true)); + else if (item[0] == '&')pc.setExp(item.substr(1), lua_to_string(L, -1)); + else pc.setInfo(item, lua_to_string(L, -1)); } return 0; } @@ -262,7 +269,7 @@ int sleepTime(lua_State* L) { int drawDeck(lua_State* L) { long long fromGroup{ (long long)lua_tonumber(L, -3) }; long long fromQQ{ (long long)lua_tonumber(L, -2) }; - string nameDeck{ UTF8toGBK(lua_tostring(L, -1),true) }; + string nameDeck{ lua_to_string(L, -1) }; long long fromSession{ fromGroup ? fromGroup : ~fromQQ }; if (gm->has_session(fromSession)) { lua_push_string(L, gm->session(fromSession).deck_draw(nameDeck)); @@ -277,8 +284,18 @@ int drawDeck(lua_State* L) { return 1; } +int sendMsg(lua_State* L) { + string fromMsg{ lua_to_string(L, -3) }; + long long fromGroup{ (long long)lua_tonumber(L, -2) }; + long long fromQQ{ (long long)lua_tonumber(L, -1) }; + msgtype type{ fromGroup ? + chat(fromGroup).isGroup ? msgtype::Group : msgtype::Discuss + : msgtype::Private }; + AddMsgToQueue(fromMsg, fromGroup ? fromGroup : fromQQ, type); + return 0; +} int eventMsg(lua_State* L) { - string fromMsg{ UTF8toGBK(lua_tostring(L, -3),true) }; + string fromMsg{ lua_to_string(L, -3) }; long long fromGroup{ (long long)lua_tonumber(L, -2) }; long long fromQQ{ (long long)lua_tonumber(L, -1) }; msgtype type{ fromGroup ? @@ -307,6 +324,7 @@ void LuaState::regist() { {"ranint", ranint}, {"sleepTime", sleepTime}, {"drawDeck", drawDeck}, + {"sendMsg", sendMsg}, {"eventMsg", eventMsg}, {nullptr, nullptr}, }; @@ -363,8 +381,8 @@ int lua_readStringTable(const char* file, const char* var, std::unordered_map 0) { for (auto& [key, func] : mOrder) { - msgorder[key] = { fileLua,func }; + msgorder[::format(key, GlobalMsg)] = { fileLua,func }; } cntOrder += mOrder.size(); } diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 4a7c8be6..6c8b520d 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -365,6 +365,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +576:ʱű 575:Ӧ 574:ĬŻ 573:ɫŻ From bbe1616bfeba2a26421999f69245bcfbd9d9cb7f Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 14 Apr 2021 16:01:41 +0800 Subject: [PATCH 145/171] fix log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 调节转发判定的时机 --- Dice/DiceEvent.cpp | 2 +- Dice/DiceLua.cpp | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 8eeef471..e195f0aa 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -3798,13 +3798,13 @@ bool FromMsg::DiceFilter() while (isspace(static_cast(strMsg[0]))) strMsg.erase(strMsg.begin()); } - if (isOtherCalled && !isCalled)return false; init2(strMsg); strLowerMessage = strMsg; std::transform(strLowerMessage.begin(), strLowerMessage.end(), strLowerMessage.begin(), [](unsigned char c) { return tolower(c); }); trusted = trustedQQ(fromQQ); fwdMsg(); + if (isOtherCalled && !isCalled)return false; if (fromChat.second == msgtype::Private) isCalled = true; isDisabled = ((console["DisabledGlobal"] && trusted < 4) || groupset(fromGroup, "ЭЧ") > 0); if (BasicOrder()) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 4e5c3785..80524f12 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -10,6 +10,7 @@ extern "C"{ #include "CharacterCard.h" #include "CardDeck.h" #include "DiceSession.h" +#include "DDAPI.h" unordered_set UTF8Luas; @@ -27,10 +28,12 @@ class LuaState { void regist(); }; string lua_to_string(lua_State* L, int idx) { + const char* str{ lua_tostring(L, idx) }; + if (!str)return {}; #ifdef _WIN32 - return UTF8toGBK(lua_tostring(L, idx), true); + return UTF8toGBK(str, true); #else - return lua_tostring(L, -1); + return str; #endif } void lua_push_string(lua_State* L, const string& str) { @@ -125,8 +128,11 @@ bool lua_call_task(const char* file, const char* func) { //luaű int loadLua(lua_State* L) { string nameFile{ lua_to_string(L, -1) }; + while (nameFile.find('/') != string::npos) { + nameFile[nameFile.find('/')] = '\\'; + } std::filesystem::path pathFile = DiceDir / "plugin" / (nameFile + ".lua"); - if (!std::filesystem::exists(pathFile) && nameFile.find('/') == string::npos && nameFile.find('\\') == string::npos) + if (!std::filesystem::exists(pathFile) && nameFile.find('\\') == string::npos) pathFile = DiceDir / "plugin" / nameFile / "init.lua"; if (luaL_loadfile(L, pathFile.string().c_str())) { const char* pErrorMsg = lua_tostring(L, -1); @@ -159,14 +165,17 @@ int getUserConf(lua_State* L) { long long qq{ (long long)lua_tonumber(L, 1) }; string item{ lua_to_string(L, 2) }; User& user{ getUser(qq) }; - if (item == "trust") { + if (string val; item == "nick" && user.getNick(val)) { + lua_push_string(L, val); + } + else if (item == "trust") { lua_pushnumber(L, trustedQQ(qq)); } else if (user.intConf.count(item)) { lua_pushnumber(L, (double)user.intConf[item]); } else if (user.strConf.count(item)) { - lua_push_string(L, user.strConf[item].c_str()); + lua_push_string(L, user.strConf[item]); } else { lua_pushnil(L); From c1684de9d2e42587066ad7dd0a7acbf16f918e10 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 16 Apr 2021 10:32:20 +0800 Subject: [PATCH 146/171] fix lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 取number时增加前置判断 --- Dice/DiceLua.cpp | 73 +++++++++++++++++++++++++------------------- Dice/GlobalVar.cpp | 5 +-- Dice/GlobalVar.h | 2 +- Dice/ManagerSystem.h | 1 + 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 80524f12..78b83c69 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -3,6 +3,7 @@ extern "C"{ #include #include #include +#include }; #include "ManagerSystem.h" #include "DiceEvent.h" @@ -27,6 +28,12 @@ class LuaState { } void regist(); }; +double lua_to_number(lua_State* L, int idx) { + return luaL_checknumber(L, idx); +} +long long lua_to_int(lua_State* L, int idx) { + return luaL_checkinteger(L, idx); +} string lua_to_string(lua_State* L, int idx) { const char* str{ lua_tostring(L, idx) }; if (!str)return {}; @@ -162,11 +169,12 @@ int mkDirs(lua_State* L) { return 0; } int getUserConf(lua_State* L) { - long long qq{ (long long)lua_tonumber(L, 1) }; + long long qq{ lua_to_int(L, 1) }; + if (!qq)return 0; string item{ lua_to_string(L, 2) }; User& user{ getUser(qq) }; - if (string val; item == "nick" && user.getNick(val)) { - lua_push_string(L, val); + if (item == "nick" ) { + lua_push_string(L, getName(qq)); } else if (item == "trust") { lua_pushnumber(L, trustedQQ(qq)); @@ -184,18 +192,20 @@ int getUserConf(lua_State* L) { return 1; } int setUserConf(lua_State* L) { - long long qq{ (long long)lua_tonumber(L, -3) }; - string item{ lua_to_string(L, -2) }; - if (lua_isnumber(L, -1)) { - getUser(qq).setConf(item, (int)lua_tonumber(L, -1)); + long long qq{ lua_to_int(L, 1) }; + if (!qq)return 0; + string item{ lua_to_string(L, 2) }; + if (lua_isnumber(L, 3)) { + getUser(qq).setConf(item, (int)lua_tonumber(L, 3)); } - else if (lua_isstring(L, -1)) { - getUser(qq).setConf(item, lua_to_string(L, -1)); + else if (lua_isstring(L, 3)) { + getUser(qq).setConf(item, lua_to_string(L, 3)); } return 0; } int getUserToday(lua_State* L) { - long long qq{ (long long)lua_tonumber(L, 1) }; + long long qq{ lua_to_int(L, 1) }; + if (!qq)return 0; string item{ lua_to_string(L, 2) }; if (item == "jrrp") lua_pushnumber(L, today->getJrrp(qq)); @@ -204,16 +214,17 @@ int getUserToday(lua_State* L) { return 1; } int setUserToday(lua_State* L) { - long long qq{ (long long)lua_tonumber(L, -3) }; - string item{ lua_to_string(L, -2) }; - int val{ (int)lua_tonumber(L, -1) }; + long long qq{ lua_to_int(L, 1) }; + if (!qq)return 0; + string item{ lua_to_string(L, 2) }; + int val{ (int)lua_to_number(L, 3) }; today->set(qq, item, val); return 0; } int getPlayerCardAttr(lua_State* L) { - long long plQQ{ (long long)lua_tonumber(L, 1) }; - long long group{ (long long)lua_tonumber(L, 2) }; + long long plQQ{ lua_to_int(L, 1) }; + long long group{ lua_to_int(L, 2) }; string key{ lua_to_string(L, 3) }; CharaCard& pc = getPlayer(plQQ)[group]; if (pc.Info.count(key)) { @@ -237,8 +248,8 @@ int getPlayerCardAttr(lua_State* L) { return 1; } int getPlayerCard(lua_State* L) { - long long plQQ{ (long long)lua_tonumber(L, 1) }; - long long group{ (long long)lua_tonumber(L, 2) }; + long long plQQ{ lua_to_int(L, 1) }; + long long group{ lua_to_int(L, 2) }; if (PList.count(plQQ)) { getPlayer(plQQ)[group].pushTable(L); return 1; @@ -246,8 +257,8 @@ int getPlayerCard(lua_State* L) { return 0; } int setPlayerCardAttr(lua_State* L) { - long long plQQ{ (long long)lua_tonumber(L, 1) }; - long long group{ (long long)lua_tonumber(L, 2) }; + long long plQQ{ lua_to_int(L, 1) }; + long long group{ lua_to_int(L, 2) }; string item{ lua_to_string(L, 3) }; CharaCard& pc = getPlayer(plQQ)[group]; if (lua_isnumber(L, -1)) { @@ -263,22 +274,22 @@ int setPlayerCardAttr(lua_State* L) { //ȡ int ranint(lua_State* L) { - int l{ (int)lua_tonumber(L, -2) }; - int r{ (int)lua_tonumber(L, -1) }; + int l{ (int)lua_to_int(L, 1) }; + int r{ (int)lua_to_int(L, 2) }; lua_pushnumber(L, RandomGenerator::Randint(l,r)); return 1; } //̵߳ȴ int sleepTime(lua_State* L) { - int ms{ (int)lua_tonumber(L, -1) }; + int ms{ (int)lua_to_int(L, 1) }; std::this_thread::sleep_for(std::chrono::milliseconds(ms)); return 0; } int drawDeck(lua_State* L) { - long long fromGroup{ (long long)lua_tonumber(L, -3) }; - long long fromQQ{ (long long)lua_tonumber(L, -2) }; - string nameDeck{ lua_to_string(L, -1) }; + long long fromGroup{ lua_to_int(L, 1) }; + long long fromQQ{ lua_to_int(L, 2) }; + string nameDeck{ lua_to_string(L, 3) }; long long fromSession{ fromGroup ? fromGroup : ~fromQQ }; if (gm->has_session(fromSession)) { lua_push_string(L, gm->session(fromSession).deck_draw(nameDeck)); @@ -294,9 +305,9 @@ int drawDeck(lua_State* L) { } int sendMsg(lua_State* L) { - string fromMsg{ lua_to_string(L, -3) }; - long long fromGroup{ (long long)lua_tonumber(L, -2) }; - long long fromQQ{ (long long)lua_tonumber(L, -1) }; + string fromMsg{ lua_to_string(L, 1) }; + long long fromGroup{ lua_to_int(L, 2) }; + long long fromQQ{ lua_to_int(L, 3) }; msgtype type{ fromGroup ? chat(fromGroup).isGroup ? msgtype::Group : msgtype::Discuss : msgtype::Private }; @@ -304,9 +315,9 @@ int sendMsg(lua_State* L) { return 0; } int eventMsg(lua_State* L) { - string fromMsg{ lua_to_string(L, -3) }; - long long fromGroup{ (long long)lua_tonumber(L, -2) }; - long long fromQQ{ (long long)lua_tonumber(L, -1) }; + string fromMsg{ lua_to_string(L, 1) }; + long long fromGroup{ lua_to_int(L, 2) }; + long long fromQQ{ lua_to_int(L, 3) }; msgtype type{ fromGroup ? chat(fromGroup).isGroup ? msgtype::Group : msgtype::Discuss : msgtype::Private }; diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 6c8b520d..950719dc 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -7,7 +7,8 @@ * |_______/ |________| |________| |________| |__| * * Dice! QQ Dice Robot for TRPG - * Copyright (C) 2018-2019 w4123 + * Copyright (C) 2018-2021 w4123 + * Copyright (C) 2019-2021 String.Empty * * This program is free software: you can redistribute it and/or modify it under the terms * of the GNU Affero General Public License as published by the Free Software Foundation, @@ -365,6 +366,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +577:ڹ㲥֪ͨ 576:ʱű 575:Ӧ 574:ĬŻ @@ -381,7 +383,6 @@ const std::map HelpDoc = { 563:Żָ 562:GUI 559:Զ̸²/¼ -557:ʱҵϵͳ 554:ɫ 551:ļȡƶ 550:ּ춨 diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 522dfd3b..f28cc423 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -39,7 +39,7 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 576u; +const unsigned short Dice_Build = 577u; inline const std::string Dice_Ver_Without_Build = "2.5.1"; constexpr auto DiceRequestHeader = "Dice/2.5.1"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index d027db14..eba7eba1 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -101,6 +101,7 @@ class User void setConf(const string& key, int val) { + if (key.empty())return; std::lock_guard lock_queue(ex_user); intConf[key] = val; } From 5d5e2f47423c5f733d811868dce199c6cf6cdc23 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sun, 18 Apr 2021 09:43:56 +0800 Subject: [PATCH 147/171] update checkUTF8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将编码检查提到脚本文件运行前 --- Dice/DiceLua.cpp | 8 +++----- Dice/EncodingConvert.cpp | 36 +++++++++++++++++++++++++++++++++++- Dice/EncodingConvert.h | 2 ++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 78b83c69..b33f0efa 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -371,6 +371,9 @@ LuaState::LuaState(const char* file) {//:isValid(false) { } luaL_openlibs(state); regist(); + ifstream fs(file); + if (checkUTF8(fs))UTF8Luas.insert(state); + fs.close(); if (lua_pcall(state, 0, 0, 0)) { const char* pErrorMsg = lua_tostring(state, -1); console.log(GlobalMsg["strSelfName"] + "luaļ" + file + "ʧ:" + pErrorMsg, 0b10); @@ -378,11 +381,6 @@ LuaState::LuaState(const char* file) {//:isValid(false) { state = nullptr; return; } - ifstream fs(file); - stringstream ss; - ss << fs.rdbuf(); - string strCheck = ss.str(); - if (checkUTF8(strCheck))UTF8Luas.insert(state); } int lua_readStringTable(const char* file, const char* var, std::unordered_map& tab) { diff --git a/Dice/EncodingConvert.cpp b/Dice/EncodingConvert.cpp index b645dde8..13e387e7 100644 --- a/Dice/EncodingConvert.cpp +++ b/Dice/EncodingConvert.cpp @@ -72,7 +72,41 @@ bool checkUTF8(const std::string& strUTF8) { } return cntUTF8; } - +bool checkUTF8(std::ifstream& fin) { + size_t cntUTF8(0); + int num = 0; + char ch{0}; + while (fin >> ch) { + if ((ch & 0x80) == 0x00) { + continue; + } + else if ((ch & 0xc0) == 0xc0 && (ch & 0xfe) != 0xfe) { + // 110X_XXXX 10XX_XXXX + // 1110_XXXX 10XX_XXXX 10XX_XXXX + // 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX + // 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX + // 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX + unsigned char mask = 0x80; + for (num = 0; num < 8; ++num) { + if ((ch & mask) == mask) { + mask = mask >> 1; + } + else + break; + } + for (int j = 0; j < num - 1; j++) { + if ((fin >> ch) && (ch & 0xc0) != 0x80) { + return false; + } + } + if (++cntUTF8 > 10)return true; + } + else { + return false; + } + } + return cntUTF8; +} // GBK std::string GBKtoUTF8(const std::string& strGBK, bool isTrial) { diff --git a/Dice/EncodingConvert.h b/Dice/EncodingConvert.h index 7b2a891a..126705d4 100644 --- a/Dice/EncodingConvert.h +++ b/Dice/EncodingConvert.h @@ -32,8 +32,10 @@ #include #endif #include +#include bool checkUTF8(const std::string& strUTF8); +bool checkUTF8(std::ifstream&); template && From 20ac2546e5d772bd478805101f7f9877615b6aec Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sun, 18 Apr 2021 14:59:54 +0800 Subject: [PATCH 148/171] update GroupConf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新群conf读写 --- Dice/DiceEvent.cpp | 66 ++++++++++++++++++++++++++++------------------ Dice/DiceLua.cpp | 41 ++++++++++++++++++++++++++++ Dice/DiceMod.cpp | 2 +- Dice/GlobalVar.cpp | 16 ++++++----- Dice/GlobalVar.h | 2 +- 5 files changed, 94 insertions(+), 33 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index e195f0aa..aa919df7 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1441,44 +1441,60 @@ int FromMsg::InnerOrder() { Chat& grp = chat(llGroup); while (isspace(static_cast(strLowerMessage[intMsgCnt]))) intMsgCnt++; - if (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-') { - bool isSet = strMsg[intMsgCnt] == '+'; - intMsgCnt++; - strVar["option"] = readRest(); - if (!mChatConf.count(strVar["option"])) { - reply(GlobalMsg["strGroupSetDenied"]); - return 1; - } - if (getGroupAuth(llGroup) >= get(mChatConf, strVar["option"], 0)) { - if (isSet) { - if (groupset(llGroup, strVar["option"]) < 1) { - chat(llGroup).set(strVar["option"]); - reply(GlobalMsg["strGroupSetOn"]); - if (strVar["Option"] == "ʹ") { - AddMsgToQueue(getMsg("strGroupAuthorized", strVar), fromQQ, msgtype::Group); + if(strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-'){ + int cntSet{ 0 }; + while (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-') { + bool isSet = strMsg[intMsgCnt] == '+'; + intMsgCnt++; + strVar["option"] = readPara(); + if (!mChatConf.count(strVar["option"])) { + reply(GlobalMsg["strGroupSetInvalid"]); + continue; + } + if (getGroupAuth(llGroup) >= get(mChatConf, strVar["option"], 0)) { + if (isSet) { + if (groupset(llGroup, strVar["option"]) < 1) { + chat(llGroup).set(strVar["option"]); + ++cntSet; + if (strVar["Option"] == "ʹ") { + AddMsgToQueue(getMsg("strGroupAuthorized", strVar), fromQQ, msgtype::Group); + } } + else { + reply(GlobalMsg["strGroupSetOnAlready"]); + } + } + else if (grp.isset(strVar["option"])) { + ++cntSet; + chat(llGroup).reset(strVar["option"]); + reply(GlobalMsg["strGroupSetOff"]); } else { - reply(GlobalMsg["strGroupSetOnAlready"]); + reply(GlobalMsg["strGroupSetOffAlready"]); } - return 1; - } - if (grp.isset(strVar["option"])) { - chat(llGroup).reset(strVar["option"]); - reply(GlobalMsg["strGroupSetOff"]); } else { - reply(GlobalMsg["strGroupSetOffAlready"]); + reply(GlobalMsg["strGroupSetDenied"]); } - return 1; + readSkipSpace(); + } + if (cntSet == 1) { + reply(GlobalMsg["strGroupSetOn"]); + } + else if(cntSet > 1) { + strVar["opt_list"] = grp.listBoolConf(); + reply(GlobalMsg["strGroupMultiSet"]); } - reply(GlobalMsg["strGroupSetDenied"]); return 1; } bool isInGroup{ fromGroup == llGroup || DD::isGroupMember(llGroup,console.DiceMaid,true) }; string Command = readPara(); strVar["group"] = DD::printGroupInfo(llGroup); - if (Command == "state") { + if (Command.empty()) { + reply(fmt->get_help("group")); + return 1; + } + else if (Command == "state") { ResList res; res << "{group}"; res << grp.listBoolConf(); diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index b33f0efa..abedbbe8 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -168,6 +168,45 @@ int mkDirs(lua_State* L) { mkDir(dir); return 0; } +int getGroupConf(lua_State* L) { + long long id{ lua_to_int(L, 1) }; + if (!id)return 0; + string item{ lua_to_string(L, 2) }; + Chat& grp{ chat(id) }; + if (item == "name") { + lua_push_string(L, grp.Name); + } + else if (mChatConf.count(item)) { + lua_pushboolean(L, grp.boolConf.count(item)); + } + else if (grp.intConf.count(item)) { + lua_pushnumber(L, (double)grp.intConf[item]); + } + else if (grp.strConf.count(item)) { + lua_push_string(L, grp.strConf[item]); + } + else { + lua_pushnil(L); + lua_insert(L, 3); + } + return 1; +} +int setGroupConf(lua_State* L) { + long long id{ lua_to_int(L, 1) }; + if (!id)return 0; + string item{ lua_to_string(L, 2) }; + Chat& grp{ chat(id) }; + if (mChatConf.count(item)) { + lua_toboolean(L, 3) ? grp.set(item) : grp.reset(item); + } + else if (lua_isnumber(L, 3)) { + grp.setConf(item, (int)lua_tonumber(L, 3)); + } + else if (lua_isstring(L, 3)) { + grp.setText(item, lua_to_string(L, 3)); + } + return 0; +} int getUserConf(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; @@ -334,6 +373,8 @@ void LuaState::regist() { {"getDiceQQ", getDiceQQ}, {"getDiceDir", getDiceDir}, {"mkDirs", mkDirs}, + {"getGroupConf", getGroupConf}, + {"setGroupConf", setGroupConf}, {"getUserConf", getUserConf}, {"setUserConf", setUserConf}, {"getUserToday", getUserToday}, diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 905e96eb..b8a23c95 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -173,7 +173,7 @@ bool DiceModManager::listen_order(DiceJobDetail* msg) { return true; } string DiceModManager::list_order() { - return "չָ:" + listKey(msgorder); + return msgorder.empty() ? "" : "չָ:" + listKey(msgorder); } bool DiceModManager::call_task(const string& task) { diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 950719dc..9511ddc1 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -77,9 +77,11 @@ std::map GlobalMsg {"strGroupSetOnAlready","{self}ڴȺ{option}"}, {"strGroupSetOff","ѹر{self}ڴȺġ{option}ѡ"}, {"strGroupSetOffAlready","{self}δڴȺ{option}"}, + {"strGroupMultiSet","{self}ѽȺѡ޸Ϊ:{opt_list}"}, {"strGroupSetAll","{self}޸ļ¼{cnt}Ⱥġ{option}ѡ"}, {"strGroupDenied","{nick}{self}ȨʴȺá"}, {"strGroupSetDenied","{nick}{self}{option}Ȩ޲"}, + {"strGroupSetInvalid","{nick}ЧȺ{option}"}, {"strGroupSetNotExist","{self}{option}ѡ"}, {"strGroupWholeUnban","{self}ѹرȫֽԡ"}, {"strGroupWholeBan","{self}ѿȫֽԡ"}, @@ -359,13 +361,14 @@ std::map GlobalMsg .help趨 ȷ趨 .help 鿴Դĵ ٷ̳: https://forum.kokona.tech/ -̳: https://kokona.tech)" +Dice!ڳƻ: https://afdian.net/@suhuiw4123)" } }; std::map EditedMsg; const std::map HelpDoc = { {"",R"( +578:ŻȺöд 577:ڹ㲥֪ͨ 576:ʱű 575:Ӧ @@ -380,7 +383,6 @@ const std::map HelpDoc = { 566:.helpѯ 565:.log־¼ 564:๦Żƶѵ -563:Żָ 562:GUI 559:Զ̸²/¼ 554:ɫ @@ -388,13 +390,13 @@ const std::map HelpDoc = { 550:ּ춨 549:ˢ)"}, {"Э","0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, -{"","Dice!̳: https://kokona.tech \n Dice!̳: https://forum.kokona.tech"}, -{"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ{}\n{}{}\nûȺ:{ûȺ}\nٷ(ˮ)Ⱥ: 882747577\n˽Ⱥ863062599 192499947\nȺ1029435374"}, +{"","Dice!̳: https://kokona.tech \nDice!̳: https://forum.kokona.tech \nDice!ڳƻ: https://afdian.net/@suhuiw4123"}, +{"趨","Master{master_QQ}\n룺Ҫʹü¼\nȺ룺ƣǺڼ\nʹã\nƳƣȺͲ\nԷƣĬȺȺ\nˢƣ\nΣ\nܣ{}\n{}{}\nûȺ:{ûȺ}\n˽Ⱥ863062599 192499947\nȺ1029435374"}, {"ûȺ","δá"}, {"",""}, {"","δá"}, {"","{list_dice_sister}"}, -{"","Copyright (C) 2018-2020 w4123\nCopyright (C) 2019-2020 String.Empty"}, +{"","Copyright (C) 2018-2021 w4123\nCopyright (C) 2019-2021 String.Empty"}, {"ָ",R"(atָָﵥӦat.bot off ָҪӲ.helpӦָ ȡϸϢ.help jrrp ָ: @@ -432,7 +434,9 @@ const std::map HelpDoc = { .jrrp Ʒ .welcome Ⱥӭ .me ˳ƶ -Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)"}, +Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)" +"\f" +R"({list_extern_order})"}, {"master",R"(ǰMaster:{master_QQ} MasterӵȨޣҿԵ)"}, {"log",R"(־¼ diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index f28cc423..15d8909f 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -39,7 +39,7 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 577u; +const unsigned short Dice_Build = 578u; inline const std::string Dice_Ver_Without_Build = "2.5.1"; constexpr auto DiceRequestHeader = "Dice/2.5.1"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; From acc9fed7a3753193114f9dcc0ff27c595a1f3095 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sun, 18 Apr 2021 15:41:35 +0800 Subject: [PATCH 149/171] update format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 字典统一为大小写不敏感,优化format函数 --- Dice/DiceLua.cpp | 3 ++- Dice/DiceMod.cpp | 10 +++++---- Dice/DiceMod.h | 2 +- Dice/GlobalVar.cpp | 6 +++--- Dice/GlobalVar.h | 4 ++-- Dice/MsgFormat.cpp | 49 +++++++++++++++++++++++++++++++++++++++++- Dice/MsgFormat.h | 53 +++------------------------------------------- 7 files changed, 65 insertions(+), 62 deletions(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index abedbbe8..985f5410 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -350,7 +350,8 @@ int sendMsg(lua_State* L) { msgtype type{ fromGroup ? chat(fromGroup).isGroup ? msgtype::Group : msgtype::Discuss : msgtype::Private }; - AddMsgToQueue(fromMsg, fromGroup ? fromGroup : fromQQ, type); + AddMsgToQueue(format(fromMsg, GlobalMsg), + fromGroup ? fromGroup : fromQQ, type); return 0; } int eventMsg(lua_State* L) { diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index b8a23c95..5c12c67f 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -54,7 +54,7 @@ DiceModManager::DiceModManager() : helpdoc(HelpDoc) { } -string DiceModManager::format(string s, const map& dict, const char* mod_name = "") const +string DiceModManager::format(string s, const map& dict, const char* mod_name) const { //ֱض if (s[0] == '&') @@ -77,9 +77,11 @@ string DiceModManager::format(string s, const map& dict continue; } string key = s.substr(l + 1, r - l - 1), val; - auto it = dict.find(key); - if (it != dict.end()) - { + if (key.find("help:") == 0) { + key = key.substr(5); + val = fmt->format(fmt->get_help(key), dict); + } + else if (auto it = dict.find(key); it != dict.end()){ val = format(it->second, dict, mod_name); } else if (auto func = strFuncs.find(key); func != strFuncs.end()) diff --git a/Dice/DiceMod.h b/Dice/DiceMod.h index c12fe886..e433fc71 100644 --- a/Dice/DiceMod.h +++ b/Dice/DiceMod.h @@ -100,7 +100,7 @@ class DiceModManager DiceModManager(); friend void loadData(); bool isIniting{ false }; - string format(string, const map&, const char*) const; + string format(string, const map&, const char* mod_name = "") const; unordered_mapcntHelp; [[nodiscard]] string get_help(const string&) const; void _help(const shared_ptr&); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 9511ddc1..2e753356 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -42,7 +42,7 @@ HMODULE hDllModule = nullptr; bool msgSendThreadRunning = false; -std::map GlobalMsg +std::map GlobalMsg { {"strParaEmpty","Ϊա"}, //͵ܻظ {"strParaIllegal","Ƿ"}, //͵ܻظ @@ -365,7 +365,7 @@ std::map GlobalMsg } }; -std::map EditedMsg; +std::map EditedMsg; const std::map HelpDoc = { {"",R"( 578:ŻȺöд @@ -436,7 +436,7 @@ const std::map HelpDoc = { .me ˳ƶ Ϊ˱δԤϵָУ뾡ڲ֮ʹÿո)" "\f" -R"({list_extern_order})"}, +R"({help:չָ})"}, {"master",R"(ǰMaster:{master_QQ} MasterӵȨޣҿԵ)"}, {"log",R"(־¼ diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 15d8909f..9d7b93f5 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -90,9 +90,9 @@ extern std::string Dice_Full_Ver_On; extern bool msgSendThreadRunning; // ظϢ, ݿͨCustomMsg޸Ķ޸Դ -extern std::map GlobalMsg; +extern std::map GlobalMsg; // ޸ĺGlobal -extern std::map EditedMsg; +extern std::map EditedMsg; // ĵ extern const std::map HelpDoc; // ޸ĺİĵ diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 32c4cb27..98764258 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -24,6 +24,7 @@ #include #include // #include "DiceJob.h" +#include "DiceMod.h" #include "EncodingConvert.h" #include "StrExtern.hpp" #include "CardDeck.h" @@ -62,7 +63,53 @@ std::string format(std::string str, const std::initializer_list& replace_str, + const std::unordered_map& str_tmp) { + if (s[0] == '&') { + string key = s.substr(1); + auto it = replace_str.find(key); + if (it != replace_str.end()) { + return format(it->second, replace_str, str_tmp); + } + if (auto uit = str_tmp.find(key); uit != str_tmp.end()) { + return uit->second; + } + } + int l = 0, r = 0; + while ((l = s.find('{', r)) != string::npos && (r = s.find('}', l)) != string::npos) { + //ǰӡ\ʾݲת + if (l - 1 >= 0 && s[l - 1] == 0x5c) { + s.replace(l - 1, 1, ""); + continue; + } + string key = s.substr(l + 1, r - l - 1); + string val; + if (key.find("help:") == 0) { + key = key.substr(5); + val = fmt->format(fmt->get_help(key), replace_str); + } + else if (auto it = replace_str.find(key); it != replace_str.end()) { + val = format(it->second, replace_str, str_tmp); + } + else if ((it = GlobalChar.find(key)) != GlobalChar.end()) { + val = it->second; + } + else if ((it = GlobalChar.find(key)) != GlobalChar.end()) { + val = it->second; + } + else if (auto uit = str_tmp.find(key); uit != str_tmp.end()) { + if (key == "res")val = format(uit->second, replace_str, str_tmp); + else val = uit->second; + } + else if (auto func = strFuncs.find(key); func != strFuncs.end()) { + val = func->second(); + } + else continue; + s.replace(l, r - l + 1, val); + r = l + val.length(); + } + return s; +} string strip(std::string origin) { while (true) diff --git a/Dice/MsgFormat.h b/Dice/MsgFormat.h index 43d6d5ab..93c309c5 100644 --- a/Dice/MsgFormat.h +++ b/Dice/MsgFormat.h @@ -30,6 +30,7 @@ #include #include #include +#include "STLExtern.hpp" using std::string; extern std::map GlobalChar; @@ -38,56 +39,8 @@ extern std::map strFuncs; std::string format(std::string str, const std::initializer_list& replace_str); -template -std::string format(std::string s, const std::map& replace_str, - const std::unordered_map& str_tmp = {}) -{ - if (s[0] == '&') - { - string key = s.substr(1); - auto it = replace_str.find(key); - if (it != replace_str.end()) - { - return format(it->second, replace_str, str_tmp); - } - if (auto uit = str_tmp.find(key); uit != str_tmp.end()) - { - return uit->second; - } - } - int l = 0, r = 0; - while ((l = s.find('{', r)) != string::npos && (r = s.find('}', l)) != string::npos) { - //ǰӡ\ʾݲת - if (l - 1 >= 0 && s[l - 1] == 0x5c) { - s.replace(l - 1, 1, ""); - continue; - } - string key = s.substr(l + 1, r - l - 1); - string val; - auto it = replace_str.find(key); - if (it != replace_str.end()) - { - val = format(it->second, replace_str, str_tmp); - } - else if ((it = GlobalChar.find(key)) != GlobalChar.end()) - { - val = it->second; - } - else if (auto uit = str_tmp.find(key); uit != str_tmp.end()) - { - if (key == "res")val = format(uit->second, replace_str, str_tmp); - else val = uit->second; - } - else if (auto func = strFuncs.find(key); func != strFuncs.end()) - { - val = func->second(); - } - else continue; - s.replace(l, r - l + 1, val); - r = l + val.length(); - } - return s; -} +std::string format(std::string s, const std::map& replace_str, + const std::unordered_map& str_tmp = {}); class ResList { From 222f5387ac60fc3b1c6cc8146d672c9f1de5837b Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Sun, 18 Apr 2021 20:22:30 +0800 Subject: [PATCH 150/171] fix log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复log记录格式 --- Dice/DiceEvent.cpp | 4 ++-- Dice/DiceJob.cpp | 4 ++-- Dice/DiceJob.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index aa919df7..dc70da74 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -103,7 +103,7 @@ void FromMsg::fwdMsg() filter_CQcode(msg, fromGroup); ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); if (!strVar.count("pc") || strVar["pc"].empty())getPCName(*this); - logout << GBKtoUTF8(strVar["pc"]) + " " + printTTime(fromTime) << endl + logout << GBKtoUTF8(strVar["pc"]) + "(" + to_string(fromQQ) + ") " + printTTime(fromTime) << endl << GBKtoUTF8(msg) << endl << endl; } } @@ -1447,6 +1447,7 @@ int FromMsg::InnerOrder() { bool isSet = strMsg[intMsgCnt] == '+'; intMsgCnt++; strVar["option"] = readPara(); + readSkipSpace(); if (!mChatConf.count(strVar["option"])) { reply(GlobalMsg["strGroupSetInvalid"]); continue; @@ -1476,7 +1477,6 @@ int FromMsg::InnerOrder() { else { reply(GlobalMsg["strGroupSetDenied"]); } - readSkipSpace(); } if (cntSet == 1) { reply(GlobalMsg["strGroupSetOn"]); diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 021d95f4..29a0f709 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -22,7 +22,7 @@ using namespace std; -int sendSelf(const string msg) { +int sendSelf(const string& msg) { static long long selfQQ = DD::getLoginQQ(); DD::sendPrivateMsg(selfQQ, msg); return 0; @@ -205,7 +205,7 @@ void clear_group(DiceJob& job) { this_thread::sleep_for(2s); } } - console.log(GlobalMsg["strSelfName"] + "ɸDZˮ" + strDayLim + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10, printSTNow()); + job.note(GlobalMsg["strSelfName"] + "ɸDZˮ" + strDayLim + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10); } else if (job.strVar["clear_mode"] == "black") { try { diff --git a/Dice/DiceJob.h b/Dice/DiceJob.h index 4d0409da..a645ae43 100644 --- a/Dice/DiceJob.h +++ b/Dice/DiceJob.h @@ -3,7 +3,7 @@ inline time_t tNow = time(NULL); -int sendSelf(const string msg); +int sendSelf(const string& msg); void cq_exit(DiceJob& job); void frame_restart(DiceJob& job); From e7f8da8da3b73f77f39b96cc68a92573f31d9476 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Mon, 19 Apr 2021 19:53:00 +0800 Subject: [PATCH 151/171] Fix encoding issues in Lua --- Dice/DiceLua.cpp | 118 +++++++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 39 deletions(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 985f5410..4344ef47 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -34,28 +34,37 @@ double lua_to_number(lua_State* L, int idx) { long long lua_to_int(lua_State* L, int idx) { return luaL_checkinteger(L, idx); } + +// Return a GB18030 string +string lua_to_gb18030_string(lua_State* L, int idx) { + const char* str{ luaL_checkstring(L, idx) }; + if (!str) return {}; + return UTF8toGBK(str, true); +} + +// Return a GB18030 string on Windows and a UTF-8 string on other platforms string lua_to_string(lua_State* L, int idx) { - const char* str{ lua_tostring(L, idx) }; - if (!str)return {}; + const char* str{ luaL_checkstring(L, idx) }; + if (!str) return {}; #ifdef _WIN32 return UTF8toGBK(str, true); #else - return str; + return GBKtoUTF8(str, true); #endif } + + void lua_push_string(lua_State* L, const string& str) { -#ifdef _WIN32 if (UTF8Luas.count(L)) - lua_pushstring(L, GBKtoUTF8(str).c_str()); + lua_pushstring(L, GBKtoUTF8(str, true).c_str()); else -#endif // _WIN32 - lua_pushstring(L, str.c_str()); + lua_pushstring(L, UTF8toGBK(str, true).c_str()); } void lua_set_field(lua_State* L, int idx, const string& str) { if (UTF8Luas.count(L)) - lua_setfield(L, idx, GBKtoUTF8(str).c_str()); + lua_setfield(L, idx, GBKtoUTF8(str, true).c_str()); else - lua_setfield(L, idx, str.c_str()); + lua_setfield(L, idx, UTF8toGBK(str, true).c_str()); } void lua_push_msg(lua_State* L, FromMsg* msg) { @@ -83,33 +92,48 @@ void CharaCard::pushTable(lua_State* L) { void CharaCard::toCard(lua_State* L) { } -//ȡָluaļĺ +//ȡָluaļĺfileΪԭϵͳ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { +#ifndef _WIN32 + // תseparator + string fileStr(file); + for (auto& c : fileStr) + { + if (c == '\\') c = '/'; + } + file = fileStr.c_str(); +#endif LuaState L(file); if (!L)return false; +#ifndef _WIN32 + // תΪGB18030 + string fileGB18030(file); +#else + string fileGB18030(UTF8toGBK(file, true)); +#endif lua_getglobal(L, func); lua_push_msg(L, msg); if (lua_pcall(L, 1, 2, 0)) { - const char* pErrorMsg = lua_tostring(L, -1); - console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ʧ!\n" + pErrorMsg, 1); + string pErrorMsg = lua_to_gb18030_string(L, -1); + console.log(GlobalMsg["strSelfName"] + "" + fileGB18030 + "" + func + "ʧ!\n" + pErrorMsg, 1); msg->reply(GlobalMsg["strOrderLuaErr"]); return false; } if (lua_gettop(L) && lua_type(L, 1) != LUA_TNIL) { if (!lua_isstring(L, 1)) { - console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ֵʽ!", 1); + console.log(GlobalMsg["strSelfName"] + "" + fileGB18030 + "" + func + "ֵʽ!", 1); msg->reply(GlobalMsg["strOrderLuaErr"]); return false; } - if (!(msg->strVar["msg_reply"] = lua_to_string(L, 1)).empty()) { + if (!(msg->strVar["msg_reply"] = lua_to_gb18030_string(L, 1)).empty()) { msg->reply(msg->strVar["msg_reply"]); } if (lua_type(L, 2) != LUA_TNIL) { if (!lua_isstring(L, 2)) { - console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ֵʽ!", 1); + console.log(GlobalMsg["strSelfName"] + "" + fileGB18030 + "" + func + "ֵʽ!", 1); return false; } - if (!(msg->strVar["msg_hidden"] = lua_to_string(L, 2)).empty()) { + if (!(msg->strVar["msg_hidden"] = lua_to_gb18030_string(L, 2)).empty()) { msg->replyHidden(msg->strVar["msg_hidden"]); } } @@ -117,12 +141,27 @@ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { return true; } bool lua_call_task(const char* file, const char* func) { +#ifndef _WIN32 + // תseparator + string fileStr(file); + for (auto& c : fileStr) + { + if (c == '\\') c = '/'; + } + file = fileStr.c_str(); +#endif LuaState L(file); if (!L)return false; lua_getglobal(L, func); +#ifndef _WIN32 + // תΪGB18030 + string fileGB18030(file); +#else + string fileGB18030(UTF8toGBK(file, true)); +#endif if (lua_pcall(L, 0, 0, 0)) { - const char* pErrorMsg = lua_tostring(L, -1); - console.log(GlobalMsg["strSelfName"] + "" + file + "" + func + "ʧ!\n" + pErrorMsg, 1); + string pErrorMsg = lua_to_gb18030_string(L, -1); + console.log(GlobalMsg["strSelfName"] + "" + fileGB18030 + "" + func + "ʧ!\n" + pErrorMsg, 1); return false; } return true; @@ -135,6 +174,7 @@ bool lua_call_task(const char* file, const char* func) { //luaű int loadLua(lua_State* L) { string nameFile{ lua_to_string(L, -1) }; + // ??? while (nameFile.find('/') != string::npos) { nameFile[nameFile.find('/')] = '\\'; } @@ -142,12 +182,12 @@ int loadLua(lua_State* L) { if (!std::filesystem::exists(pathFile) && nameFile.find('\\') == string::npos) pathFile = DiceDir / "plugin" / nameFile / "init.lua"; if (luaL_loadfile(L, pathFile.string().c_str())) { - const char* pErrorMsg = lua_tostring(L, -1); + string pErrorMsg = lua_to_gb18030_string(L, -1); console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + UTF8toGBK(pathFile.u8string()) + "ʧ:"+ pErrorMsg, 0b10); return 0; } if (lua_pcall(L, 0, 1, 0)) { - const char* pErrorMsg = lua_tostring(L, -1); + string pErrorMsg = lua_to_gb18030_string(L, -1); console.log(GlobalMsg["strSelfName"] + "luaļ" + UTF8toGBK(pathFile.u8string()) + "ʧ:"+ pErrorMsg, 0b10); return 1; } @@ -171,7 +211,7 @@ int mkDirs(lua_State* L) { int getGroupConf(lua_State* L) { long long id{ lua_to_int(L, 1) }; if (!id)return 0; - string item{ lua_to_string(L, 2) }; + string item{ lua_to_gb18030_string(L, 2) }; Chat& grp{ chat(id) }; if (item == "name") { lua_push_string(L, grp.Name); @@ -194,7 +234,7 @@ int getGroupConf(lua_State* L) { int setGroupConf(lua_State* L) { long long id{ lua_to_int(L, 1) }; if (!id)return 0; - string item{ lua_to_string(L, 2) }; + string item{ lua_to_gb18030_string(L, 2) }; Chat& grp{ chat(id) }; if (mChatConf.count(item)) { lua_toboolean(L, 3) ? grp.set(item) : grp.reset(item); @@ -203,14 +243,14 @@ int setGroupConf(lua_State* L) { grp.setConf(item, (int)lua_tonumber(L, 3)); } else if (lua_isstring(L, 3)) { - grp.setText(item, lua_to_string(L, 3)); + grp.setText(item, lua_to_gb18030_string(L, 3)); } return 0; } int getUserConf(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; - string item{ lua_to_string(L, 2) }; + string item{ lua_to_gb18030_string(L, 2) }; User& user{ getUser(qq) }; if (item == "nick" ) { lua_push_string(L, getName(qq)); @@ -233,19 +273,19 @@ int getUserConf(lua_State* L) { int setUserConf(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; - string item{ lua_to_string(L, 2) }; + string item{ lua_to_gb18030_string(L, 2) }; if (lua_isnumber(L, 3)) { getUser(qq).setConf(item, (int)lua_tonumber(L, 3)); } else if (lua_isstring(L, 3)) { - getUser(qq).setConf(item, lua_to_string(L, 3)); + getUser(qq).setConf(item, lua_to_gb18030_string(L, 3)); } return 0; } int getUserToday(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; - string item{ lua_to_string(L, 2) }; + string item{ lua_to_gb18030_string(L, 2) }; if (item == "jrrp") lua_pushnumber(L, today->getJrrp(qq)); else @@ -255,7 +295,7 @@ int getUserToday(lua_State* L) { int setUserToday(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; - string item{ lua_to_string(L, 2) }; + string item{ lua_to_gb18030_string(L, 2) }; int val{ (int)lua_to_number(L, 3) }; today->set(qq, item, val); return 0; @@ -264,7 +304,7 @@ int setUserToday(lua_State* L) { int getPlayerCardAttr(lua_State* L) { long long plQQ{ lua_to_int(L, 1) }; long long group{ lua_to_int(L, 2) }; - string key{ lua_to_string(L, 3) }; + string key{ lua_to_gb18030_string(L, 3) }; CharaCard& pc = getPlayer(plQQ)[group]; if (pc.Info.count(key)) { lua_push_string(L, pc.Info.find(key)->second); @@ -298,15 +338,15 @@ int getPlayerCard(lua_State* L) { int setPlayerCardAttr(lua_State* L) { long long plQQ{ lua_to_int(L, 1) }; long long group{ lua_to_int(L, 2) }; - string item{ lua_to_string(L, 3) }; + string item{ lua_to_gb18030_string(L, 3) }; CharaCard& pc = getPlayer(plQQ)[group]; if (lua_isnumber(L, -1)) { pc.set(item, (int)lua_tonumber(L, -1)); } else if (lua_isstring(L, -1)) { if (item.empty())return 0; - else if (item[0] == '&')pc.setExp(item.substr(1), lua_to_string(L, -1)); - else pc.setInfo(item, lua_to_string(L, -1)); + else if (item[0] == '&')pc.setExp(item.substr(1), lua_to_gb18030_string(L, -1)); + else pc.setInfo(item, lua_to_gb18030_string(L, -1)); } return 0; } @@ -328,7 +368,7 @@ int sleepTime(lua_State* L) { int drawDeck(lua_State* L) { long long fromGroup{ lua_to_int(L, 1) }; long long fromQQ{ lua_to_int(L, 2) }; - string nameDeck{ lua_to_string(L, 3) }; + string nameDeck{ lua_to_gb18030_string(L, 3) }; long long fromSession{ fromGroup ? fromGroup : ~fromQQ }; if (gm->has_session(fromSession)) { lua_push_string(L, gm->session(fromSession).deck_draw(nameDeck)); @@ -344,7 +384,7 @@ int drawDeck(lua_State* L) { } int sendMsg(lua_State* L) { - string fromMsg{ lua_to_string(L, 1) }; + string fromMsg{ lua_to_gb18030_string(L, 1) }; long long fromGroup{ lua_to_int(L, 2) }; long long fromQQ{ lua_to_int(L, 3) }; msgtype type{ fromGroup ? @@ -355,7 +395,7 @@ int sendMsg(lua_State* L) { return 0; } int eventMsg(lua_State* L) { - string fromMsg{ lua_to_string(L, 1) }; + string fromMsg{ lua_to_gb18030_string(L, 1) }; long long fromGroup{ lua_to_int(L, 2) }; long long fromQQ{ lua_to_int(L, 3) }; msgtype type{ fromGroup ? @@ -405,7 +445,7 @@ LuaState::LuaState(const char* file) {//:isValid(false) { state = luaL_newstate(); if (!state)return; if (luaL_loadfile(state, file)) { - const char* pErrorMsg = lua_tostring(state, -1); + string pErrorMsg = lua_to_gb18030_string(state, -1); console.log(GlobalMsg["strSelfName"] + "ȡluaļ" + file + "ʧ:" + pErrorMsg, 0b10); lua_close(state); state = nullptr; @@ -417,7 +457,7 @@ LuaState::LuaState(const char* file) {//:isValid(false) { if (checkUTF8(fs))UTF8Luas.insert(state); fs.close(); if (lua_pcall(state, 0, 0, 0)) { - const char* pErrorMsg = lua_tostring(state, -1); + string pErrorMsg = lua_to_gb18030_string(state, -1); console.log(GlobalMsg["strSelfName"] + "luaļ" + file + "ʧ:" + pErrorMsg, 0b10); lua_close(state); state = nullptr; @@ -441,8 +481,8 @@ int lua_readStringTable(const char* file, const char* var, std::unordered_map Date: Mon, 19 Apr 2021 20:18:54 +0800 Subject: [PATCH 152/171] More Lua Encoding Fix --- Dice/DiceLua.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 4344ef47..7a4c66fb 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -105,7 +105,7 @@ bool lua_msg_order(FromMsg* msg, const char* file, const char* func) { #endif LuaState L(file); if (!L)return false; -#ifndef _WIN32 +#ifdef _WIN32 // תΪGB18030 string fileGB18030(file); #else @@ -153,7 +153,7 @@ bool lua_call_task(const char* file, const char* func) { LuaState L(file); if (!L)return false; lua_getglobal(L, func); -#ifndef _WIN32 +#ifdef _WIN32 // תΪGB18030 string fileGB18030(file); #else @@ -174,10 +174,6 @@ bool lua_call_task(const char* file, const char* func) { //luaű int loadLua(lua_State* L) { string nameFile{ lua_to_string(L, -1) }; - // ??? - while (nameFile.find('/') != string::npos) { - nameFile[nameFile.find('/')] = '\\'; - } std::filesystem::path pathFile = DiceDir / "plugin" / (nameFile + ".lua"); if (!std::filesystem::exists(pathFile) && nameFile.find('\\') == string::npos) pathFile = DiceDir / "plugin" / nameFile / "init.lua"; From 8f53fa998c30400e54e0629dee85f6ea8cdb44c0 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Mon, 19 Apr 2021 20:35:41 +0800 Subject: [PATCH 153/171] Lua Path Optimize --- Dice/DiceLua.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 7a4c66fb..773ce358 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -174,8 +174,15 @@ bool lua_call_task(const char* file, const char* func) { //luaű int loadLua(lua_State* L) { string nameFile{ lua_to_string(L, -1) }; +#ifndef _WIN32 + // תseparator + for (auto& c : nameFile) + { + if (c == '\\') c = '/'; + } +#endif std::filesystem::path pathFile = DiceDir / "plugin" / (nameFile + ".lua"); - if (!std::filesystem::exists(pathFile) && nameFile.find('\\') == string::npos) + if (!std::filesystem::exists(pathFile) && nameFile.find('\\') == string::npos && nameFile.find('/') == string::npos) pathFile = DiceDir / "plugin" / nameFile / "init.lua"; if (luaL_loadfile(L, pathFile.string().c_str())) { string pErrorMsg = lua_to_gb18030_string(L, -1); @@ -438,6 +445,15 @@ void LuaState::regist() { } LuaState::LuaState(const char* file) {//:isValid(false) { +#ifndef _WIN32 + // תseparator + string fileStr(file); + for (auto& c : fileStr) + { + if (c == '\\') c = '/'; + } + file = fileStr.c_str(); +#endif state = luaL_newstate(); if (!state)return; if (luaL_loadfile(state, file)) { @@ -462,6 +478,15 @@ LuaState::LuaState(const char* file) {//:isValid(false) { } int lua_readStringTable(const char* file, const char* var, std::unordered_map& tab) { +#ifndef _WIN32 + // תseparator + string fileStr(file); + for (auto& c : fileStr) + { + if (c == '\\') c = '/'; + } + file = fileStr.c_str(); +#endif LuaState L(file); if (!L)return -1; lua_getglobal(L, var); From 997b29c066ce925a2197efa956e07ea2260ae4b3 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Mon, 19 Apr 2021 20:41:29 +0800 Subject: [PATCH 154/171] Nickname fix --- Dice/DiceEvent.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index dc70da74..c30766f9 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -26,7 +26,7 @@ FromMsg& FromMsg::initVar(const std::initializer_list& replac void FromMsg::formatReply() { if (!strVar.count("nick") || strVar["nick"].empty())strVar["nick"] = getName(fromQQ, fromGroup); if (!strVar.count("pc") || strVar["pc"].empty())getPCName(*this); - if (!strVar.count("at") || strVar["nick"].empty())strVar["at"] = fromChat.second != msgtype::Private ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; + if (!strVar.count("at") || strVar["at"].empty())strVar["at"] = fromChat.second != msgtype::Private ? "[CQ:at,qq=" + to_string(fromQQ) + "]" : strVar["nick"]; strReply = format(strReply, GlobalMsg, strVar); } @@ -102,6 +102,7 @@ void FromMsg::fwdMsg() string msg = strMsg; filter_CQcode(msg, fromGroup); ofstream logout(gm->session(fromSession).log_path(), ios::out | ios::app); + if (!strVar.count("nick") || strVar["nick"].empty())strVar["nick"] = getName(fromQQ, fromGroup); if (!strVar.count("pc") || strVar["pc"].empty())getPCName(*this); logout << GBKtoUTF8(strVar["pc"]) + "(" + to_string(fromQQ) + ") " + printTTime(fromTime) << endl << GBKtoUTF8(msg) << endl << endl; @@ -2054,6 +2055,7 @@ int FromMsg::InnerOrder() { return 1; } } + strVar["nick"] = getName(fromQQ, fromGroup); strVar["res"] = to_string(today->getJrrp(fromQQ)); reply(GlobalMsg["strJrrp"], { strVar["nick"], strVar["res"] }); return 1; @@ -2349,6 +2351,7 @@ int FromMsg::InnerOrder() { intMsgCnt++; string type = readPara(); string strDeckName = (!type.empty() && CardDeck::mPublicDeck.count("_" + type)) ? "_" + type : ""; + strVar["nick"] = getName(fromQQ, fromGroup); strVar["new_nick"] = strip(CardDeck::drawCard(CardDeck::mPublicDeck[strDeckName], true)); getUser(fromQQ).setNick(fromGroup, strVar["new_nick"]); const string strReply = format(GlobalMsg["strNameSet"], { strVar["nick"], strVar["new_nick"] }); @@ -2383,6 +2386,7 @@ int FromMsg::InnerOrder() { getUser(fromQQ).rmIntConf("Ĭ"); else getUser(fromQQ).setConf("Ĭ", intDefaultDice); + strVar["nick"] = getName(fromQQ, fromGroup); reply("ѽ" + strVar["nick"] + "Ĭ͸ΪD" + strVar["default"]); return 1; } From ff41b0d90fc7364a6d4039ab26cd06eced7f3c5c Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Mon, 19 Apr 2021 20:47:49 +0800 Subject: [PATCH 155/171] Ignore file that starts with dot --- Dice/DiceFile.cpp | 2 +- Dice/DiceFile.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dice/DiceFile.cpp b/Dice/DiceFile.cpp index 2455f400..88ff0d2c 100644 --- a/Dice/DiceFile.cpp +++ b/Dice/DiceFile.cpp @@ -244,7 +244,7 @@ int _listDir(const std::filesystem::path& dir, vector& fi std::error_code err; for (const auto& file : T(dir, err)) { - if (file.is_regular_file()) + if (file.is_regular_file() && file.path().filename().string().substr(0, 1) != ".") { intFile++; files.push_back(file.path()); diff --git a/Dice/DiceFile.hpp b/Dice/DiceFile.hpp index 4212a772..9d3298e0 100644 --- a/Dice/DiceFile.hpp +++ b/Dice/DiceFile.hpp @@ -561,7 +561,7 @@ int _loadDir(int (*load)(const std::filesystem::path&, T2&), const std::filesyst std::error_code err; for (const auto& p : T1(fpDir, err)) { - if (p.is_regular_file()) + if (p.is_regular_file() && p.path().filename().string().substr(0, 1) != ".") { intFile++; const int Cnt = load(p, tmp); From f7c90da84d519fef3f89c96e8bc582363ad8b41d Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Mon, 19 Apr 2021 20:58:31 +0800 Subject: [PATCH 156/171] Fix pc names --- Dice/DiceEvent.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index c30766f9..14575be1 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1942,6 +1942,8 @@ int FromMsg::InnerOrder() { } strVar["res"] = Res.dot("|").show(); strVar["cnt"] = to_string(Res.size()); + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); initVar({ strVar["pc"], strVar["res"] }); if (isPrivate) { reply(GlobalMsg["strDrawHidden"]); @@ -2317,7 +2319,6 @@ int FromMsg::InnerOrder() { reply(GlobalMsg["strCharacterCannotBeZero"]); return 1; } - string strReply = strVar["pc"]; DND(strVar["res"], intNum); reply(GlobalMsg["strDNDBuild"]); return 1; @@ -2376,6 +2377,8 @@ int FromMsg::InnerOrder() { reply(GlobalMsg["strSetTooBig"]); return 1; } + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); const int intDefaultDice = stoi(strVar["default"]); if (PList.count(fromQQ)) { PList[fromQQ][fromGroup]["__DefaultDice"] = intDefaultDice; @@ -2386,7 +2389,7 @@ int FromMsg::InnerOrder() { getUser(fromQQ).rmIntConf("Ĭ"); else getUser(fromQQ).setConf("Ĭ", intDefaultDice); - strVar["nick"] = getName(fromQQ, fromGroup); + reply("ѽ" + strVar["nick"] + "Ĭ͸ΪD" + strVar["default"]); return 1; } @@ -2574,6 +2577,8 @@ int FromMsg::InnerOrder() { reply(GlobalMsg["strActionEmpty"]); return 1; } + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); trusted > 4 ? reply(strAction) : reply(strVar["pc"] + strAction); return 1; } @@ -3004,6 +3009,8 @@ int FromMsg::InnerOrder() { ? "/" + to_string( intSkillDivisor) : ""); + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); if (strVar["reason"].empty()) { strReply = format(GlobalMsg["strRollSkill"], { strVar["pc"], strVar["attr"] }); } @@ -3224,6 +3231,8 @@ int FromMsg::InnerOrder() { reply(fmt->get_help("st")); return 1; } + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); if (strLowerMessage.substr(intMsgCnt, 3) == "clr") { if (!PList.count(fromQQ)) { reply(getMsg("strPcNotExistErr")); @@ -3383,6 +3392,8 @@ int FromMsg::InnerOrder() { isHidden = true; intMsgCnt += 1; } + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); if (!fromGroup)isHidden = false; string strMainDice = readDice(); readSkipSpace(); @@ -3575,6 +3586,8 @@ int FromMsg::InnerOrder() { return 1; } else if (strLowerMessage[intMsgCnt] == 'r' || strLowerMessage[intMsgCnt] == 'h') { + strVar["nick"] = getName(fromQQ, fromGroup); + getPCName(*this); bool isHidden = false; if (strLowerMessage[intMsgCnt] == 'h') isHidden = true; From 46780864b060004e3855b305b09008b511b78090 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Tue, 20 Apr 2021 09:38:52 +0800 Subject: [PATCH 157/171] fix pc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化lua参数验证 优化rename判别 优化{pc}获取 --- Dice/CharacterCard.cpp | 28 ++++++++++++++++++++++++++-- Dice/CharacterCard.h | 13 ++----------- Dice/DiceEvent.cpp | 12 ++++-------- Dice/DiceLua.cpp | 33 +++++++++++++++++++++++++-------- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/Dice/CharacterCard.cpp b/Dice/CharacterCard.cpp index cd26a26b..f269f914 100644 --- a/Dice/CharacterCard.cpp +++ b/Dice/CharacterCard.cpp @@ -1,9 +1,21 @@ /* * │ - * Copyright (C) 2019-2020 String.Empty + * Copyright (C) 2019-2021 String.Empty */ #include "CharacterCard.h" +/** + * 󷵻ֵ + * -1:PcCardFull ɫ + * -2:PcTempInvalid ģЧã + * -3:PcNameEmpty Ϊ + * -4:PCNameExist Ѵ + * -5:PcNameNotExist + * -6:PCNameInvalid Ч + * -7:PcInitDelErr ɾʼ + */ + + string CardTemp::show() { ResList res; if (!mVariable.empty()) { @@ -187,11 +199,22 @@ void CharaCard::readb(std::ifstream& fin) { } pTemplet = &getCardTemplet(Info["__Type"]); } + Player& getPlayer(long long qq) { if (!PList.count(qq))PList[qq] = {}; return PList[qq]; } +int Player::renameCard(const string& name, const string& name_new) { + std::lock_guard lock_queue(cardMutex); + if (name_new.empty())return -3; + if (mNameIndex.count(name_new))return -4; + if (name_new.find(":") != string::npos)return -6; + const int i = mNameIndex[name_new] = mNameIndex[name]; + mNameIndex.erase(name); + mCardList[i].setName(name_new); + return 0; +} string Player::listCard() { ResList Res; for (auto& [idx, pc] : mCardList) { @@ -205,5 +228,6 @@ void getPCName(FromMsg& msg) { msg["pc"] = (PList.count(msg.fromQQ) && PList[msg.fromQQ][msg.fromGroup].getName() != "ɫ") ? PList[msg.fromQQ][msg.fromGroup].getName() - : msg["nick"]; + : ((!msg.strVar.count("nick") || msg["nick"].empty()) + ? msg["nick"] = getName(msg.fromQQ, msg.fromGroup) : msg["nick"]); } diff --git a/Dice/CharacterCard.h b/Dice/CharacterCard.h index 42385768..0c9fafdf 100644 --- a/Dice/CharacterCard.h +++ b/Dice/CharacterCard.h @@ -2,7 +2,7 @@ /* * │ - * Copyright (C) 2019 String.Empty + * Copyright (C) 2019-2021 String.Empty */ #include @@ -637,16 +637,7 @@ class Player return 0; } - int renameCard(const string& name, const string& name_new) - { - std::lock_guard lock_queue(cardMutex); - if (mNameIndex.count(name_new))return -4; - if (name_new.find(":") != string::npos)return -6; - const int i = mNameIndex[name_new] = mNameIndex[name]; - mNameIndex.erase(name); - mCardList[i].setName(name_new); - return 0; - } + int renameCard(const string& name, const string& name_new); int copyCard(const string& name1, const string& name2, long long group = 0) { diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 14575be1..5b4a7429 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -2713,9 +2713,6 @@ int FromMsg::InnerOrder() { case -1: reply(GlobalMsg["strPcCardFull"]); break; - case -2: - reply(GlobalMsg["strPcTempInvalid"]); - break; case -4: reply(GlobalMsg["strPcNameExist"]); break; @@ -2758,16 +2755,15 @@ int FromMsg::InnerOrder() { } if (strOption == "nn") { strVar["new_name"] = strip(readRest()); - filter_CQcode(strVar["char"]); - if (strVar["new_name"].empty()) { - reply(GlobalMsg["strPCNameEmpty"]); - return 1; - } + filter_CQcode(strVar["new_name"]); strVar["old_name"] = pl[fromGroup].getName(); switch (pl.renameCard(strVar["old_name"], strVar["new_name"])) { case 0: reply(GlobalMsg["strPcCardRename"]); break; + case -3: + reply(GlobalMsg["strPCNameEmpty"]); + break; case -4: reply(GlobalMsg["strPCNameExist"]); break; diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 773ce358..c688461f 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -173,7 +173,7 @@ bool lua_call_task(const char* file, const char* func) { //luaű int loadLua(lua_State* L) { - string nameFile{ lua_to_string(L, -1) }; + string nameFile{ lua_to_string(L, 1) }; #ifndef _WIN32 // תseparator for (auto& c : nameFile) @@ -207,14 +207,14 @@ int getDiceDir(lua_State* L) { return 1; } int mkDirs(lua_State* L) { - string dir{ lua_to_string(L, -1) }; + string dir{ lua_to_string(L, 1) }; mkDir(dir); return 0; } int getGroupConf(lua_State* L) { long long id{ lua_to_int(L, 1) }; - if (!id)return 0; string item{ lua_to_gb18030_string(L, 2) }; + if (!id || item.empty())return 0; Chat& grp{ chat(id) }; if (item == "name") { lua_push_string(L, grp.Name); @@ -236,8 +236,8 @@ int getGroupConf(lua_State* L) { } int setGroupConf(lua_State* L) { long long id{ lua_to_int(L, 1) }; - if (!id)return 0; string item{ lua_to_gb18030_string(L, 2) }; + if (!id || item.empty())return 0; Chat& grp{ chat(id) }; if (mChatConf.count(item)) { lua_toboolean(L, 3) ? grp.set(item) : grp.reset(item); @@ -254,6 +254,7 @@ int getUserConf(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; string item{ lua_to_gb18030_string(L, 2) }; + if (item.empty())return 0; User& user{ getUser(qq) }; if (item == "nick" ) { lua_push_string(L, getName(qq)); @@ -277,6 +278,7 @@ int setUserConf(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; string item{ lua_to_gb18030_string(L, 2) }; + if (item.empty())return 0; if (lua_isnumber(L, 3)) { getUser(qq).setConf(item, (int)lua_tonumber(L, 3)); } @@ -289,6 +291,7 @@ int getUserToday(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; string item{ lua_to_gb18030_string(L, 2) }; + if (item.empty())return 0; if (item == "jrrp") lua_pushnumber(L, today->getJrrp(qq)); else @@ -299,6 +302,7 @@ int setUserToday(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; string item{ lua_to_gb18030_string(L, 2) }; + if (item.empty())return 0; int val{ (int)lua_to_number(L, 3) }; today->set(qq, item, val); return 0; @@ -308,6 +312,7 @@ int getPlayerCardAttr(lua_State* L) { long long plQQ{ lua_to_int(L, 1) }; long long group{ lua_to_int(L, 2) }; string key{ lua_to_gb18030_string(L, 3) }; + if (!plQQ || key.empty())return 0; CharaCard& pc = getPlayer(plQQ)[group]; if (pc.Info.count(key)) { lua_push_string(L, pc.Info.find(key)->second); @@ -331,6 +336,7 @@ int getPlayerCardAttr(lua_State* L) { } int getPlayerCard(lua_State* L) { long long plQQ{ lua_to_int(L, 1) }; + if (!plQQ)return 0; long long group{ lua_to_int(L, 2) }; if (PList.count(plQQ)) { getPlayer(plQQ)[group].pushTable(L); @@ -342,13 +348,20 @@ int setPlayerCardAttr(lua_State* L) { long long plQQ{ lua_to_int(L, 1) }; long long group{ lua_to_int(L, 2) }; string item{ lua_to_gb18030_string(L, 3) }; + if (!plQQ || item.empty())return 0; + //4ΪΪɾ,__Name CharaCard& pc = getPlayer(plQQ)[group]; - if (lua_isnumber(L, -1)) { + if (item == "__Name") { + getPlayer(plQQ).renameCard(pc.getName(), lua_to_gb18030_string(L, 4)); + } + else if (lua_isnoneornil(L, 4)) { + pc.erase(item); + } + else if (lua_isnumber(L, 4)) { pc.set(item, (int)lua_tonumber(L, -1)); } - else if (lua_isstring(L, -1)) { - if (item.empty())return 0; - else if (item[0] == '&')pc.setExp(item.substr(1), lua_to_gb18030_string(L, -1)); + else if (lua_isstring(L, 4)) { + if (item[0] == '&')pc.setExp(item.substr(1), lua_to_gb18030_string(L, -1)); else pc.setInfo(item, lua_to_gb18030_string(L, -1)); } return 0; @@ -364,6 +377,7 @@ int ranint(lua_State* L) { //̵߳ȴ int sleepTime(lua_State* L) { int ms{ (int)lua_to_int(L, 1) }; + if (ms <= 0)return 0; std::this_thread::sleep_for(std::chrono::milliseconds(ms)); return 0; } @@ -371,7 +385,9 @@ int sleepTime(lua_State* L) { int drawDeck(lua_State* L) { long long fromGroup{ lua_to_int(L, 1) }; long long fromQQ{ lua_to_int(L, 2) }; + if (!fromGroup && !fromQQ)return 0; string nameDeck{ lua_to_gb18030_string(L, 3) }; + if (nameDeck.empty())return 0; long long fromSession{ fromGroup ? fromGroup : ~fromQQ }; if (gm->has_session(fromSession)) { lua_push_string(L, gm->session(fromSession).deck_draw(nameDeck)); @@ -390,6 +406,7 @@ int sendMsg(lua_State* L) { string fromMsg{ lua_to_gb18030_string(L, 1) }; long long fromGroup{ lua_to_int(L, 2) }; long long fromQQ{ lua_to_int(L, 3) }; + if (!fromGroup && !fromQQ)return 0; msgtype type{ fromGroup ? chat(fromGroup).isGroup ? msgtype::Group : msgtype::Discuss : msgtype::Private }; From 2943d05c01ca0292162ab50890ca3efdcfad8b81 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Thu, 29 Apr 2021 22:10:38 +0800 Subject: [PATCH 158/171] fix ri MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复先攻指令新项目无法替换的bug --- Dice/DiceEvent.cpp | 4 +--- Dice/DiceSession.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 5b4a7429..845cbfea 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -3087,7 +3087,7 @@ int FromMsg::InnerOrder() { strinit = readDice(); } readSkipSpace(); - string strname = strMsg.substr(intMsgCnt); + string strname = strip(strMsg.substr(intMsgCnt)); if (strname.empty()) { if (!strVar.count("pc") || strVar["pc"].empty()) { @@ -3096,8 +3096,6 @@ int FromMsg::InnerOrder() { } strname = strVar["pc"]; } - else - strname = strip(strname); RD initdice(strinit, 20); const int intFirstTimeRes = initdice.Roll(); if (intFirstTimeRes == Value_Err) { diff --git a/Dice/DiceSession.cpp b/Dice/DiceSession.cpp index d5e8f8c3..3c7d4146 100644 --- a/Dice/DiceSession.cpp +++ b/Dice/DiceSession.cpp @@ -25,7 +25,7 @@ bool DiceSession::table_del(const string& tab, const string& item) { int DiceSession::table_add(const string& key, int prior, const string& item) { - mTable[key].emplace(item, prior); + mTable[key][item] = prior; update(); return 0; } From 7419c90cc47fcb2896118864c5148daae01df566 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Thu, 29 Apr 2021 21:02:07 +0800 Subject: [PATCH 159/171] bug fix --- Dice/DiceEvent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 845cbfea..05341ceb 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -2810,7 +2810,7 @@ int FromMsg::InnerOrder() { string strName = strip(readRest()); filter_CQcode(strName); strVar["char1"] = strName.substr(0, strName.find('=')); - strVar["char2"] = (strVar["char1"].length() < strName.length() - 1) + strVar["char2"] = (strVar["char1"].length() + 1 < strName.length()) ? strip(strName.substr(strVar["char1"].length() + 1)) : pl[fromGroup].getName(); switch (pl.copyCard(strVar["char1"], strVar["char2"], fromGroup)) { From 9651d006bd426953ae4bb2b1bb2604e25197a9b7 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 21 May 2021 14:04:24 +0800 Subject: [PATCH 160/171] fix group set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复单项群开关反馈错误的bug --- Dice/DiceEvent.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index 05341ceb..a8fe5232 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1444,8 +1444,9 @@ int FromMsg::InnerOrder() { intMsgCnt++; if(strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-'){ int cntSet{ 0 }; - while (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-') { - bool isSet = strMsg[intMsgCnt] == '+'; + bool isSet{ strMsg[intMsgCnt] == '+' }; + do{ + isSet = strMsg[intMsgCnt] == '+'; intMsgCnt++; strVar["option"] = readPara(); readSkipSpace(); @@ -1469,7 +1470,6 @@ int FromMsg::InnerOrder() { else if (grp.isset(strVar["option"])) { ++cntSet; chat(llGroup).reset(strVar["option"]); - reply(GlobalMsg["strGroupSetOff"]); } else { reply(GlobalMsg["strGroupSetOffAlready"]); @@ -1478,9 +1478,9 @@ int FromMsg::InnerOrder() { else { reply(GlobalMsg["strGroupSetDenied"]); } - } + } while (strMsg[intMsgCnt] == '+' || strMsg[intMsgCnt] == '-'); if (cntSet == 1) { - reply(GlobalMsg["strGroupSetOn"]); + isSet ? reply(GlobalMsg["strGroupSetOn"]) : reply(GlobalMsg["strGroupSetOff"]); } else if(cntSet > 1) { strVar["opt_list"] = grp.listBoolConf(); From f8a8a247c82fcde5ddf5768a089b066f660d28b9 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 24 May 2021 04:22:36 +0800 Subject: [PATCH 161/171] fix audit log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复了audit下的正确文件路径 --- Dice/DiceConsole.cpp | 2 +- Dice/DiceEvent.h | 2 +- Dice/DiceSchedule.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 225bdfcf..0ad95f38 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -133,7 +133,7 @@ void Console::rmNotice(chatType ct) int Console::log(const std::string& strMsg, int note_lv, const string& strTime) { - ofstream fout(DiceDir / "audit" / "log" / (to_string(DiceMaid) + "_" + printDate() + ".txt"), + ofstream fout(DiceDir / "audit" / ("log" + to_string(DiceMaid) + "_" + printDate() + ".txt"), ios::out | ios::app); fout << strTime << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); diff --git a/Dice/DiceEvent.h b/Dice/DiceEvent.h index 0dd08f5a..8b315273 100644 --- a/Dice/DiceEvent.h +++ b/Dice/DiceEvent.h @@ -50,7 +50,7 @@ class FromMsg : public DiceJobDetail { void note(std::string strMsg, int note_lv = 0b1) { strMsg = format(strMsg, GlobalMsg, strVar); - ofstream fout(DiceDir / "audit" / "log" / (to_string(console.DiceMaid) + "_" + printDate() + ".txt"), + ofstream fout(DiceDir / "audit" / ("log" + to_string(console.DiceMaid) + "_" + printDate() + ".txt"), ios::out | ios::app); fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 18d54dd0..0709ddbb 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -57,7 +57,7 @@ void DiceJob::reply(const std::string& msg) { AddMsgToQueue(format(msg, GlobalMsg, strVar), fromChat); } void DiceJob::note(const std::string& strMsg, int note_lv = 0b1) { - ofstream fout(DiceDir / "audit" / "log" / (to_string(console.DiceMaid) + "_" + printDate() + ".txt"), ios::out | ios::app); + ofstream fout(DiceDir / "audit" / ("log" + to_string(console.DiceMaid) + "_" + printDate() + ".txt"), ios::out | ios::app); fout << printSTNow() << "\t" << note_lv << "\t" << printLine(strMsg) << std::endl; fout.close(); echo(strMsg); From bb4ef61cddfee822f13d85ff124e69e89ab962a4 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Thu, 3 Jun 2021 16:30:37 +0800 Subject: [PATCH 162/171] update GroupAdd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加新入群时的同系Dice!识别 --- Dice/Dice.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index f748add6..77ac0d46 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -568,7 +568,7 @@ bool eve_GroupAdd(Chat& grp) } else { - int cntUser(0), cntMember(0); + size_t cntUser{ 0 }, cntMember{ 0 }, cntDiceMaid{ 0 }; for (auto& each : list) { if (each == console.DiceMaid)continue; @@ -617,6 +617,9 @@ bool eve_GroupAdd(Chat& grp) AddMsgToQueue(blacklist->list_self_qq_warning(each), fromGroup, msgtype::Group); } } + if (DD::isDiceMaid(each)) { + ++cntDiceMaid; + } } if (!chat(fromGroup).inviter && list.size() == 2 && ownerQQ) { @@ -632,6 +635,9 @@ bool eve_GroupAdd(Chat& grp) ave_trust /= cntMember; strMsg += "\nûŨ" + to_string(cntUser * 100 / cntMember) + "% (" + to_string(cntUser) + "/" + to_string(cntMember) + "), ζ" + toString(ave_trust); } + if (cntMember) { + strMsg += "\nʶͬϵDice!" + to_string(cntDiceMaid) + "λ"; + } } if (!blacks.empty()) { From 189b148ca86731f329ecf627d2c1f7ee3145770c Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 7 Jun 2021 13:29:49 +0800 Subject: [PATCH 163/171] update sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持sample转义(随机多取一) --- Dice/DiceMod.cpp | 12 ++++++++++++ Dice/GlobalVar.cpp | 5 +---- Dice/GlobalVar.h | 6 +++--- Dice/MsgFormat.cpp | 22 ++++++++++++++-------- Dice/StrExtern.hpp | 4 ++++ Dice/strExtern.cpp | 11 +++++++++++ 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Dice/DiceMod.cpp b/Dice/DiceMod.cpp index 5c12c67f..249c3cb7 100644 --- a/Dice/DiceMod.cpp +++ b/Dice/DiceMod.cpp @@ -29,6 +29,7 @@ #include "DiceFile.hpp" #include "DiceEvent.h" #include "DiceLua.h" +#include "RandomGenerator.h" using std::set; bool DiceMsgOrder::exec(FromMsg* msg) { @@ -81,9 +82,20 @@ string DiceModManager::format(string s, const map& dict key = key.substr(5); val = fmt->format(fmt->get_help(key), dict); } + else if (key.find("sample:") == 0) { + key = key.substr(7); + vector samples{ split(key,"|") }; + if (samples.empty())val = ""; + else + val = fmt->format(samples[RandomGenerator::Randint(0, samples.size() - 1)], dict); + } else if (auto it = dict.find(key); it != dict.end()){ val = format(it->second, dict, mod_name); } + //ֲȫ + else if ((it = GlobalChar.find(key)) != GlobalChar.end()) { + val = it->second; + } else if (auto func = strFuncs.find(key); func != strFuncs.end()) { val = func->second(); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 2e753356..76965ff9 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -368,6 +368,7 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( +578:תıѡһ 578:ŻȺöд 577:ڹ㲥֪ͨ 576:ʱű @@ -382,11 +383,7 @@ const std::map HelpDoc = { 567:дʼ 566:.helpѯ 565:.log־¼ -564:๦Żƶѵ 562:GUI -559:Զ̸²/¼ -554:ɫ -551:ļȡƶ 550:ּ춨 549:ˢ)"}, {"Э","0.ЭDice!ĬϷЭ顣㿴仰ζMasterӦĬЭ飬ע⡣\n1.ʹȺĶЭΪͬⲢŵشЭ飬ʹ.dismissƳ\n2.ԡƳˢȶIJΪЩΪﱻƲõķաӦʹ.bot on/off\n3.ĬΪȵõȺͬ⣬ԶͬȺ롣ʹΪʱδԤеΡ\n4.ֹڶIJΥΪ\n5.dzƵ޷ԤпΪܻұܾṩ\n6.ڼԼʽԭ޷֤100%ʱȶУܲʱͣάᣬӦἰʱ֪ͨͨ½⡣ʱͣﲻκӦʶӰȺڻ״̬ȻֹΪ\n7.ΥЭΪォֹûȺṩ񣬲¼ṩ˿ṩЭ̣ղöȨڷṩ\n8.ЭʱпܸĶעϢǩռ䡢ٷȺȴﶯ̬\n9.ṩȫѵģӭͶʳ\n10.սȨṩС"}, diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index 9d7b93f5..afce295b 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -39,9 +39,9 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 578u; -inline const std::string Dice_Ver_Without_Build = "2.5.1"; -constexpr auto DiceRequestHeader = "Dice/2.5.1"; +const unsigned short Dice_Build = 579u; +inline const std::string Dice_Ver_Without_Build = "2.5.2"; +constexpr auto DiceRequestHeader = "Dice/2.5.2"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; inline const std::string Dice_Short_Ver = "Dice! by & Shiki Ver " + Dice_Ver; diff --git a/Dice/MsgFormat.cpp b/Dice/MsgFormat.cpp index 98764258..84189b1f 100644 --- a/Dice/MsgFormat.cpp +++ b/Dice/MsgFormat.cpp @@ -28,10 +28,12 @@ #include "EncodingConvert.h" #include "StrExtern.hpp" #include "CardDeck.h" +#include "RandomGenerator.h" using std::string; -std::map GlobalChar{ +std::map GlobalChar{ {"FormFeed","\f"}, + {"Vertical","|"}, {"LBrace","{"}, {"RBrace","}"}, {"LBracket","["}, @@ -88,19 +90,23 @@ std::string format(std::string s, const std::mapformat(fmt->get_help(key), replace_str); } + else if (auto uit = str_tmp.find(key); uit != str_tmp.end()) { + if (key == "res")val = format(uit->second, replace_str, str_tmp); + else val = uit->second; + } + else if (key.find("sample:") == 0) { + key = key.substr(7); + vector samples{ split(key,"|") }; + if (samples.empty())val = ""; + else + val = format(samples[RandomGenerator::Randint(0, samples.size() - 1)], replace_str, str_tmp); + } else if (auto it = replace_str.find(key); it != replace_str.end()) { val = format(it->second, replace_str, str_tmp); } - else if ((it = GlobalChar.find(key)) != GlobalChar.end()) { - val = it->second; - } else if ((it = GlobalChar.find(key)) != GlobalChar.end()) { val = it->second; } - else if (auto uit = str_tmp.find(key); uit != str_tmp.end()) { - if (key == "res")val = format(uit->second, replace_str, str_tmp); - else val = uit->second; - } else if (auto func = strFuncs.find(key); func != strFuncs.end()) { val = func->second(); } diff --git a/Dice/StrExtern.hpp b/Dice/StrExtern.hpp index fc376eb3..bb8e0411 100644 --- a/Dice/StrExtern.hpp +++ b/Dice/StrExtern.hpp @@ -5,10 +5,12 @@ */ #include +#include using std::string; using std::u16string; using std::to_string; +using std::vector; #define CP_GBK (936) @@ -36,6 +38,8 @@ string to_signed_string(Dig num) int count_char(const string& s, char ch); +vector split(const string&, const string&); + string convert_w2a(const char16_t* wch); u16string convert_a2w(const char* ch); diff --git a/Dice/strExtern.cpp b/Dice/strExtern.cpp index 5b5be3b1..0ce0e5ae 100644 --- a/Dice/strExtern.cpp +++ b/Dice/strExtern.cpp @@ -35,6 +35,17 @@ int count_char(const string& s, char ch) { return std::count(s.begin(), s.end(), ch); } +vector split(const string& str, const string& sep) { + vector res; + size_t l{ 0 }, pos{ str.find(sep) }; + while (pos != string::npos) { + res.push_back(str.substr(l, pos - l)); + l = pos + sep.length(); + pos = str.find(sep, l); + } + if (l < str.length()) res.push_back(str.substr(l)); + return res; +} string convert_w2a(const char16_t* wch) { From fe35c6d512eb8b42ee7260b70bb950f7a8c2a4e5 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 7 Jun 2021 13:35:17 +0800 Subject: [PATCH 164/171] update loadLua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为loadLua新增后缀与相对路径的自动识别 --- Dice/DiceLua.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index c688461f..12a96c6d 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -181,7 +181,9 @@ int loadLua(lua_State* L) { if (c == '\\') c = '/'; } #endif - std::filesystem::path pathFile = DiceDir / "plugin" / (nameFile + ".lua"); + std::filesystem::path pathFile{ nameFile + ".lua" }; + if (pathFile.extension() != ".lua")pathFile = nameFile + ".lua"; + if (pathFile.is_relative())pathFile = DiceDir / "plugin" / pathFile; if (!std::filesystem::exists(pathFile) && nameFile.find('\\') == string::npos && nameFile.find('/') == string::npos) pathFile = DiceDir / "plugin" / nameFile / "init.lua"; if (luaL_loadfile(L, pathFile.string().c_str())) { From 05e4c6b6e22a94e29b7b9d7fdf618a8b8f4dd259 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 7 Jun 2021 14:50:54 +0800 Subject: [PATCH 165/171] update DiceJob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 部分任务将记录日志 --- Dice/DiceJob.cpp | 64 +++++++++++++++++++++++++++++++++------------- Dice/GlobalVar.cpp | 2 +- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 29a0f709..4a5a87c2 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -17,6 +17,7 @@ #include "CardDeck.h" #include "DiceMod.h" #include "DiceNetwork.h" +#include "DiceSession.h" #include "S3PutObject.h" #pragma warning(disable:28159) @@ -66,18 +67,19 @@ void frame_restart(DiceJob& job) { #endif } -void frame_reload(DiceJob& job){ - if(DD::reload()) +void frame_reload(DiceJob& job) { + if (DD::reload()) job.note("" + getMsg("self") + "ɡ", 1); else job.note("" + getMsg("self") + "ʧܡ", 0b10); } void check_system(DiceJob& job) { - #ifdef _WIN32 + console.log("ϵͳ", 0, printSTNow()); +#ifdef _WIN32 static int perRAM(0), perLastRAM(0); static double perLastCPU(0), perLastDisk(0), - perCPU(0), perDisk(0); + perCPU(0), perDisk(0); static bool isAlarmRAM(false), isAlarmCPU(false), isAlarmDisk(false); static double mbFreeBytes = 0, mbTotalBytes = 0; //ڴ @@ -132,17 +134,18 @@ void check_system(DiceJob& job) { else { sch.add_job_for(30 * 60, job); } - #endif +#endif } void auto_save(DiceJob& job) { if (sch.is_job_cold("autosave"))return; + console.log("Զ", 0, printSTNow()); dataBackUp(); //console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); if (console["AutoSaveInterval"] > 0) { - sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"]); + sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"] * 60); sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); } } @@ -165,7 +168,7 @@ void clear_image(DiceJob& job) { } job.note("" + GlobalMsg["strSelfName"] + "ͼƬ" + to_string(sReferencedImage.size()) + "", 0b0); int cnt = clrDir("data/image/", sReferencedImage); - job.note("imageļ"+ to_string(cnt) + "", 1); + job.note("imageļ" + to_string(cnt) + "", 1); if (console["AutoClearImage"] > 0) { sch.refresh_cold("clrimage", time(NULL) + console["AutoClearImage"]); sch.add_job_for(console["AutoClearImage"] * 60 * 60, "clrimage"); @@ -173,19 +176,25 @@ void clear_image(DiceJob& job) { } void clear_group(DiceJob& job) { + console.log("ʼȺ", 0, printSTNow()); int intCnt = 0; ResList res; + vector GrpDelete; + time_t grpline{ console["InactiveGroupLine"] > 0 ? tNow - console["InactiveGroupLine"] * 86400 : 0 }; if (job.strVar["clear_mode"] == "unpower") { for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; if (grp.isGroup && !DD::isGroupAdmin(id, console.DiceMaid, true)) { res << printGroup(id); + time_t tLast{ grp.tUpdated }; + if (gm->has_session(id) && gm->session(id).tUpdate > grp.tUpdated)tLast = gm->session(id).tUpdate; + if (tLast < grpline)GrpDelete.push_back(id); grp.leave(getMsg("strLeaveNoPower")); intCnt++; this_thread::sleep_for(3s); } } - console.log(GlobalMsg["strSelfName"] + "ɸȺȨȺ" + to_string(intCnt) + ":" + res.show(), 0b10, printSTNow()); + job.note(GlobalMsg["strSelfName"] + "ɸȺȨȺ" + to_string(intCnt) + ":" + res.show(), 0b10); } else if (isdigit(static_cast(job.strVar["clear_mode"][0]))) { int intDayLim = stoi(job.strVar["clear_mode"]); @@ -198,6 +207,9 @@ void clear_group(DiceJob& job) { if (!tLast)continue; int intDay = (int)(tNow - tLast) / 86400; if (intDay > intDayLim) { + time_t tLast{ grp.tUpdated }; + if (gm->has_session(id) && gm->session(id).tUpdate > grp.tUpdated)tLast = gm->session(id).tUpdate; + if (tLast < grpline)GrpDelete.push_back(id); job["day"] = to_string(intDay); res << printGroup(id) + ":" + to_string(intDay) + "\n"; grp.leave(getMsg("strLeaveUnused", job.strVar)); @@ -211,25 +223,30 @@ void clear_group(DiceJob& job) { try { for (auto id : DD::getGroupIDList()) { Chat& grp = chat(id).group().name(DD::getGroupName(id)); - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("") || grp.isset("ЭЧ"))continue; + if (grp.isset("") || grp.isset("") || grp.isset("") || grp.isset("ЭЧ"))continue; if (blacklist->get_group_danger(id)) { - res << printGroup(id) + "" + "Ⱥ"; + time_t tLast{ grp.tUpdated }; + if (gm->has_session(id) && gm->session(id).tUpdate > grp.tUpdated)tLast = gm->session(id).tUpdate; + if (tLast < grpline)GrpDelete.push_back(id); + res << printGroup(id) + "Ⱥ"; if (console["LeaveBlackGroup"])grp.leave(getMsg("strBlackGroup")); } set MemberList{ DD::getGroupMemberList(id) }; int authSelf{ DD::getGroupAuth(id, console.DiceMaid, 1) }; for (auto eachQQ : MemberList) { if (blacklist->get_qq_danger(eachQQ) > 1) { - if (auto authBlack{DD::getGroupAuth(id, eachQQ, 1)};authBlack < authSelf) { + if (auto authBlack{ DD::getGroupAuth(id, eachQQ, 1) }; authBlack < authSelf) { continue; } else if (authBlack > authSelf) { + if (grp.tUpdated < grpline)GrpDelete.push_back(id); res << printChat(grp) + "" + printQQ(eachQQ) + "ԷȺȨ޽ϸ"; grp.leave("ֺԱ" + printQQ(eachQQ) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); intCnt++; break; } else if (console["LeaveBlackQQ"]) { + if (grp.tUpdated < grpline)GrpDelete.push_back(id); res << printChat(grp) + "" + printQQ(eachQQ); grp.leave("ֺԱ" + printQQ(eachQQ) + "\n" + GlobalMsg["strSelfName"] + "ԤȺ"); intCnt++; @@ -238,8 +255,7 @@ void clear_group(DiceJob& job) { } } } - } - catch (...) { + } catch (...) { console.log("ѣ" + GlobalMsg["strSelfName"] + "Ⱥʱ", 0b10, printSTNow()); } if (intCnt) { @@ -256,17 +272,28 @@ void clear_group(DiceJob& job) { grp.set("ʹ"); continue; } + time_t tLast{ grp.tUpdated }; + if (gm->has_session(id) && gm->session(id).tUpdate > grp.tUpdated)tLast = gm->session(id).tUpdate; + if (tLast < grpline)GrpDelete.push_back(id); res << printChat(grp); grp.leave(getMsg("strPreserve")); intCnt++; this_thread::sleep_for(3s); } - console.log(GlobalMsg["strSelfName"] + "ɸȺ" + to_string(intCnt) + "" + res.show(), 1, printSTNow()); + job.note(GlobalMsg["strSelfName"] + "ɸȺ" + to_string(intCnt) + "" + res.show(), 1); } else job.echo("޷ʶɸѡ"); + if (!GrpDelete.empty()) { + for (const auto& id : GrpDelete) { + ChatList.erase(id); + gm->session_end(id); + } + job.note("ȺʱղԾ¼" + to_string(GrpDelete.size()) + "", 0b1); + } } void list_group(DiceJob& job) { + console.log("Ⱥб", 0, printSTNow()); if (job["list_mode"].empty()) { job.reply(fmt->get_help("groups_list")); } @@ -347,7 +374,7 @@ void dice_update(DiceJob& job) { //ȡƲ¼ void dice_cloudblack(DiceJob& job) { bool isSuccess(false); - job.echo("ʼȡƶ˼¼"); + job.note("ʼȡƶ˼¼", 0); string strURL("https://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); switch (Cloud::DownloadFile(strURL.c_str(), DiceDir / "conf" / "CloudBlackList.json")) { case -1: { @@ -360,7 +387,7 @@ void dice_cloudblack(DiceJob& job) { else job.echo("ͬƲ¼ͬʧ:" + des); } - break; + break; case -2: job.echo("ͬƲ¼ͬʧ!ļδҵ"); break; @@ -384,12 +411,13 @@ void log_put(DiceJob& job) { if (job["ret"] == "SUCCESS") { job.echo(getMsg("strLogUpSuccess", job.strVar)); } - else if (job.cntExec++ > 1) { - job.echo(getMsg("strLogUpFailureEnd",job.strVar)); + else if (job.cntExec++ > 2) { + job.echo(getMsg("strLogUpFailureEnd", job.strVar)); } else { job["retry"] = to_string(job.cntExec); job.echo(getMsg("strLogUpFailure", job.strVar)); + console.log(getMsg("strLogUpFailure", job.strVar), 1); sch.add_job_for(2 * 60, job); } } diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 76965ff9..5515f7d7 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -57,7 +57,7 @@ std::map GlobalMsg {"strLogEndEmpty","{self}ѽ¼\n־"}, {"strLogNullErr","{self}־¼ѽ"}, {"strLogUpSuccess","{self}־ϴ\n https://logpainter.kokona.tech/?s3={log_file} Բ鿴¼"}, - {"strLogUpFailure","{self}ϴ־ļʧܣڵ{retry}ԡ{ret}"}, + {"strLogUpFailure","{self}ϴ־ļʧܣڵ{retry}ش{log_file}{ret}"}, {"strLogUpFailureEnd","ź{self}޷ɹϴ־ļ\n{ret}\nȡϵMaster:{master_QQ}\nļ:{log_file}"}, {"strGMTableShow","{self}¼{table_name}б: {res}"}, {"strGMTableClr","{self}{table_name}"}, From f8ca54cdd06b106d8c5066340d47b82a2636c91e Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Mon, 7 Jun 2021 16:32:26 +0800 Subject: [PATCH 166/171] update AutoClear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增clearGroup,更新clearUser,清理超出期限未使用的记录 期限默认360天 受信任用户、免清群无视清理 函数会在clear任务中调用 --- Dice/DiceConsole.cpp | 2 ++ Dice/DiceEvent.cpp | 11 +++++++++- Dice/DiceJob.cpp | 14 ++++++------- Dice/DiceSchedule.cpp | 6 ++++++ Dice/GlobalVar.cpp | 3 ++- Dice/GlobalVar.h | 2 +- Dice/ManagerSystem.cpp | 46 ++++++++++++++++++++++++++++++++++-------- Dice/ManagerSystem.h | 1 + 8 files changed, 67 insertions(+), 18 deletions(-) diff --git a/Dice/DiceConsole.cpp b/Dice/DiceConsole.cpp index 0ad95f38..d48a45ef 100644 --- a/Dice/DiceConsole.cpp +++ b/Dice/DiceConsole.cpp @@ -56,6 +56,8 @@ const std::mapConsole::intDefault{ {"SendIntervalIdle",500},{"SendIntervalBusy",100}, //Զ¼[min],ԶͼƬ[h],Զܼ[h] {"AutoSaveInterval",5},{"AutoClearImage",0},{"AutoFrameRemake",0}, +//û¼[Day],Ⱥ¼[Day] +{"InactiveUserLine",360},{"InactiveGroupLine",360}, //ȺԼϢԼ˽Ϣ {"ListenGroupEcho",0},{"ListenSelfEcho",0}, }; diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index a8fe5232..eb2c2e3f 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -1185,6 +1185,15 @@ int FromMsg::InnerOrder() { cmd_key = "lsgroup"; sch.push_job(*this); } + else if (strOption == "clr") { + if (trusted < 5) { + reply(GlobalMsg["strNotMaster"]); + return 1; + } + int cnt = clearGroup(); + note("Ⱥ¼" + to_string(cnt) + "", 0b10); + return 1; + } } else if (strLowerMessage.substr(intMsgCnt, 6) == "setcoc") { if (!isAuth) { @@ -2271,7 +2280,7 @@ int FromMsg::InnerOrder() { return 1; } int cnt = clearUser(); - note("Чû¼" + to_string(cnt) + "", 0b10); + note("Чû¼" + to_string(cnt) + "", 0b10); return 1; } } diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 4a5a87c2..0cdaa633 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -180,7 +180,7 @@ void clear_group(DiceJob& job) { int intCnt = 0; ResList res; vector GrpDelete; - time_t grpline{ console["InactiveGroupLine"] > 0 ? tNow - console["InactiveGroupLine"] * 86400 : 0 }; + time_t grpline{ console["InactiveGroupLine"] > 0 ? (tNow - console["InactiveGroupLine"] * (time_t)86400) : 0 }; if (job.strVar["clear_mode"] == "unpower") { for (auto& [id, grp] : ChatList) { if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; @@ -201,15 +201,15 @@ void clear_group(DiceJob& job) { string strDayLim = to_string(intDayLim); time_t tNow = time(NULL); for (auto& [id, grp] : ChatList) { - if (grp.isset("") || grp.isset("") || grp.isset("δ") || grp.isset("") || grp.isset("ЭЧ"))continue; - time_t tLast = grp.tUpdated; + if (grp.isset("") || grp.isset("") || grp.isset("ЭЧ"))continue; + time_t tLast{ grp.tUpdated }; if (long long tLMT; grp.isGroup && ((tLMT = DD::getGroupLastMsg(id, console.DiceMaid)) > 0 && tLMT > tLast))tLast = tLMT; + if (gm->has_session(id) && gm->session(id).tUpdate > tLast)tLast = gm->session(id).tUpdate; if (!tLast)continue; + if (tLast < grpline && !grp.isset(""))GrpDelete.push_back(id); + if (grp.isset("") || grp.isset("δ"))continue; int intDay = (int)(tNow - tLast) / 86400; if (intDay > intDayLim) { - time_t tLast{ grp.tUpdated }; - if (gm->has_session(id) && gm->session(id).tUpdate > grp.tUpdated)tLast = gm->session(id).tUpdate; - if (tLast < grpline)GrpDelete.push_back(id); job["day"] = to_string(intDay); res << printGroup(id) + ":" + to_string(intDay) + "\n"; grp.leave(getMsg("strLeaveUnused", job.strVar)); @@ -287,7 +287,7 @@ void clear_group(DiceJob& job) { if (!GrpDelete.empty()) { for (const auto& id : GrpDelete) { ChatList.erase(id); - gm->session_end(id); + if (gm->has_session(id))gm->session_end(id); } job.note("ȺʱղԾ¼" + to_string(GrpDelete.size()) + "", 0b1); } diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 0709ddbb..123737f4 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -292,6 +292,12 @@ void ConsoleTimer() { sch.push_job("clrgroup", true, { {"clear_mode","black"} }); + if (int cnt{ clearGroup() }) { + console.log("Ⱥ¼" + to_string(cnt) + "", 1, printSTime(stTmp)); + } + if (int cnt{ clearUser() }) { + console.log("Чû¼" + to_string(cnt) + "", 1, printSTime(stTmp)); + } break; default: fmt->call_task(eve_type); diff --git a/Dice/GlobalVar.cpp b/Dice/GlobalVar.cpp index 5515f7d7..2dedddc8 100644 --- a/Dice/GlobalVar.cpp +++ b/Dice/GlobalVar.cpp @@ -368,7 +368,8 @@ std::map GlobalMsg std::map EditedMsg; const std::map HelpDoc = { {"",R"( -578:תıѡһ +580:ڼ¼ +579:תıѡһ 578:ŻȺöд 577:ڹ㲥֪ͨ 576:ʱű diff --git a/Dice/GlobalVar.h b/Dice/GlobalVar.h index afce295b..216015c9 100644 --- a/Dice/GlobalVar.h +++ b/Dice/GlobalVar.h @@ -39,7 +39,7 @@ * ޸Dice_Build, Dice_Ver_Without_BuildDiceRequestHeaderԼDice_Ver * ޸Dice_Short_VerDice_Full_VerԴﵽ汾Զ */ -const unsigned short Dice_Build = 579u; +const unsigned short Dice_Build = 580u; inline const std::string Dice_Ver_Without_Build = "2.5.2"; constexpr auto DiceRequestHeader = "Dice/2.5.2"; inline const std::string Dice_Ver = Dice_Ver_Without_Build + "(" + std::to_string(Dice_Build) + ")"; diff --git a/Dice/ManagerSystem.cpp b/Dice/ManagerSystem.cpp index e93130ce..436e905f 100644 --- a/Dice/ManagerSystem.cpp +++ b/Dice/ManagerSystem.cpp @@ -11,9 +11,11 @@ #include "ManagerSystem.h" #include "CardDeck.h" +#include "CharacterCard.h" #include "GlobalVar.h" #include "DDAPI.h" #include "CQTools.h" +#include "DiceSession.h" std::filesystem::path dirExe; std::filesystem::path DiceDir("DiceData"); @@ -53,22 +55,50 @@ short trustedQQ(long long qq) else return UserList[qq].nTrust; } -int clearUser() -{ + +int clearUser() { vector QQDelete; - for (const auto& [qq, user] : UserList) - { - if (user.empty()) - { + time_t tNow{ time(nullptr) }; + time_t userline{ tNow - console["InactiveUserLine"] * (time_t)86400 }; + bool isClearInactive{ console["InactiveUserLine"] > 0 }; + for (const auto& [qq, user] : UserList) { + if (user.nTrust > 0)continue; + if (user.empty()) { + QQDelete.push_back(qq); + } + else if (isClearInactive) { + time_t tLast{ user.tUpdated }; + if (gm->has_session(~qq) && gm->session(~qq).tUpdate > tLast)tLast = gm->session(~qq).tUpdate; + if (tLast >= userline)continue; QQDelete.push_back(qq); + if (PList.count(qq)) { + PList.erase(qq); + } } } - for (const auto& qq : QQDelete) - { + for (const auto& qq : QQDelete) { UserList.erase(qq); + if (gm->has_session(~qq))gm->session_end(~qq); } return QQDelete.size(); } +int clearGroup() { + if (console["InactiveGroupLine"] <= 0)return 0; + vector GrpDelete; + time_t tNow{ time(nullptr) }; + time_t grpline{ tNow - console["InactiveGroupLine"] * (time_t)86400 }; + for (const auto& [id, grp] : ChatList) { + if (grp.is_except() || grp.isset("") || grp.isset(""))continue; + time_t tLast{ grp.tUpdated }; + if (gm->has_session(id) && gm->session(id).tUpdate > grp.tUpdated)tLast = gm->session(id).tUpdate; + if (tLast < grpline)GrpDelete.push_back(id); + } + for (const auto& id : GrpDelete) { + ChatList.erase(id); + } + return GrpDelete.size(); + +} string getName(long long QQ, long long GroupID) { diff --git a/Dice/ManagerSystem.h b/Dice/ManagerSystem.h index eba7eba1..b8b3ef62 100644 --- a/Dice/ManagerSystem.h +++ b/Dice/ManagerSystem.h @@ -176,6 +176,7 @@ extern unordered_map UserList; User& getUser(long long qq); short trustedQQ(long long qq); int clearUser(); +int clearGroup(); string getName(long long QQ, long long GroupID = 0); void filter_CQcode(string&, long long fromGroup = 0); From fde0301f11c789ac25b1d79771c0d1bbb70d9a7e Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Tue, 8 Jun 2021 21:36:19 +0800 Subject: [PATCH 167/171] fix a small bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正判断条件 --- Dice/Dice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dice/Dice.cpp b/Dice/Dice.cpp index 77ac0d46..609584a2 100644 --- a/Dice/Dice.cpp +++ b/Dice/Dice.cpp @@ -635,7 +635,7 @@ bool eve_GroupAdd(Chat& grp) ave_trust /= cntMember; strMsg += "\nûŨ" + to_string(cntUser * 100 / cntMember) + "% (" + to_string(cntUser) + "/" + to_string(cntMember) + "), ζ" + toString(ave_trust); } - if (cntMember) { + if (cntDiceMaid) { strMsg += "\nʶͬϵDice!" + to_string(cntDiceMaid) + "λ"; } } From 308ae5dc4f1ee9bceb1f161dcf01d5114a0bdbf5 Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Fri, 9 Jul 2021 21:50:06 +0800 Subject: [PATCH 168/171] update FrqMonitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化指令检测机制,高频通知时会附带最近一次的指令内容 --- Dice/DiceEvent.cpp | 12 ++++++------ Dice/DiceJob.cpp | 4 ++-- Dice/DiceSchedule.cpp | 2 ++ Dice/MsgMonitor.cpp | 20 ++++++++++++-------- Dice/MsgMonitor.h | 4 ++-- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Dice/DiceEvent.cpp b/Dice/DiceEvent.cpp index eb2c2e3f..057b1160 100644 --- a/Dice/DiceEvent.cpp +++ b/Dice/DiceEvent.cpp @@ -3798,9 +3798,9 @@ int FromMsg::CustomReply() string strAns(CardDeck::drawCard(deck->second, true)); if (fromQQ == console.DiceMaid && strAns == strKey)return 0; reply(strAns); - if(!isVirtual)AddFrq(fromQQ, fromTime, fromChat); + if (!isVirtual)AddFrq(fromQQ, fromTime, fromChat, strMsg); else - AddFrq(0, fromTime, fromChat); + AddFrq(0, fromTime, fromChat, strMsg); return 1; } return 0; @@ -3847,12 +3847,12 @@ bool FromMsg::DiceFilter() { if (isAns) { if (!isVirtual) { - AddFrq(fromQQ, fromTime, fromChat); + AddFrq(fromQQ, fromTime, fromChat, strMsg); getUser(fromQQ).update(fromTime); if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); } else { - AddFrq(0, fromTime, fromChat); + AddFrq(0, fromTime, fromChat, strMsg); } } return 1; @@ -3866,12 +3866,12 @@ bool FromMsg::DiceFilter() if (!isDisabled && (isCalled || !pGrp->isset("ָͣ"))) { if (fmt->listen_order(this) || InnerOrder()) { if (!isVirtual) { - AddFrq(fromQQ, fromTime, fromChat); + AddFrq(fromQQ, fromTime, fromChat, strMsg); getUser(fromQQ).update(fromTime); if (fromChat.second != msgtype::Private)chat(fromGroup).update(fromTime); } else { - AddFrq(0, fromTime, fromChat); + AddFrq(0, fromTime, fromChat, strMsg); } return true; } diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 0cdaa633..2577dfb9 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -145,7 +145,7 @@ void auto_save(DiceJob& job) { dataBackUp(); //console.log(GlobalMsg["strSelfName"] + "Զ", 0, printSTNow()); if (console["AutoSaveInterval"] > 0) { - sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"] * 60); + sch.refresh_cold("autosave", time(NULL) + console["AutoSaveInterval"] * (time_t)60); sch.add_job_for(console["AutoSaveInterval"] * 60, "autosave"); } } @@ -374,7 +374,7 @@ void dice_update(DiceJob& job) { //ȡƲ¼ void dice_cloudblack(DiceJob& job) { bool isSuccess(false); - job.note("ʼȡƶ˼¼", 0); + job.note("ʼȡƲ¼", 0); string strURL("https://shiki.stringempty.xyz/blacklist/checked.json?" + to_string(job.fromTime)); switch (Cloud::DownloadFile(strURL.c_str(), DiceDir / "conf" / "CloudBlackList.json")) { case -1: { diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index 123737f4..fad3dccb 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -165,6 +165,8 @@ void DiceScheduler::start() { if (console["AutoFrameRemake"] > 0) add_job_for(console["AutoFrameRemake"] * 60 * 60, "remake"); else add_job_for(60 * 60, "remake"); + if (console["CloudBlackShare"] > 0) + add_job_for(12 * 60 * 60, "cloudblack"); } void DiceScheduler::end() { } diff --git a/Dice/MsgMonitor.cpp b/Dice/MsgMonitor.cpp index 803e3549..0440db6c 100644 --- a/Dice/MsgMonitor.cpp +++ b/Dice/MsgMonitor.cpp @@ -21,12 +21,12 @@ std::queue EarliestMsgQueue; std::set setFrq; std::mutex FrqMutex; -void AddFrq(long long QQ, time_t TT, chatType CT) +void AddFrq(long long QQ, time_t TT, chatType CT, const string& msg) { std::lock_guard lock_queue(FrqMutex); if (setFrq.count(QQ)) return; setFrq.insert(QQ); - auto* newFrq = new FrqMonitor(QQ, TT, CT); + auto* newFrq = new FrqMonitor(QQ, TT, CT, msg); EarlyMsgQueue.push(newFrq); if (QQ) { FrqMonitor::sumFrqTotal++; @@ -77,7 +77,7 @@ void frqHandler() } -FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromTime(TT) { +FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT, const string& msg) : fromQQ(QQ), fromTime(TT) { if (mFrequence.count(fromQQ)) { mFrequence[fromQQ] += 10; mCntOrder[fromQQ] += 1; @@ -87,7 +87,8 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT const std::string strMsg = "ѣ\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 18 ? "/5min" - : (mCntOrder[fromQQ] > 8 ? "/min" : "/30s")); + : (mCntOrder[fromQQ] > 8 ? "/min" : "/30s")) + + "\nָ: " + msg; if(QQ!=console.DiceMaid)AddMsgToQueue(getMsg("strSpamFirstWarning"), CT); console.log(strMsg, 1, printSTNow()); } @@ -96,8 +97,9 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT const std::string strMsg = "棺\n" + (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + printQQ(fromQQ) + "Ƶָ" + to_string(mCntOrder[fromQQ]) + (mCntOrder[fromQQ] > 36 ? "/5min" - : (mCntOrder[fromQQ] > 15 ? "/min" : "/30s")); - if (QQ!=console.DiceMaid)AddMsgToQueue(getMsg("strSpamFinalWarning"), CT); + : (mCntOrder[fromQQ] > 15 ? "/min" : "/30s")) + + "\nָ: " + msg; + if (QQ != console.DiceMaid)AddMsgToQueue(getMsg("strSpamFinalWarning"), CT); console.log(strMsg, 0b10, printSTNow()); } else if (mFrequence[fromQQ] > 200 && mWarnLevel[fromQQ] < 200) { @@ -107,11 +109,13 @@ FrqMonitor::FrqMonitor(long long QQ, time_t TT, chatType CT) : fromQQ(QQ), fromT + (mCntOrder[fromQQ] > 60 ? "/5min" : (mCntOrder[fromQQ] > 25 ? "/min" : "/30s")); if (!QQ) { - console.log("棺" + GlobalMsg["strSelfName"] + "Ƶָ" + strFrq, 0b1000, strNow); + console.log("棺" + GlobalMsg["strSelfName"] + "Ƶָ" + strFrq + + "\nָ: " + msg, 0b1000, strNow); return; } std::string strNote = (CT.second != msgtype::Private ? printChat(CT) : "˽Ĵ") + "⵽" + - printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + strFrq; + printQQ(fromQQ) + "" + printQQ(console.DiceMaid) + "Ƶָ" + strFrq + + "\nָ: " + msg; if (QQ==console.DiceMaid) { console.set("ListenSelfEcho", 0); console.set("ListenGroupEcho", 0); diff --git a/Dice/MsgMonitor.h b/Dice/MsgMonitor.h index 731d30ae..09a05d9b 100644 --- a/Dice/MsgMonitor.h +++ b/Dice/MsgMonitor.h @@ -27,7 +27,7 @@ class FrqMonitor long long fromQQ = 0; time_t fromTime = 0; - FrqMonitor(long long QQ, time_t TT, chatType CT); + FrqMonitor(long long QQ, time_t TT, chatType CT, const string& msg); ~FrqMonitor() { @@ -67,5 +67,5 @@ class FrqMonitor } }; -void AddFrq(long long QQ, time_t TT, chatType CT); +void AddFrq(long long QQ, time_t TT, chatType CT, const string& msg); void frqHandler(); From b8de20a653f5f476e9ba3e5fc78d0b13df1edafd Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 14 Jul 2021 17:20:10 +0800 Subject: [PATCH 169/171] update getJrrp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 因每名用户获取jrrp失败发送的报错通知每天至多一次 --- Dice/DiceSchedule.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Dice/DiceSchedule.cpp b/Dice/DiceSchedule.cpp index fad3dccb..b2047cb9 100644 --- a/Dice/DiceSchedule.cpp +++ b/Dice/DiceSchedule.cpp @@ -180,11 +180,12 @@ int DiceToday::getJrrp(long long qq) { return cntUser[qq]["jrrp"] = stoi(res); } else { - if (!cntUser[qq].count("jrrp_local")) + if (!cntUser[qq].count("jrrp_local")) { cntUser[qq]["jrrp_local"] = RandomGenerator::Randint(1, 100); - console.log(getMsg("strJrrpErr", - { {"res", res} } - ), 1); + console.log(getMsg("strJrrpErr", + { {"res", res} } + ), 1); + } return cntUser[qq]["jrrp_local"]; } } From de3f9aa6c3faa441c53d6082fbe2094be3d34fcc Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Wed, 14 Jul 2021 17:22:29 +0800 Subject: [PATCH 170/171] update getConf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化了2个获取配置的接口 原本无记录时不会生成记录 允许获取记录创建时间和最后更新时间 允许获取群人数、容量 --- Dice/DiceLua.cpp | 69 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/Dice/DiceLua.cpp b/Dice/DiceLua.cpp index 12a96c6d..0a88edbf 100644 --- a/Dice/DiceLua.cpp +++ b/Dice/DiceLua.cpp @@ -181,7 +181,7 @@ int loadLua(lua_State* L) { if (c == '\\') c = '/'; } #endif - std::filesystem::path pathFile{ nameFile + ".lua" }; + std::filesystem::path pathFile{ nameFile }; if (pathFile.extension() != ".lua")pathFile = nameFile + ".lua"; if (pathFile.is_relative())pathFile = DiceDir / "plugin" / pathFile; if (!std::filesystem::exists(pathFile) && nameFile.find('\\') == string::npos && nameFile.find('/') == string::npos) @@ -217,18 +217,37 @@ int getGroupConf(lua_State* L) { long long id{ lua_to_int(L, 1) }; string item{ lua_to_gb18030_string(L, 2) }; if (!id || item.empty())return 0; - Chat& grp{ chat(id) }; - if (item == "name") { - lua_push_string(L, grp.Name); - } - else if (mChatConf.count(item)) { - lua_pushboolean(L, grp.boolConf.count(item)); - } - else if (grp.intConf.count(item)) { - lua_pushnumber(L, (double)grp.intConf[item]); + if (lua_gettop(L) > 3)lua_settop(L, 3); + if (item == "size") { + lua_pushnumber(L, (double)DD::getGroupSize(id).siz); + } + else if (item == "maxsize") { + lua_pushnumber(L, (double)DD::getGroupSize(id).cap); + } + else if (ChatList.count(id)) { + Chat& grp{ chat(id) }; + if (item == "name") { + if (grp.Name.empty())lua_push_string(L, grp.Name = DD::getGroupName(id)); + else lua_push_string(L, grp.Name); + } + else if (item == "firstCreate") { + lua_pushnumber(L, (double)grp.tCreated); + } + else if (item == "lastUpdate") { + lua_pushnumber(L, (double)grp.tUpdated); + } + else if (mChatConf.count(item)) { + lua_pushboolean(L, grp.boolConf.count(item)); + } + else if (grp.intConf.count(item)) { + lua_pushnumber(L, (double)grp.intConf[item]); + } + else if (grp.strConf.count(item)) { + lua_push_string(L, grp.strConf[item]); + } } - else if (grp.strConf.count(item)) { - lua_push_string(L, grp.strConf[item]); + else if (item == "name") { + lua_push_string(L, DD::getGroupName(id)); } else { lua_pushnil(L); @@ -255,20 +274,34 @@ int setGroupConf(lua_State* L) { int getUserConf(lua_State* L) { long long qq{ lua_to_int(L, 1) }; if (!qq)return 0; + if (lua_gettop(L) > 3)lua_settop(L, 3); string item{ lua_to_gb18030_string(L, 2) }; if (item.empty())return 0; - User& user{ getUser(qq) }; if (item == "nick" ) { lua_push_string(L, getName(qq)); } else if (item == "trust") { lua_pushnumber(L, trustedQQ(qq)); } - else if (user.intConf.count(item)) { - lua_pushnumber(L, (double)user.intConf[item]); - } - else if (user.strConf.count(item)) { - lua_push_string(L, user.strConf[item]); + else if (UserList.count(qq)) { + User& user{ getUser(qq) }; + if (item == "firstCreate") { + lua_pushnumber(L, (double)user.tCreated); + } + else if (item == "lastUpdate") { + lua_pushnumber(L, (double)user.tUpdated); + } + else if (item == "nn") { + string nick; + user.getNick(nick, qq); + lua_push_string(L, nick); + } + else if (user.intConf.count(item)) { + lua_pushnumber(L, (double)user.intConf[item]); + } + else if (user.strConf.count(item)) { + lua_push_string(L, user.strConf[item]); + } } else { lua_pushnil(L); From 76f87804c540f6163034050bbce00e25c316a8cb Mon Sep 17 00:00:00 2001 From: "String.Empty" Date: Thu, 22 Jul 2021 15:47:33 +0800 Subject: [PATCH 171/171] fix GroupClearLimit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复单次清群上限实际并无作用的bug --- Dice/DiceJob.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Dice/DiceJob.cpp b/Dice/DiceJob.cpp index 2577dfb9..2b409328 100644 --- a/Dice/DiceJob.cpp +++ b/Dice/DiceJob.cpp @@ -191,6 +191,7 @@ void clear_group(DiceJob& job) { if (tLast < grpline)GrpDelete.push_back(id); grp.leave(getMsg("strLeaveNoPower")); intCnt++; + if (console["GroupClearLimit"] > 0 && intCnt >= console["GroupClearLimit"])break; this_thread::sleep_for(3s); } } @@ -214,7 +215,8 @@ void clear_group(DiceJob& job) { res << printGroup(id) + ":" + to_string(intDay) + "\n"; grp.leave(getMsg("strLeaveUnused", job.strVar)); intCnt++; - this_thread::sleep_for(2s); + if (console["GroupClearLimit"] > 0 && intCnt >= console["GroupClearLimit"])break; + this_thread::sleep_for(3s); } } job.note(GlobalMsg["strSelfName"] + "ɸDZˮ" + strDayLim + "Ⱥ" + to_string(intCnt) + "" + res.show(), 0b10); @@ -245,6 +247,10 @@ void clear_group(DiceJob& job) { intCnt++; break; } + else if (console["GroupClearLimit"] > 0 && intCnt >= console["GroupClearLimit"]) { + if(intCnt == console["GroupClearLimit"])res << "*Ѵ*"; + res << printChat(grp) + "" + printQQ(eachQQ); + } else if (console["LeaveBlackQQ"]) { if (grp.tUpdated < grpline)GrpDelete.push_back(id); res << printChat(grp) + "" + printQQ(eachQQ); @@ -278,6 +284,7 @@ void clear_group(DiceJob& job) { res << printChat(grp); grp.leave(getMsg("strPreserve")); intCnt++; + if (console["GroupClearLimit"] > 0 && intCnt >= console["GroupClearLimit"])break; this_thread::sleep_for(3s); } job.note(GlobalMsg["strSelfName"] + "ɸȺ" + to_string(intCnt) + "" + res.show(), 1);