Complain about missing custom elements when opening online saves

The complaint manifests as a button in the bottom right corner of the half-size preview.

Also convert the loading error popup to such a button.
This commit is contained in:
Tamás Bálint Misius 2023-05-13 16:00:36 +02:00
parent 374209eebe
commit 36800a76cd
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
8 changed files with 99 additions and 34 deletions

View File

@ -29,7 +29,7 @@ ThumbnailRendererTask::~ThumbnailRendererTask()
bool ThumbnailRendererTask::doWork() bool ThumbnailRendererTask::doWork()
{ {
thumbnail = std::unique_ptr<VideoBuffer>(SaveRenderer::Ref().Render(save.get(), decorations, fire)); std::tie(thumbnail, std::ignore) = SaveRenderer::Ref().Render(save.get(), decorations, fire);
if (thumbnail) if (thumbnail)
{ {
thumbnail->ResizeToFit(size, true); thumbnail->ResizeToFit(size, true);

View File

@ -1958,7 +1958,7 @@ void GameView::NotifyTransformedPlaceSaveChanged(GameModel *sender)
{ {
if (sender->GetTransformedPlaceSave()) if (sender->GetTransformedPlaceSave())
{ {
placeSaveThumb = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRenderer()); std::tie(placeSaveThumb, std::ignore) = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRenderer());
selectMode = PlaceSave; selectMode = PlaceSave;
selectPoint2 = mousePosition; selectPoint2 = mousePosition;
} }

View File

@ -93,6 +93,22 @@ PreviewView::PreviewView(std::unique_ptr<VideoBuffer> newSavePreview):
browserOpenButton->SetActionCallback({ [this] { c->OpenInBrowser(); } }); browserOpenButton->SetActionCallback({ [this] { c->OpenInBrowser(); } });
AddComponent(browserOpenButton); AddComponent(browserOpenButton);
loadErrorButton = new ui::Button({ 0, 0 }, ui::Point(148, 19), "Error loading save");
loadErrorButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
loadErrorButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
loadErrorButton->SetIcon(IconDelete);
loadErrorButton->SetActionCallback({ [this] { ShowLoadError(); } });
loadErrorButton->Visible = false;
AddComponent(loadErrorButton);
missingElementsButton = new ui::Button({ 0, 0 }, ui::Point(148, 19), "Missing custom elements");
missingElementsButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
missingElementsButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
missingElementsButton->SetIcon(IconReport);
missingElementsButton->SetActionCallback({ [this] { ShowMissingCustomElements(); } });
missingElementsButton->Visible = false;
AddComponent(missingElementsButton);
if(showAvatars) if(showAvatars)
saveNameLabel = new ui::Label(ui::Point(39, (YRES/2)+4), ui::Point(100, 16), ""); saveNameLabel = new ui::Label(ui::Point(39, (YRES/2)+4), ui::Point(100, 16), "");
else else
@ -387,9 +403,9 @@ void PreviewView::OnTick(float dt)
c->Update(); c->Update();
if (doError) if (doError)
{ {
new ErrorMessage("Error loading save", doErrorMessage, { [this]() { openButton->Enabled = false;
c->Exit(); loadErrorButton->Visible = true;
} }); UpdateLoadStatus();
} }
if (reportSaveRequest && reportSaveRequest->CheckDone()) if (reportSaveRequest && reportSaveRequest->CheckDone())
@ -464,6 +480,36 @@ void PreviewView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ct
openButton->DoAction(); openButton->DoAction();
} }
void PreviewView::ShowLoadError()
{
new ErrorMessage("Error loading save", doErrorMessage, {});
}
void PreviewView::ShowMissingCustomElements()
{
StringBuilder sb;
sb << "This save uses custom elements that are not currently available. Make sure that you use the mod and/or have all the scripts the save requires to fully load. A list of identifiers of missing custom elements follows, which may help you determine how to fix this problem.\n";
for (auto &identifier : missingElementTypes)
{
sb << "\n - " << identifier.FromUtf8();
}
new InformationMessage("Missing custom elements", sb.Build(), true);
}
void PreviewView::UpdateLoadStatus()
{
auto y = YRES / 2 - 22;
auto showButton = [&y](ui::Button *button) {
if (button->Visible)
{
button->Position = { XRES / 2 - button->Size.X - 3, y };
y -= button->Size.Y + 3;
}
};
showButton(missingElementsButton);
showButton(loadErrorButton);
}
void PreviewView::NotifySaveChanged(PreviewModel * sender) void PreviewView::NotifySaveChanged(PreviewModel * sender)
{ {
auto *save = sender->GetSaveInfo(); auto *save = sender->GetSaveInfo();
@ -510,9 +556,11 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
if(save->GetGameSave()) if(save->GetGameSave())
{ {
savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true); std::tie(savePreview, missingElementTypes) = SaveRenderer::Ref().Render(save->GetGameSave(), false, true);
if (savePreview) if (savePreview)
savePreview->ResizeToFit(RES / 2, true); savePreview->ResizeToFit(RES / 2, true);
missingElementsButton->Visible = missingElementTypes.size();
UpdateLoadStatus();
} }
else if (!sender->GetCanOpen()) else if (!sender->GetCanOpen())
openButton->Enabled = false; openButton->Enabled = false;

View File

@ -26,25 +26,28 @@ class PreviewModel;
class PreviewController; class PreviewController;
class PreviewView: public ui::Window class PreviewView: public ui::Window
{ {
PreviewController * c; PreviewController *c{};
std::vector<ByteString> missingElementTypes;
std::unique_ptr<VideoBuffer> savePreview; std::unique_ptr<VideoBuffer> savePreview;
ui::Button * openButton; ui::Button *openButton{};
ui::Button * browserOpenButton; ui::Button *browserOpenButton{};
ui::Button * favButton; ui::Button *favButton{};
ui::Button * reportButton; ui::Button *reportButton{};
ui::Button * submitCommentButton; ui::Button *submitCommentButton{};
ui::Textbox * addCommentBox; ui::Button *loadErrorButton{};
ui::Label * commentWarningLabel; ui::Button *missingElementsButton{};
ui::Label * saveNameLabel; ui::Textbox *addCommentBox{};
ui::Label * authorDateLabel; ui::Label *commentWarningLabel{};
ui::AvatarButton * avatarButton; ui::Label *saveNameLabel{};
ui::Label * pageInfo; ui::Label *authorDateLabel{};
ui::Label * saveDescriptionLabel; ui::AvatarButton *avatarButton{};
ui::Label * viewsLabel; ui::Label *pageInfo{};
ui::Label * saveIDLabel; ui::Label *saveDescriptionLabel{};
ui::Label * saveIDLabel2; ui::Label *viewsLabel{};
ui::CopyTextButton * saveIDButton; ui::Label *saveIDLabel{};
ui::ScrollPanel * commentsPanel; ui::Label *saveIDLabel2{};
ui::CopyTextButton *saveIDButton{};
ui::ScrollPanel *commentsPanel{};
std::vector<ui::Component*> commentComponents; std::vector<ui::Component*> commentComponents;
std::vector<ui::Component*> commentTextComponents; std::vector<ui::Component*> commentTextComponents;
int votesUp; int votesUp;
@ -70,6 +73,9 @@ class PreviewView: public ui::Window
void submitComment(); void submitComment();
bool CheckSwearing(String text); bool CheckSwearing(String text);
void CheckComment(); void CheckComment();
void ShowMissingCustomElements();
void ShowLoadError();
void UpdateLoadStatus();
std::unique_ptr<http::AddCommentRequest> addCommentRequest; std::unique_ptr<http::AddCommentRequest> addCommentRequest;
std::unique_ptr<http::ReportSaveRequest> reportSaveRequest; std::unique_ptr<http::ReportSaveRequest> reportSaveRequest;

View File

@ -20,7 +20,7 @@ void SaveRenderer::Flush(int begin, int end)
std::fill(ren->graphicscache + begin, ren->graphicscache + end, gcache_item()); std::fill(ren->graphicscache + begin, ren->graphicscache + end, gcache_item());
} }
std::unique_ptr<VideoBuffer> SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource) std::pair<std::unique_ptr<VideoBuffer>, std::vector<ByteString>> SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource)
{ {
std::lock_guard<std::mutex> gx(renderMutex); std::lock_guard<std::mutex> gx(renderMutex);
@ -32,11 +32,9 @@ std::unique_ptr<VideoBuffer> SaveRenderer::Render(const GameSave *save, bool dec
ren->SetColourMode(renderModeSource->GetColourMode()); ren->SetColourMode(renderModeSource->GetColourMode());
} }
std::unique_ptr<VideoBuffer> tempThumb;
sim->clear_sim(); sim->clear_sim();
sim->Load(save, true, { 0, 0 }); auto missingElementTypes = sim->Load(save, true, { 0, 0 });
ren->decorations_enable = true; ren->decorations_enable = true;
ren->blackDecorations = !decorations; ren->blackDecorations = !decorations;
ren->ClearAccumulation(); ren->ClearAccumulation();
@ -57,10 +55,10 @@ std::unique_ptr<VideoBuffer> SaveRenderer::Render(const GameSave *save, bool dec
ren->RenderBegin(); ren->RenderBegin();
ren->RenderEnd(); ren->RenderEnd();
tempThumb = std::make_unique<VideoBuffer>(save->blockSize * CELL); auto tempThumb = std::make_unique<VideoBuffer>(save->blockSize * CELL);
tempThumb->BlendImage(ren->Data(), 0xFF, ren->Size().OriginRect()); tempThumb->BlendImage(ren->Data(), 0xFF, ren->Size().OriginRect());
return tempThumb; return { std::move(tempThumb), missingElementTypes };
} }
SaveRenderer::~SaveRenderer() SaveRenderer::~SaveRenderer()

View File

@ -1,7 +1,10 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <utility>
#include <vector>
#include "common/ExplicitSingleton.h" #include "common/ExplicitSingleton.h"
#include "common/String.h"
class GameSave; class GameSave;
class VideoBuffer; class VideoBuffer;
@ -15,7 +18,7 @@ class SaveRenderer: public ExplicitSingleton<SaveRenderer> {
std::mutex renderMutex; std::mutex renderMutex;
public: public:
SaveRenderer(); SaveRenderer();
std::unique_ptr<VideoBuffer> Render(const GameSave *save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr); std::pair<std::unique_ptr<VideoBuffer>, std::vector<ByteString>> Render(const GameSave *save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr);
void Flush(int begin, int end); void Flush(int begin, int end);
virtual ~SaveRenderer(); virtual ~SaveRenderer();
}; };

