mirror of
https://github.com/Awakenchan/LanternEngine.git
synced 2026-11-10 01:11:45 -10:00
189 lines
8.8 KiB
C++
189 lines
8.8 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#include "gpu/shim/engine/world_copy.h"
|
|
#include <cstdio>
|
|
#include <map>
|
|
|
|
namespace {
|
|
int failures = 0;
|
|
|
|
void Check(bool ok, const char* what) {
|
|
if (ok) return;
|
|
std::printf("FAIL: %s\n", what);
|
|
++failures;
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
using namespace BbEngine::WorldCopy;
|
|
|
|
Check(ShareGroup(12400) && ShareGroup(72410) && ShareGroup(52300), "doors, lamps and pickups are shared");
|
|
Check(ShareGroup(0) && ShareGroup(1) && ShareGroup(2) && ShareGroup(9), "the world's global blocks are shared");
|
|
Check(!ShareGroup(3) && !ShareGroup(4) && !ShareGroup(5), "death and load flags stay each hunter's");
|
|
Check(!ShareGroup(6) && !ShareGroup(7) && !ShareGroup(8), "the personal blocks stay each hunter's");
|
|
Check(!ShareGroup(12404) && !ShareGroup(12405) && !ShareGroup(12406), "a fight's own state stays out");
|
|
Check(!ShareGroup(12100) && !ShareGroup(72101), "the Dream stays each hunter's");
|
|
Check(!ShareGroup(12900) && !ShareGroup(72900), "dungeons have their own sharing");
|
|
Check(!ShareGroup(60009), "the item ledger stays out");
|
|
|
|
Check(!ShareFlag(21) && !ShareFlag(23), "ending flags are never copied");
|
|
Check(!ShareFlag(2010) && ShareFlag(2410), "session flags stay, area gates go");
|
|
Check(!ShareFlag(9022) && !ShareFlag(9180) && !ShareFlag(9450) && !ShareFlag(9550) && !ShareFlag(9901), "personal flags in block 9 stay");
|
|
Check(ShareFlag(9801) && ShareFlag(9400), "time of day and world flags in block 9 go");
|
|
Check(!ShareFlag(70009300) && ShareFlag(70009700), "shop stock is per hunter");
|
|
Check(ShareFlag(12401800) && !ShareFlag(12404800), "boss deaths go, the fight's state stays");
|
|
|
|
const Group mask = MaskOf(9);
|
|
Check((mask[0] & 0x80) != 0, "mask keeps 9000");
|
|
Check((mask[2] & (0x80 >> 4)) == 0, "mask drops 9020");
|
|
|
|
{
|
|
const Group bits = RangeBits(72400, 72400096, 72400115);
|
|
Check(bits[12] == 0xff && bits[13] == 0xff && bits[14] == 0xf0 && bits[11] == 0, "a quest block's bits in its group");
|
|
Check(IsZero(RangeBits(72400, 1000, 1019)) && IsZero(RangeBits(72400, 72401000, 72401019)), "a block of another group has no bits here");
|
|
Check(RangeBits(72400, 72399990, 72400001)[0] == 0xc0, "a block that starts before the group");
|
|
Group own{}, overlay{};
|
|
own[12] = 0xa0;
|
|
overlay[12] = 0x5f;
|
|
overlay[20] = 0x33;
|
|
Check(CopyMasked(overlay.data(), own.data(), bits) == 1 && overlay[12] == 0xa0 && overlay[20] == 0x33, "a guest's own step goes in the overlay, the host's flags beside it stay");
|
|
overlay[13] = 0x80;
|
|
own[20] = 0x11;
|
|
Check(CopyMasked(own.data(), overlay.data(), bits) == 1 && own[13] == 0x80 && own[20] == 0x11, "a guest's own step goes back to its save, nothing else does");
|
|
Check(CopyMasked(own.data(), overlay.data(), bits) == 0, "nothing new, nothing written");
|
|
overlay[13] = 0;
|
|
CopyMasked(own.data(), overlay.data(), bits);
|
|
Check(own[13] == 0, "a quest step that clears a flag goes back too");
|
|
Group shared{};
|
|
shared.fill(0xff);
|
|
Remove(shared, bits);
|
|
Check(shared[12] == 0 && shared[14] == 0x0f && shared[11] == 0xff, "the host's copy leaves a guest's own bits out");
|
|
Group both = bits;
|
|
Group more{};
|
|
more[0] = 0x01;
|
|
Add(both, more);
|
|
Check(both[0] == 0x01 && both[12] == 0xff, "own bits add up");
|
|
Check(IsPickupGroup(52400) && IsPickupGroup(50000) && !IsPickupGroup(12400), "pickup flags are type 5");
|
|
Check(IsNpcGroup(1) && IsNpcGroup(72400) && IsNpcGroup(73600) && !IsNpcGroup(0) && !IsNpcGroup(2) && !IsNpcGroup(12400) && !IsNpcGroup(52400),
|
|
"NPC state 1000-1999 and the type 7 talk blocks are a hunter's own");
|
|
}
|
|
|
|
Group own{}, host{}, full{};
|
|
full.fill(0xff);
|
|
own[0] = 0x0f;
|
|
host[0] = 0xf0;
|
|
Check(WriteShared(own.data(), host, full) == 1 && own[0] == 0xf0, "a whole write takes the host's bits");
|
|
Group partial{};
|
|
partial[0] = 0xf0;
|
|
Group target{};
|
|
target[0] = 0x0f;
|
|
WriteShared(target.data(), host, partial);
|
|
Check(target[0] == 0xff, "bits out of the mask stay");
|
|
|
|
Group guest{}, was{}, now{};
|
|
guest[1] = 0x01;
|
|
now[1] = 0x80;
|
|
Check(WriteChanged(guest.data(), was, now, full) == 1 && guest[1] == 0x81, "a live change keeps the guest's own bits");
|
|
was = now;
|
|
now[1] = 0;
|
|
WriteChanged(guest.data(), was, now, full);
|
|
Check(guest[1] == 0x01, "a live clear only clears what the host cleared");
|
|
|
|
Image groups;
|
|
for (std::uint32_t i = 0; i < 130; ++i) {
|
|
Group g{};
|
|
g[0] = std::uint8_t(i + 1);
|
|
groups[12400 + 0] = g;
|
|
groups[52400 + i] = g;
|
|
}
|
|
const auto frames = Encode(groups, true);
|
|
Check(frames.size() == 3, "130 groups and one more take 3 frames");
|
|
Check(frames.front()[0] == kFirstPart && frames.back()[0] == kLastPart, "the whole image is marked");
|
|
Receiver in;
|
|
Image changed;
|
|
for (const auto& f : frames) Check(in.Take(f.data(), f.size(), changed), "a frame reads");
|
|
Check(in.Have() && in.Current().size() == groups.size() && changed.size() == groups.size(), "the whole image arrives");
|
|
|
|
Image one;
|
|
Group g{};
|
|
g[3] = 9;
|
|
one[12400] = g;
|
|
const auto delta = Encode(one, false);
|
|
Check(delta.size() == 1 && delta[0][0] == 0, "a delta is one plain frame");
|
|
changed.clear();
|
|
in.Take(delta[0].data(), delta[0].size(), changed);
|
|
Check(changed.size() == 1 && in.Value(12400)[3] == 9, "a delta changes one group");
|
|
changed.clear();
|
|
in.Take(delta[0].data(), delta[0].size(), changed);
|
|
Check(changed.empty(), "the same delta changes nothing");
|
|
|
|
Image smaller;
|
|
smaller[12400] = g;
|
|
const auto again = Encode(smaller, true);
|
|
changed.clear();
|
|
in.Take(again[0].data(), again[0].size(), changed);
|
|
Check(in.Current().size() == 1, "a newer whole image drops what it lacks");
|
|
Check(changed.size() == groups.size() - 1 && IsZero(changed.begin()->second), "what it lacks reads as cleared");
|
|
|
|
const Image none;
|
|
const auto empty = Encode(none, true);
|
|
Check(empty.size() == 1 && empty[0][0] == (kFirstPart | kLastPart), "an empty world is still a whole image");
|
|
changed.clear();
|
|
in.Take(empty[0].data(), empty[0].size(), changed);
|
|
Check(in.Current().empty(), "an empty image clears");
|
|
|
|
const std::uint8_t cut[3] = {0, 1, 2};
|
|
Check(!in.Take(cut, sizeof cut, changed), "a cut frame is refused");
|
|
Image bad;
|
|
std::vector<std::uint8_t> sneaky(1 + kEntryBytes);
|
|
const std::uint32_t personal = 6;
|
|
std::memcpy(sneaky.data() + 1, &personal, 4);
|
|
changed.clear();
|
|
in.Take(sneaky.data(), sneaky.size(), changed);
|
|
Check(changed.empty(), "a group that isn't shared is ignored");
|
|
|
|
Check(IsGiftFlag(9040) && IsGiftFlag(9045) && IsGiftFlag(9077) && !IsGiftFlag(9039) && !IsGiftFlag(9080), "the gift request flags of common events 9040 and 9100");
|
|
Check(ShareFlag(9045), "a gift flag sits in a shared block, so it has to be taken out as a hunter's own");
|
|
{
|
|
const Group gifts = RangeBits(9, kGiftFirst, kGiftLast);
|
|
Check(CountBits(gifts) == 40 && (gifts[5] & 0x80) != 0 && (gifts[4] & 0x01) == 0, "40 gift bits from 9040");
|
|
}
|
|
{
|
|
Group was{}, now{}, mask{};
|
|
mask[0] = 0xf0;
|
|
was[0] = 0x80;
|
|
now[0] = 0xc8;
|
|
const Group on = TurnedOn(was, now, mask);
|
|
Check(on[0] == 0x40 && CountBits(on) == 1, "only a bit that was off, is on and is watched counts as turned on");
|
|
std::uint8_t save[kGroupBytes] = {};
|
|
Check(SetBits(save, on) == 1 && save[0] == 0x40 && SetBits(save, on) == 0, "a witnessed flag is written once");
|
|
}
|
|
{
|
|
Witness witness;
|
|
std::map<std::uint32_t, Group> masks;
|
|
masks[12400].fill(0xff);
|
|
Image first;
|
|
first[12400][0] = 0x80;
|
|
Check(!witness.Ready(), "a witness starts with no baseline");
|
|
witness.Start(first);
|
|
bool rebuilt = true;
|
|
Check(witness.Step(first, masks, rebuilt).empty() && !rebuilt, "what was on at the baseline is not witnessed");
|
|
Image door = first;
|
|
door[12400][1] = 0x01;
|
|
const Image fresh = witness.Step(door, masks, rebuilt);
|
|
Check(fresh.size() == 1 && fresh.at(12400)[1] == 0x01 && fresh.at(12400)[0] == 0 && !rebuilt, "a door opened with the hunter there is witnessed");
|
|
Check(witness.Step(door, masks, rebuilt).empty(), "and only once");
|
|
Image image = door;
|
|
image[12400].fill(0xff);
|
|
Check(witness.Step(image, masks, rebuilt).empty() && rebuilt, "many flags at once are an image arriving, not play");
|
|
Image other = image;
|
|
other[52400][0] = 0x80;
|
|
Check(witness.Step(other, masks, rebuilt).empty() && !rebuilt, "a group that is not watched is never witnessed");
|
|
witness.Forget();
|
|
Check(!witness.Ready(), "a new copy of the world needs a new baseline");
|
|
}
|
|
|
|
if (failures) return 1;
|
|
std::printf("world-copy-test: ok\n");
|
|
return 0;
|
|
}
|