forked from townforge/townforge
More script work, especially with blob deduplication
This commit is contained in:
parent
b2b89c6d00
commit
91d09e545e
2
external/tb
vendored
2
external/tb
vendored
@ -1 +1 @@
|
||||
Subproject commit 7ad8019d0e1e6ade950ea441b63996e1d42b6bd2
|
||||
Subproject commit 24487e5a2f27c6df8e7ecdbfa45f623512f0e197
|
@ -26,6 +26,7 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "string_tools.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_db/locked_txn.h"
|
||||
@ -160,6 +161,10 @@ bool cc_command_handler_start_script::execute(cryptonote::BlockchainDB &db, cons
|
||||
if (std::get<0>(e) == cc::script::override_string)
|
||||
string_overrides[std::get<1>(e)] = std::get<2>(e);
|
||||
}
|
||||
for (auto &e: string_overrides)
|
||||
{
|
||||
cc::script::resolve_reference(db, e.second); // ignore errors, we might have 32 byte text and not a ref
|
||||
}
|
||||
db.set_cc_account_script_state(start_script.cc_account, start_script.script, owner, 0, start_script.city, string_overrides);
|
||||
|
||||
for (const auto &e: overrides)
|
||||
|
@ -294,6 +294,25 @@ bool load_script_binary(const std::string &blob, ScriptHandle &script)
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename S, typename F>
|
||||
static bool foreach_blob_candidate(S *s, const F &f)
|
||||
{
|
||||
if (!f(s->icon)) return false;
|
||||
for (auto &o: s->overrides)
|
||||
{
|
||||
if (o.type == override_string)
|
||||
if (!f(o.text))
|
||||
return false;
|
||||
}
|
||||
for (auto &state: s->states)
|
||||
{
|
||||
if (!f(state.image)) return false;
|
||||
if (!f(state.ui)) return false;
|
||||
if (!f(state.choices_ui)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
crypto::hash get_blob_hash(const cryptonote::blobdata &blob)
|
||||
{
|
||||
crypto::hash hash;
|
||||
@ -318,9 +337,7 @@ bool save_script_binary(const ScriptHandle &script, cryptonote::blobdata &blob,
|
||||
if (blobs)
|
||||
{
|
||||
blobs->clear();
|
||||
replace_blob_with_hash(copy.icon, *blobs);
|
||||
for (auto &s: copy.states)
|
||||
replace_blob_with_hash(s.image, *blobs);
|
||||
foreach_blob_candidate(©, [blobs](std::string &s){ replace_blob_with_hash(s, *blobs); return true; });
|
||||
}
|
||||
blob = cryptonote::t_serializable_object_to_blob(const_cast<Script&>(copy));
|
||||
return true;
|
||||
@ -328,15 +345,21 @@ bool save_script_binary(const ScriptHandle &script, cryptonote::blobdata &blob,
|
||||
|
||||
static bool load_contents_if_reference(std::string &dst)
|
||||
{
|
||||
if (dst.empty() || dst[0] != '@')
|
||||
if (dst.empty())
|
||||
return true;
|
||||
const std::string filename = dst.substr(1);
|
||||
const bool raw = starting_with(dst, "@raw:");
|
||||
const bool hex = starting_with(dst, "@hex:");
|
||||
if (!raw && !hex)
|
||||
return true;
|
||||
const std::string filename = dst.substr(5);
|
||||
std::string str;
|
||||
if (!epee::file_io_utils::load_file_to_string(filename, str))
|
||||
{
|
||||
MERROR("File not found: " << filename);
|
||||
return false;
|
||||
}
|
||||
if (hex)
|
||||
str = epee::string_tools::buff_to_hex_nodelimer(str);
|
||||
dst = std::move(str);
|
||||
return true;
|
||||
}
|
||||
@ -349,18 +372,7 @@ static bool set_unresolved_reference(std::string &unresolved_reference, const st
|
||||
|
||||
bool resolve_references(ScriptHandle &s, std::string &unresolved_reference)
|
||||
{
|
||||
if (!load_contents_if_reference(s.script->icon))
|
||||
return set_unresolved_reference(unresolved_reference, s.script->icon);
|
||||
for (auto &state: s.script->states)
|
||||
{
|
||||
if (!load_contents_if_reference(state.image))
|
||||
return set_unresolved_reference(unresolved_reference, state.image);
|
||||
if (!load_contents_if_reference(state.ui))
|
||||
return set_unresolved_reference(unresolved_reference, state.ui);
|
||||
if (!load_contents_if_reference(state.choices_ui))
|
||||
return set_unresolved_reference(unresolved_reference, state.choices_ui);
|
||||
}
|
||||
return true;
|
||||
return foreach_blob_candidate(s.script, [&unresolved_reference](std::string &s){ if (!load_contents_if_reference(s)) return set_unresolved_reference(unresolved_reference, s); return true; });
|
||||
}
|
||||
|
||||
static bool load_contents_if_reference(const cryptonote::BlockchainDB &db, std::string &s)
|
||||
@ -378,53 +390,49 @@ static bool load_contents_if_reference(const cryptonote::BlockchainDB &db, std::
|
||||
|
||||
bool resolve_references(const cryptonote::BlockchainDB &db, ScriptHandle &s)
|
||||
{
|
||||
if (!load_contents_if_reference(db, s.script->icon))
|
||||
return false;
|
||||
for (auto &state: s.script->states)
|
||||
{
|
||||
if (!load_contents_if_reference(db, state.image))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return foreach_blob_candidate(s.script, [&db](std::string &s){ return load_contents_if_reference(db, s); });
|
||||
}
|
||||
|
||||
bool resolve_reference(const cryptonote::BlockchainDB &db, std::string &s)
|
||||
{
|
||||
return load_contents_if_reference(db, s);
|
||||
}
|
||||
|
||||
static bool get_contents_if_reference(std::string &s, std::vector<cryptonote::blobdata> &references)
|
||||
{
|
||||
if (s.empty() || s[0] != '@')
|
||||
if (s.empty())
|
||||
return true;
|
||||
const std::string filename = s.substr(1);
|
||||
const bool raw = starting_with(s, "@raw:");
|
||||
const bool hex = starting_with(s, "@hex:");
|
||||
if (!raw && !hex)
|
||||
return true;
|
||||
const std::string filename = s.substr(5);
|
||||
std::string str;
|
||||
if (!epee::file_io_utils::load_file_to_string(filename, str))
|
||||
{
|
||||
MERROR("File not found: " << filename);
|
||||
return false;
|
||||
}
|
||||
if (hex)
|
||||
str = epee::string_tools::buff_to_hex_nodelimer(str);
|
||||
const crypto::hash hash = get_blob_hash(str);
|
||||
s.assign((const char*)&hash, sizeof(hash));
|
||||
references.push_back(std::move(str));
|
||||
if (std::find(references.begin(), references.end(), str) == references.end())
|
||||
references.push_back(std::move(str));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_references(ScriptHandle &s, std::vector<cryptonote::blobdata> &references, std::string &unresolved_reference)
|
||||
{
|
||||
if (!get_contents_if_reference(s.script->icon, references))
|
||||
return set_unresolved_reference(unresolved_reference, s.script->icon);
|
||||
for (auto &state: s.script->states)
|
||||
{
|
||||
if (!get_contents_if_reference(state.image, references))
|
||||
return set_unresolved_reference(unresolved_reference, state.image);
|
||||
}
|
||||
return true;
|
||||
Script copy = *s.script;
|
||||
return foreach_blob_candidate(©, [&references, &unresolved_reference](std::string &s){ if (!get_contents_if_reference(s, references)) return set_unresolved_reference(unresolved_reference, s); return true; });
|
||||
}
|
||||
|
||||
bool is_blob_used(const ScriptHandle &s, const crypto::hash hash)
|
||||
bool is_blob_used(const ScriptHandle &s, const crypto::hash &hash)
|
||||
{
|
||||
if (s.script->icon.size() == 32 && *(const crypto::hash*)s.script->icon.data() == hash)
|
||||
return true;
|
||||
for (auto &state: s.script->states)
|
||||
if (state.image.size() == 32 && *(const crypto::hash*)state.image.data() == hash)
|
||||
return true;
|
||||
return false;
|
||||
bool used = false;
|
||||
foreach_blob_candidate(s.script, [&hash, &used](std::string &s){ if (s.size() == 32 && *(const crypto::hash*)s.data() == hash) used = true; return true; });
|
||||
return used;
|
||||
}
|
||||
|
||||
uint32_t get_script_owner(const ScriptHandle &script)
|
||||
|
@ -136,8 +136,9 @@ bool save_script_native(const ScriptHandle &s, std::string &source, uint32_t fla
|
||||
crypto::hash get_blob_hash(const cryptonote::blobdata &blob);
|
||||
bool resolve_references(ScriptHandle &s, std::string &unresolved_reference);
|
||||
bool resolve_references(const cryptonote::BlockchainDB &db, ScriptHandle &s);
|
||||
bool resolve_reference(const cryptonote::BlockchainDB &db, std::string &s);
|
||||
bool get_references(ScriptHandle &s, std::vector<cryptonote::blobdata> &references, std::string &unresolved_reference);
|
||||
bool is_blob_used(const ScriptHandle &s, const crypto::hash hash);
|
||||
bool is_blob_used(const ScriptHandle &s, const crypto::hash &hash);
|
||||
uint32_t get_script_owner(const ScriptHandle &script);
|
||||
std::string get_script_name(const ScriptHandle &script);
|
||||
std::string get_script_name(const std::string &blob);
|
||||
|
@ -181,7 +181,7 @@ state_content: TEXT STRING { set_partial_state_state_text(state, $2); }
|
||||
| UI STRING { set_partial_state_state_ui(state, $2); }
|
||||
| CHOICES UI STRING { set_partial_state_state_choices_ui(state, $3); }
|
||||
| CHOICE '{' choice_contents '}' { set_partial_state_choice(state); }
|
||||
| ACTIONS '{' actions '}' { set_partial_state_state_init_actions(state); }
|
||||
| INIT '{' actions '}' { set_partial_state_state_init_actions(state); }
|
||||
;
|
||||
|
||||
choice_contents: choice_contents choice_content
|
||||
|
@ -79,10 +79,13 @@ void UINewScriptDialog::Parse(const std::shared_ptr<GameWallet> &w)
|
||||
okWidget->SetState(WIDGET_STATE_DISABLED, false);
|
||||
validContainer->SetValue(1);
|
||||
|
||||
std::string blob;
|
||||
blob.clear();
|
||||
shared_blobs.clear();
|
||||
new_blobs.clear();
|
||||
std::vector<cryptonote::blobdata> blobs;
|
||||
|
||||
cc::script::save_script_binary(script, blob);
|
||||
const size_t size = blob.size();
|
||||
std::vector<cryptonote::blobdata> blobs;
|
||||
cc::script::save_script_binary(script, blob, &blobs);
|
||||
std::vector<crypto::hash> hashes;
|
||||
hashes.reserve(blob.size());
|
||||
@ -93,8 +96,18 @@ void UINewScriptDialog::Parse(const std::shared_ptr<GameWallet> &w)
|
||||
{
|
||||
size_t marginal_size = size;
|
||||
for (size_t i = 0; i < refs.size(); ++i)
|
||||
{
|
||||
marginal_size += sizeof(crypto::hash);
|
||||
if (refs[i] > 0)
|
||||
marginal_size -= blobs[i].size() - sizeof(crypto::hash);
|
||||
{
|
||||
marginal_size -= blobs[i].size();
|
||||
shared_blobs.push_back(blobs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
new_blobs.push_back(blobs[i]);
|
||||
}
|
||||
}
|
||||
const std::string msg = " - " + std::to_string(marginal_size) + " bytes (+" + std::to_string(size - marginal_size) + " shared)";
|
||||
sizeWidget->SetText(msg.c_str());
|
||||
}
|
||||
@ -153,11 +166,8 @@ void UINewScriptDialog::HandleConfirmation(StringHash eventType, VariantMap& eve
|
||||
return;
|
||||
}
|
||||
|
||||
std::string blob;
|
||||
cc::script::save_script_binary(script, blob);
|
||||
|
||||
VariantMap& newEventData = GetEventDataMap();
|
||||
newEventData[NewScriptOkayed::P_BLOBS] = &references;
|
||||
newEventData[NewScriptOkayed::P_BLOBS] = &new_blobs;
|
||||
newEventData[NewScriptOkayed::P_SCRIPT] = String(blob.data(), blob.size());
|
||||
SendEvent(E_NEW_SCRIPT_OKAYED, newEventData);
|
||||
|
||||
|
@ -59,6 +59,10 @@ private:
|
||||
tb::TBButton *okWidget;
|
||||
tb::TBToggleContainer *validContainer;
|
||||
tb::TBToggleContainer *editContainer;
|
||||
|
||||
cryptonote::blobdata blob;
|
||||
std::vector<cryptonote::blobdata> shared_blobs;
|
||||
std::vector<cryptonote::blobdata> new_blobs;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -269,12 +269,8 @@ void UIScriptsDialog::FillScriptList(const std::shared_ptr<GameWallet> &w)
|
||||
}
|
||||
}
|
||||
|
||||
void UIScriptsDialog::ProcessOverrides(tb::TBWidget *w, const std::vector<std::tuple<uint8_t, uint32_t, std::string>> &overrides)
|
||||
void UIScriptsDialog::ProcessOverrides(std::string &s, const std::vector<std::tuple<uint8_t, uint32_t, std::string>> &overrides)
|
||||
{
|
||||
if (!w)
|
||||
return;
|
||||
|
||||
std::string s = w->GetText().CStr();
|
||||
for (const auto &e: overrides)
|
||||
{
|
||||
if (std::get<0>(e) == cc::script::override_string)
|
||||
@ -283,6 +279,15 @@ void UIScriptsDialog::ProcessOverrides(tb::TBWidget *w, const std::vector<std::t
|
||||
boost::replace_all(s, code, std::get<2>(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIScriptsDialog::ProcessOverrides(tb::TBWidget *w, const std::vector<std::tuple<uint8_t, uint32_t, std::string>> &overrides)
|
||||
{
|
||||
if (!w)
|
||||
return;
|
||||
|
||||
std::string s = w->GetText().CStr();
|
||||
ProcessOverrides(s, overrides);
|
||||
w->SetText(s.c_str());
|
||||
|
||||
for (w = w->GetFirstChild(); w; w = w->GetNext())
|
||||
@ -331,7 +336,11 @@ void UIScriptsDialog::FillChoiceList(const std::shared_ptr<GameWallet> &w)
|
||||
if (!image.empty())
|
||||
{
|
||||
TBImageWidget *imageWidget = new TBImageWidget();
|
||||
imageWidget->SetImage(image.data(), image.size());
|
||||
std::string decoded_image;
|
||||
if (epee::string_tools::parse_hexstr_to_binbuff(image, decoded_image))
|
||||
imageWidget->SetImage(decoded_image.data(), decoded_image.size());
|
||||
else
|
||||
printf("Failed to decode image\n");
|
||||
choicesLayout->AddChild(imageWidget);
|
||||
}
|
||||
|
||||
@ -352,6 +361,7 @@ void UIScriptsDialog::FillChoiceList(const std::shared_ptr<GameWallet> &w)
|
||||
|
||||
if (!ui.empty())
|
||||
{
|
||||
ProcessOverrides(ui, overrides);
|
||||
if (!g_widgets_reader->LoadData(choicesLayout, ui.c_str()))
|
||||
{
|
||||
new MessageBox(context_, "Failed to parse UI");
|
||||
@ -361,15 +371,16 @@ void UIScriptsDialog::FillChoiceList(const std::shared_ptr<GameWallet> &w)
|
||||
|
||||
if (!choices_ui.empty())
|
||||
{
|
||||
ProcessOverrides(choices_ui, overrides);
|
||||
if (!g_widgets_reader->LoadData(choicesLayout, choices_ui.c_str()))
|
||||
{
|
||||
new MessageBox(context_, "Failed to parse choices UI");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
{
|
||||
if (choices.empty())
|
||||
if (choices.empty() && choices_ui.empty())
|
||||
{
|
||||
TBButton *button = new TBButton();
|
||||
button->data.SetInt(0);
|
||||
@ -377,8 +388,10 @@ void UIScriptsDialog::FillChoiceList(const std::shared_ptr<GameWallet> &w)
|
||||
button->SetText("End");
|
||||
choicesLayout->AddChild(button);
|
||||
}
|
||||
else for (const auto &e: choices)
|
||||
for (const auto &e: choices)
|
||||
{
|
||||
if (std::get<1>(e).empty())
|
||||
continue;
|
||||
TBButton *button = new TBButton();
|
||||
button->data.SetInt(std::get<0>(e));
|
||||
button->SetID(TBIDC("choice"));
|
||||
@ -387,7 +400,6 @@ void UIScriptsDialog::FillChoiceList(const std::shared_ptr<GameWallet> &w)
|
||||
}
|
||||
}
|
||||
|
||||
ProcessOverrides(choicesLayout, overrides);
|
||||
ProcessControlCodes(choicesLayout);
|
||||
ProcessWidgetValues(choicesLayout);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ private:
|
||||
void FillScriptList(const std::shared_ptr<GameWallet> &w);
|
||||
void FillChoiceList(const std::shared_ptr<GameWallet> &w);
|
||||
void UpdateDetailsVisibility();
|
||||
void ProcessOverrides(std::string &s, const std::vector<std::tuple<uint8_t, uint32_t, std::string>> &overrides);
|
||||
void ProcessOverrides(tb::TBWidget *w, const std::vector<std::tuple<uint8_t, uint32_t, std::string>> &overrides);
|
||||
void ProcessControlCodes(tb::TBWidget *w);
|
||||
void ProcessWidgetValues(tb::TBWidget *w);
|
||||
|
@ -5109,6 +5109,21 @@ namespace cryptonote
|
||||
res.desc = std::move(sd.desc);
|
||||
res.owner = sd.owner;
|
||||
res.is_public = sd.is_public;
|
||||
|
||||
cc::script::ScriptHandle script;
|
||||
if (!cc::script::load_script_binary(sd.blob, script))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Error parsing script";
|
||||
return false;
|
||||
}
|
||||
if (!cc::script::resolve_references(db, script))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Error reconstructing script";
|
||||
return false;
|
||||
}
|
||||
|
||||
res.blob = epee::string_tools::buff_to_hex_nodelimer(sd.blob);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@ -5145,15 +5160,25 @@ namespace cryptonote
|
||||
error_resp.message = "Error parsing script";
|
||||
return false;
|
||||
}
|
||||
if (!cc::script::resolve_references(db, script))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Error reconstructing script";
|
||||
return false;
|
||||
}
|
||||
|
||||
cc::BlockchainStateProxy proxy(db);
|
||||
std::vector<std::tuple<uint32_t, std::string, std::string>> choices;
|
||||
if (!cc::script::get_state(proxy, script, req.account, req.state, req.city, res.ui, res.text, res.image, choices, res.choices_ui))
|
||||
std::string image, ui, choices_ui;
|
||||
if (!cc::script::get_state(proxy, script, req.account, req.state, req.city, ui, res.text, image, choices, choices_ui))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Error getting script state";
|
||||
return false;
|
||||
}
|
||||
res.image = epee::string_tools::buff_to_hex_nodelimer(image);
|
||||
res.ui = epee::string_tools::buff_to_hex_nodelimer(ui);
|
||||
res.choices_ui = epee::string_tools::buff_to_hex_nodelimer(choices_ui);
|
||||
res.choices.reserve(choices.size());
|
||||
for (const auto &e: choices)
|
||||
res.choices.push_back({std::get<0>(e), std::get<1>(e), std::get<2>(e)});
|
||||
|
@ -838,15 +838,18 @@ bool wallet2::get_cc_script_state(uint32_t account, uint32_t script, uint32_t st
|
||||
if (!r || res.status != CORE_RPC_STATUS_OK)
|
||||
return false;
|
||||
|
||||
ui = std::move(res.ui);
|
||||
if (!res.ui.empty() && !epee::string_tools::parse_hexstr_to_binbuff(res.ui, ui))
|
||||
return false;
|
||||
if (!res.choices_ui.empty() && !epee::string_tools::parse_hexstr_to_binbuff(res.choices_ui, choices_ui))
|
||||
return false;
|
||||
if (!res.image.empty() && !epee::string_tools::parse_hexstr_to_binbuff(res.image, image))
|
||||
return false;
|
||||
text = std::move(res.text);
|
||||
image = std::move(res.image);
|
||||
choices.resize(res.choices.size());
|
||||
for (size_t i = 0; i < res.choices.size(); ++i)
|
||||
{
|
||||
choices[i] = std::make_tuple(res.choices[i].id, std::move(res.choices[i].text), std::move(res.choices[i].selected_text));
|
||||
}
|
||||
choices_ui = std::move(res.choices_ui);
|
||||
overrides.resize(res.overrides.size());
|
||||
for (size_t i = 0; i < res.overrides.size(); ++i)
|
||||
overrides[i] = std::make_tuple(res.overrides[i].type, res.overrides[i].idx, std::move(res.overrides[i].str));
|
||||
|
Loading…
Reference in New Issue
Block a user