View File

@ -19,8 +19,9 @@ extern int Element_LOLZ_lolz[XRES/9][YRES/9];
extern int Element_LOVE_RuleTable[9][9]; extern int Element_LOVE_RuleTable[9][9];
extern int Element_LOVE_love[XRES/9][YRES/9]; extern int Element_LOVE_love[XRES/9][YRES/9];
void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> blockP) // block coordinates std::vector<ByteString> Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> blockP) // block coordinates
{ {
std::vector<ByteString> missingElementTypes;
auto partP = blockP * CELL; auto partP = blockP * CELL;
unsigned int pmapmask = (1<<save->pmapbits)-1; unsigned int pmapmask = (1<<save->pmapbits)-1;
@ -44,8 +45,15 @@ void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> bloc
// if this is a custom element, set the ID to the ID we found when comparing identifiers in the palette map // if this is a custom element, set the ID to the ID we found when comparing identifiers in the palette map
// set type to 0 if we couldn't find an element with that identifier present when loading, // set type to 0 if we couldn't find an element with that identifier present when loading,
// unless this is a default element, in which case keep the current ID, because otherwise when an element is renamed it wouldn't show up anymore in older saves // unless this is a default element, in which case keep the current ID, because otherwise when an element is renamed it wouldn't show up anymore in older saves
if (myId != 0 || !pi.first.BeginsWith("DEFAULT_PT_")) if (myId != 0)
{
partMap[pi.second] = myId; partMap[pi.second] = myId;
}
else if (!pi.first.BeginsWith("DEFAULT_PT_"))
{
missingElementTypes.push_back(pi.first);
partMap[pi.second] = myId;
}
} }
} }
} }
@ -318,6 +326,8 @@ void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> bloc
{ {
air->ApproximateBlockAirMaps(); air->ApproximateBlockAirMaps();
} }
return missingElementTypes;
} }
std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR) // particle coordinates std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR) // particle coordinates

View File

@ -121,7 +121,7 @@ public:
uint64_t frameCount; uint64_t frameCount;
bool ensureDeterminism; bool ensureDeterminism;
void Load(const GameSave *save, bool includePressure, Vec2<int> blockP); // block coordinates std::vector<ByteString> Load(const GameSave *save, bool includePressure, Vec2<int> blockP); // block coordinates
std::unique_ptr<GameSave> Save(bool includePressure, Rect<int> partR); // particle coordinates std::unique_ptr<GameSave> Save(bool includePressure, Rect<int> partR); // particle coordinates
void SaveSimOptions(GameSave &gameSave); void SaveSimOptions(GameSave &gameSave);
SimulationSample GetSample(int x, int y); SimulationSample GetSample(int x, int y);