Files
2025-09-18 17:55:52 -04:00

295 lines
11 KiB
C++

#include <boost/test/unit_test.hpp>
#include "Client.h"
#include "rbx/test/DataModelFixture.h"
#include "V8DataModel/DataModel.h"
#include "V8DataModel/Explosion.h"
#include "V8DataModel/MegaCluster.h"
using namespace RBX;
using namespace RBX::Voxel;
struct AutowedgeTestFixture {
static const Vector3int16 kStartingBlockMin, kStartingBlockMax;
static const Region3int16 kStartingBlockRegion;
DataModelFixture dm;
Workspace* workspace;
MegaClusterInstance* cluster;
AutowedgeTestFixture() {
DataModel::LegacyLock lock(&dm, RBX::DataModelJob::Write);
workspace = dm->getWorkspace();
workspace->createTerrain();
cluster = Instance::fastDynamicCast<MegaClusterInstance>(
workspace->getTerrain());
Cell solidVoxel;
solidVoxel.solid.setBlock(CELL_BLOCK_Solid);
solidVoxel.solid.setOrientation(CELL_ORIENTATION_NegZ);
// set one cell so that the cluster is allocated
cluster->getVoxelGrid()->setCell(Vector3int16(0, 0, 0),
solidVoxel, CELL_MATERIAL_Grass);
cluster->getVoxelGrid()->setCell(Vector3int16(0, 0, 0),
Constants::kUniqueEmptyCellRepresentation, CELL_MATERIAL_Water);
}
};
const Vector3int16 AutowedgeTestFixture::kStartingBlockMin(-2,16,-2);
const Vector3int16 AutowedgeTestFixture::kStartingBlockMax(2,17,2);
const Region3int16 AutowedgeTestFixture::kStartingBlockRegion(AutowedgeTestFixture::kStartingBlockMin, AutowedgeTestFixture::kStartingBlockMax);
static inline Cell createCell(CellBlock cellBlock, CellOrientation cellOrientation) {
Cell result;
result.solid.setBlock(cellBlock);
result.solid.setOrientation(cellOrientation);
return result;
}
static void createStartingBlock(AutowedgeTestFixture& fixture) {
fixture.cluster->setCellsScript(
AutowedgeTestFixture::kStartingBlockRegion,
CELL_MATERIAL_Grass, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
}
static void autowedgeStartingBlock(AutowedgeTestFixture& fixture) {
fixture.cluster->autoWedgeCellsScript(
AutowedgeTestFixture::kStartingBlockRegion);
}
typedef std::vector<std::vector<std::vector<Cell> > > ExpectedResult;
static void checkExpectedResult(const char* message,
const ExpectedResult& expected,
const AutowedgeTestFixture& actualSource) {
const int kOffset = 0;
const Vector3int16& min = AutowedgeTestFixture::kStartingBlockMin;
Vector3int16 delta = AutowedgeTestFixture::kStartingBlockMax - min;
int xTravel = 1 + delta.x;
int yTravel = 1 + delta.y;
int zTravel = 1 + delta.z;
for (int x = 0; x < xTravel; ++x) {
for (int y = 0; y < yTravel; ++y) {
for (int z = 0; z < zTravel; ++z) {
Cell actual = actualSource.cluster->getVoxelGrid()->getCell(min + Vector3int16(x + kOffset, y, z + kOffset));
char messageBuffer[256];
snprintf(messageBuffer, 256,
"%s @ (%d,%d,%d): actual != expected %d != %d",
message, min.x + x, min.y + y, min.z + z,
Cell::asUnsignedCharForDeprecatedUses(actual),
Cell::asUnsignedCharForDeprecatedUses(expected[x][y][z]));
BOOST_CHECK_MESSAGE(actual == expected[x][y][z], messageBuffer);
}
}
}
}
static void initializeExpectedResult(ExpectedResult& out) {
out.clear();
Vector3int16 delta = AutowedgeTestFixture::kStartingBlockMax -
AutowedgeTestFixture::kStartingBlockMin;
int xTravel = 1 + delta.x;
int yTravel = 1 + delta.y;
int zTravel = 1 + delta.z;
out.resize(xTravel);
for (int x = 0; x < xTravel; ++x) {
out[x].resize(yTravel);
for (int y = 0; y < yTravel; ++y) {
out[x][y].resize(zTravel);
for (int z = 0; z < zTravel; ++z) {
out[x][y][z] = createCell(CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
}
}
}
}
static void applyStandardExteriorWedging(ExpectedResult& out) {
out[0][0][0] = createCell(CELL_BLOCK_HorizontalWedge, CELL_ORIENTATION_NegX);
out[0][1][0] = createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_NegX);
out[0][0][4] = createCell(CELL_BLOCK_HorizontalWedge, CELL_ORIENTATION_NegZ);
out[0][1][4] = createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_NegZ);
out[4][0][0] = createCell(CELL_BLOCK_HorizontalWedge, CELL_ORIENTATION_Z);
out[4][1][0] = createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_Z);
out[4][0][4] = createCell(CELL_BLOCK_HorizontalWedge, CELL_ORIENTATION_X);
out[4][1][4] = createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_X);
for (int x = 1; x <= 3; ++x) {
out[x][1][4] = createCell(CELL_BLOCK_VerticalWedge, CELL_ORIENTATION_NegZ);
out[x][1][0] = createCell(CELL_BLOCK_VerticalWedge, CELL_ORIENTATION_Z);
out[0][1][x] = createCell(CELL_BLOCK_VerticalWedge, CELL_ORIENTATION_NegX);
out[4][1][x] = createCell(CELL_BLOCK_VerticalWedge, CELL_ORIENTATION_X);
}
}
static void fourCellSquareTest(const Vector3int16& minCorner) {
const int kOffset = 0;
const int minX = minCorner.x;
const int minY = minCorner.y;
const int minZ = minCorner.z;
AutowedgeTestFixture fixture;
fixture.cluster->setCellScript(minX + 1, minY, minZ + 1,
CELL_MATERIAL_Grass, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
fixture.cluster->setCellScript(minX + 1, minY, minZ,
CELL_MATERIAL_Grass, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
fixture.cluster->setCellScript(minX, minY, minZ + 1,
CELL_MATERIAL_Grass, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
fixture.cluster->setCellScript(minX, minY, minZ,
CELL_MATERIAL_Grass, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
const Vector3int16 minExt(minCorner - Vector3int16(10,10,10));
const Vector3int16 maxExt(minCorner + Vector3int16(10,10,10));
fixture.cluster->autoWedgeCellsScript(Region3int16(minExt, maxExt));
Cell expectedCells[4] = {
createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_NegX),
createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_Z),
createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_NegZ),
createCell(CELL_BLOCK_CornerWedge, CELL_ORIENTATION_X)
};
for (int y = minExt.y; y <= maxExt.y; ++y) {
for (int z = minExt.z; z <= maxExt.z; ++z) {
for (int x = minExt.x; x <= maxExt.x; ++x) {
if (x < minX || x > minX + 1 || z < minZ || z > minZ + 1 || y < minY || y > minY) {
BOOST_CHECK_EQUAL(CELL_MATERIAL_Deprecated_Empty,
fixture.cluster->getCellScript(x, y, z)->at(0).cast<CellMaterial>());
} else {
int index = (x - minX) + 2 * (z - minZ);
BOOST_REQUIRE_LE(index, 3);
BOOST_REQUIRE_GE(index, 0);
Cell expected = expectedCells[index];
Cell actual = fixture.cluster->getVoxelGrid()->getCell(Vector3int16(x + kOffset, y, z + kOffset));
char messageBuffer[256];
snprintf(messageBuffer, 256, "(%d,%d,%d) %d != %d", x, y, z,
Cell::asUnsignedCharForDeprecatedUses(expected),
Cell::asUnsignedCharForDeprecatedUses(actual));
BOOST_CHECK_MESSAGE(expected == actual, messageBuffer);
}
}
}
}
}
BOOST_AUTO_TEST_SUITE( AutowedgeTest )
BOOST_AUTO_TEST_CASE( AutowedgeStartingBlock )
{
AutowedgeTestFixture fixture;
createStartingBlock(fixture);
ExpectedResult result;
initializeExpectedResult(result);
checkExpectedResult("unwedged", result, fixture);
autowedgeStartingBlock(fixture);
initializeExpectedResult(result);
applyStandardExteriorWedging(result);
checkExpectedResult("wedged", result, fixture);
}
BOOST_AUTO_TEST_CASE( RemoveTopCenter ) {
AutowedgeTestFixture fixture;
createStartingBlock(fixture);
fixture.cluster->setCellScript(0, 17, 0, CELL_MATERIAL_Deprecated_Empty, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
autowedgeStartingBlock(fixture);
ExpectedResult result;
initializeExpectedResult(result);
applyStandardExteriorWedging(result);
result[2][1][2] = Constants::kUniqueEmptyCellRepresentation;
// get the vertical wedges from the outside for the sides of the hole
result[1][1][2] = result[4][1][2];
result[3][1][2] = result[0][1][2];
result[2][1][1] = result[2][1][4];
result[2][1][3] = result[2][1][0];
// get the corners of the interior hole
result[1][1][1] = createCell(CELL_BLOCK_InverseCornerWedge, CELL_ORIENTATION_X);
result[1][1][3] = createCell(CELL_BLOCK_InverseCornerWedge, CELL_ORIENTATION_Z);
result[3][1][3] = createCell(CELL_BLOCK_InverseCornerWedge, CELL_ORIENTATION_NegX);
result[3][1][1] = createCell(CELL_BLOCK_InverseCornerWedge, CELL_ORIENTATION_NegZ);
checkExpectedResult("wedged", result, fixture);
}
BOOST_AUTO_TEST_CASE( CornerOfTerrainWorks ) {
// sanity check: autowedge span entirely inside terrain
fourCellSquareTest(Vector3int16(20, 20, 20));
// max terrain extent
fourCellSquareTest(Vector3int16(253, 62, 253));
// min terrain extent
fourCellSquareTest(Vector3int16(-256, 0, -256));
}
BOOST_AUTO_TEST_CASE( BelowTerrainWorks ) {
bool check = true;
AutowedgeTestFixture fixture;
// ensure that this call doesn't crash
fixture.cluster->autoWedgeCellsScript(Region3int16(Vector3int16(-5,-20,-5), Vector3int16(5,-10,5)));
BOOST_REQUIRE(check);
}
BOOST_AUTO_TEST_CASE( ExplosionBelowTerrainWorks ) {
const int kOffset = 0;
bool check = true;
AutowedgeTestFixture fixture;
DataModel::LegacyLock lock(&fixture.dm, RBX::DataModelJob::Write);
shared_ptr<Explosion> exp = Creatable<Instance>::create<Explosion>();
exp->setBlastRadius(10.f);
Explosion::propPosition.setValue(exp.get(), Vector3(0,2,0));
fixture.cluster->setCellScript(0, 0, 0, CELL_MATERIAL_Grass, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
BOOST_REQUIRE_EQUAL(fixture.cluster->getVoxelGrid()->getCellMaterial(Vector3int16(kOffset, 0, kOffset)), CELL_MATERIAL_Grass);
Stepped stepped(100, 101, false);
exp->setParent(fixture.workspace);
// DE4979: explosions that extend below the surface used to cause a crash.
// This line triggers an explosion that extends beneath the bottom surface
// of the terrain. This function shouldn't crash if the bug is fixed.
ServiceProvider::create<RunService>(fixture.workspace)->steppedSignal(stepped);
BOOST_REQUIRE(check);
BOOST_REQUIRE_EQUAL(fixture.cluster->getVoxelGrid()->getCellMaterial(Vector3int16(kOffset, 0, kOffset)), CELL_MATERIAL_Water);
}
BOOST_AUTO_TEST_CASE( ExplosionAboveTerrainWorks ) {
const int kOffset = 0;
bool check = true;
AutowedgeTestFixture fixture;
DataModel::LegacyLock lock(&fixture.dm, RBX::DataModelJob::Write);
shared_ptr<Explosion> exp = Creatable<Instance>::create<Explosion>();
exp->setBlastRadius(20.f);
Explosion::propPosition.setValue(exp.get(), Vector3(0,240,0));
fixture.cluster->setCellScript(0, 62, 0, CELL_MATERIAL_Grass, CELL_BLOCK_Solid, CELL_ORIENTATION_NegZ);
BOOST_REQUIRE_EQUAL(fixture.cluster->getVoxelGrid()->getCellMaterial(Vector3int16(kOffset, 62, kOffset)), CELL_MATERIAL_Grass);
Stepped stepped(100, 101, false);
exp->setParent(fixture.workspace);
// DE4979: explosions that extend above the surface used to cause a crash.
// This line triggers an explosion that extends above the top surface
// of the terrain. This function shouldn't crash if the bug is fixed.
ServiceProvider::create<RunService>(fixture.workspace)->steppedSignal(stepped);
BOOST_REQUIRE(check);
BOOST_REQUIRE_EQUAL(fixture.cluster->getVoxelGrid()->getCellMaterial(Vector3int16(kOffset, 62, kOffset)), CELL_MATERIAL_Water);
}
BOOST_AUTO_TEST_SUITE_END()