Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions inkcpp/globals_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,20 +285,27 @@ const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loa
);
for (size_t i = 0; i < old_capacity; ++i) {
hash_t path;
ptr = snap_read(ptr, path);
ptr = snap_read(ptr, path);
if (! loader.migratable) {
// Not migrating, so this is the story the snapshot was taken from and the counts were loaded
// in container order. The stored hash only confirms that; it cannot be used to look the
// container up again. A tracked container without a name of its own (a sequence) stores
// hash 0, and named containers below an indexed one can share a name, so the lookup finds
// nothing or a different container.
inkAssert(
_owner->container_data(i)._hash == path,
"Snapshot does not match this story: tracked container %u differs.", ( unsigned ) i
);
continue;
}
container_t c_id = ~0U;
ip_t container_ip = _owner->find_offset_for(path);
bool found = container_ip != nullptr
&& _owner->find_container_id(
static_cast<uint32_t>(container_ip - _owner->instructions()), c_id
);
if (! loader.migratable) {
inkAssert(found, "Invalid container id reference.");
inkAssert(c_id == i, "tracked containere are not allowed to move, expect we migrate");
} else {
if (found) {
_visit_counts.set(c_id, _visit_counts.get_old(i));
}
if (found) {
_visit_counts.set(c_id, _visit_counts.get_old(i));
}
}
if (loader.migratable) {
Expand Down
35 changes: 35 additions & 0 deletions inkcpp_test/Fixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -767,3 +767,38 @@ SCENARIO(
}
}
}

SCENARIO(
"snapshot restores visit counts of unnamed and same-named containers",
"[regression][snapshot][runtime]"
)
{
// The sequence's container is tracked but has no name of its own, and the labelled gather sits
// below a choice's indexed container. Restoring a snapshot used to look each tracked container up
// again by name, which found nothing for the first and could find another container for the
// second, and threw.
GIVEN("a story with a sequence and a labelled gather inside a choice")
{
std::unique_ptr<story> ink{story::from_file(INK_TEST_RESOURCE_DIR "SnapshotVisitCounts.bin")};
runner thread = ink->new_runner();

WHEN("it is played to the first choice and a snapshot is taken")
{
std::string text = thread->getall();
REQUIRE(text == "First visit.\n");
REQUIRE(thread->num_choices() == 2);
std::unique_ptr<snapshot> snap{thread->create_snapshot()};

THEN("the snapshot loads into a new runner, which continues the sequence")
{
runner loaded;
REQUIRE_NOTHROW(loaded = ink->new_runner_from_snapshot(*snap));
REQUIRE(loaded->num_choices() == 2);
loaded->choose(0);
REQUIRE(loaded->getall() == "Inside.\n");
loaded->choose(0);
REQUIRE(loaded->getall() == "Later visit.\n");
}
}
}
}
7 changes: 7 additions & 0 deletions inkcpp_test/ink/SnapshotVisitCounts.ink
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-> knot
=== knot ===
{First visit.|Later visit.}
* [Again]
- - (inner) Inside.
** [Back] -> knot
* [Leave] -> END
Loading