#include #include "rbx/test/DataModelFixture.h" #include "rbx/test/Base.UnitTest.Lib.h" #include "v8dataModel/BasicPartInstance.h" #include "V8DataModel/JointInstance.h" #include "V8DataModel/Lighting.h" #include "v8dataModel/Workspace.h" #include "v8tree/Instance.h" #include "Network/Players.h" #include "rbx/test/ScopedFastFlagSetting.h" #include "v8datamodel/VehicleSeat.h" #include "reflection/reflection.h" #include "script/script.h" namespace RBX { extern const char* const sTheNewClass; class TheNewClass : public DescribedCreatable { public: TheNewClass() :existingPropValue(0), theNewPropValue(0), existingEventValue(0), theNewEventValue(0), existingEvt(false), theNewEvt(false) { } ~TheNewClass(){} static Reflection::PropDescriptor* prop_ExistingProp; static Reflection::PropDescriptor* prop_TheNewProp; static Reflection::RemoteEventDesc* event_ExistingEvent; static Reflection::RemoteEventDesc* event_TheNewEvent; rbx::remote_signal onExistingEventSignal; void handleExistingEventSignal(int value) { existingEventValue = value; existingEvt.Set(); } rbx::signals::connection connectExistingSignal() { return onExistingEventSignal.connect(boost::bind(&TheNewClass::handleExistingEventSignal, this, _1)); } rbx::remote_signal onTheNewEventSignal; void handleNewEventSignal(int value) { theNewEventValue = value; theNewEvt.Set(); } rbx::signals::connection connectTheNewSignal() { return onTheNewEventSignal.connect(boost::bind(&TheNewClass::handleNewEventSignal, this, _1)); } int existingPropValue; int theNewPropValue; int existingEventValue; int theNewEventValue; CEvent existingEvt; CEvent theNewEvt; int getExistingProp() const {return existingPropValue;} void setExistingProp(int value) { if (value != existingPropValue) { existingPropValue = value; raiseChanged(*prop_ExistingProp); } } int getTheNewProp() const {return theNewPropValue;} void setTheNewProp(int value) { if (value != theNewPropValue) { theNewPropValue = value; raiseChanged(*prop_TheNewProp); } } }; const char* const sTheNewClass = "TheNewClass"; RBX_REGISTER_CLASS(TheNewClass); Reflection::PropDescriptor* TheNewClass::prop_ExistingProp = NULL; Reflection::PropDescriptor* TheNewClass::prop_TheNewProp = NULL; Reflection::RemoteEventDesc* TheNewClass::event_ExistingEvent = NULL; Reflection::RemoteEventDesc* TheNewClass::event_TheNewEvent = NULL; void registerProps(bool registerNewProp) { RBXASSERT(Reflection::Descriptor::lockedDown); Reflection::Descriptor::lockedDown = false; static Reflection::PropDescriptor prop_ExistingProp("ExistingProp", category_Data, &TheNewClass::getExistingProp, &TheNewClass::setExistingProp, Reflection::PropertyDescriptor::STANDARD); TheNewClass::prop_ExistingProp = &prop_ExistingProp; if (registerNewProp) { static Reflection::PropDescriptor prop_TheNewProp("TheNewProp", category_Data, &TheNewClass::getTheNewProp, &TheNewClass::setTheNewProp, Reflection::PropertyDescriptor::STANDARD); TheNewClass::prop_TheNewProp = &prop_TheNewProp; } Reflection::Descriptor::lockedDown = true; } void registerEvents(bool registerNewEvent) { RBXASSERT(Reflection::Descriptor::lockedDown); Reflection::Descriptor::lockedDown = false; static Reflection::RemoteEventDesc event_ExistingEvent(&TheNewClass::onExistingEventSignal, "ExistingEvent", "value", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::CLIENT_SERVER); TheNewClass::event_ExistingEvent = &event_ExistingEvent; if (registerNewEvent) { static Reflection::RemoteEventDesc event_TheNewEvent(&TheNewClass::onTheNewEventSignal, "TheNewEvent", "value", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::CLIENT_SERVER); TheNewClass::event_TheNewEvent = &event_TheNewEvent; } Reflection::Descriptor::lockedDown = true; } } // namespace RBX using namespace RBX; using namespace Network; #ifdef RBX_PLATFORM_IOS #define SLEEPTIME 5 #else #define SLEEPTIME 2 #endif BOOST_GLOBAL_FIXTURE(NetworkFixture) BOOST_AUTO_TEST_SUITE(Network) BOOST_AUTO_TEST_CASE(PartReplication) { DataModelFixture serverDm; NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm); // Create a part on the server { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); shared_ptr part = Creatable::create(); part->setAnchored(true); part->setName("TestPart"); part->setParent(serverDm->getWorkspace()); } // Wait for it to replicate G3D::System::sleep(SLEEPTIME); // Confirm the client got it { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); BOOST_CHECK(clientDm->getWorkspace()->findFirstChildByName("TestPart") != 0); } } BOOST_AUTO_TEST_CASE(FilterEnabled) { DataModelFixture server; NetworkFixture::startServer(server); // Turn on filter enabled server->getWorkspace()->setNetworkFilteringEnabled(true); DataModelFixture client1; DataModelFixture client2; NetworkFixture::startClient(client1); NetworkFixture::startClient(client2); std::string BRICK_STRING = "Brick"; std::string CHANGED_STRING = "Changed"; // Create a brick on the server shared_ptr serverPart; { RBX::DataModel::LegacyLock lock(&server, RBX::DataModelJob::Write); serverPart = Creatable::create(); serverPart->setAnchored(true); serverPart->setName(BRICK_STRING); serverPart->setParent(server->getWorkspace()); } BOOST_MESSAGE("Waiting for brick to replicate"); G3D::System::sleep(SLEEPTIME); // Confirm both clients got it Instance* clientPart1; { RBX::DataModel::LegacyLock lock(&client1, RBX::DataModelJob::Write); clientPart1 = client1->getWorkspace()->findFirstChildByName(BRICK_STRING); BOOST_REQUIRE(clientPart1); } Instance* clientPart2; { RBX::DataModel::LegacyLock lock(&client2, RBX::DataModelJob::Write); clientPart2 = client2->getWorkspace()->findFirstChildByName(BRICK_STRING); BOOST_REQUIRE(clientPart2); } // Make client1 change the brick name { RBX::DataModel::LegacyLock lock(&client1, RBX::DataModelJob::Write); clientPart1->setName(CHANGED_STRING); } BOOST_MESSAGE("Giving client-1 a chance to replicate its name change"); G3D::System::sleep(SLEEPTIME); // Confirm that client 1's change was rejected because the network filter is enabled { RBX::DataModel::LegacyLock lock(&server, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(serverPart->getName(), BRICK_STRING); } { RBX::DataModel::LegacyLock lock(&client2, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(clientPart2->getName(), BRICK_STRING); } } BOOST_AUTO_TEST_CASE(FilterDisabled) { DataModelFixture server; NetworkFixture::startServer(server); // Turn off filter enabled server->getWorkspace()->setNetworkFilteringEnabled(false); DataModelFixture client1; DataModelFixture client2; NetworkFixture::startClient(client1); NetworkFixture::startClient(client2); std::string BRICK_STRING = "Brick"; std::string CHANGED_STRING = "Changed"; // Create a brick on the server shared_ptr serverPart; { RBX::DataModel::LegacyLock lock(&server, RBX::DataModelJob::Write); serverPart = Creatable::create(); serverPart->setAnchored(true); serverPart->setName(BRICK_STRING); serverPart->setParent(server->getWorkspace()); } BOOST_MESSAGE("Waiting for brick to replicate"); G3D::System::sleep(SLEEPTIME); // Confirm both clients got it Instance* clientPart1; { RBX::DataModel::LegacyLock lock(&client1, RBX::DataModelJob::Write); clientPart1 = client1->getWorkspace()->findFirstChildByName(BRICK_STRING); BOOST_REQUIRE(clientPart1); } Instance* clientPart2; { RBX::DataModel::LegacyLock lock(&client2, RBX::DataModelJob::Write); clientPart2 = client2->getWorkspace()->findFirstChildByName(BRICK_STRING); BOOST_REQUIRE(clientPart2); } // Make client1 change the brick name { RBX::DataModel::LegacyLock lock(&client1, RBX::DataModelJob::Write); clientPart1->setName(CHANGED_STRING); } BOOST_MESSAGE("Giving client-1 a chance to replicate its name change"); G3D::System::sleep(SLEEPTIME); // Confirm that client 1's change was accepted because the network filter is disabled { RBX::DataModel::LegacyLock lock(&server, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(serverPart->getName(), CHANGED_STRING); } { RBX::DataModel::LegacyLock lock(&client2, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(clientPart2->getName(), CHANGED_STRING); } } BOOST_AUTO_TEST_CASE(ShipsInTheNight) { DataModelFixture serverDm; NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm); // Create a part on the server shared_ptr serverPart; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); serverPart = Creatable::create(); serverPart->setAnchored(true); serverPart->setName("TestPart"); serverPart->setParent(serverDm->getWorkspace()); } BOOST_MESSAGE("Waiting for TestPart to replicate"); G3D::System::sleep(SLEEPTIME); // Confirm the client got it Instance* clientPart; { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); clientPart = clientDm->getWorkspace()->findFirstChildByName("TestPart"); BOOST_REQUIRE(clientPart); } // simulate network lag from server to client clientDm.execute("r = game:GetService('NetworkClient'):GetChildren()[1] r:SetPropSyncExpiration(60) r:DisableProcessPackets()"); serverDm.execute("r = game:GetService('NetworkServer'):GetChildren()[1] r:SetPropSyncExpiration(30)"); // Now make both sides change the name { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); serverPart->setName("Red"); } { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); clientPart->setName("Blue"); } BOOST_MESSAGE("Waiting for client to replicate Blue to server"); G3D::System::sleep(SLEEPTIME); // Confirm that client's value was rejected because Red hasn't been acknowledged by Client { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(serverPart->getName(), "Red"); } // Confirm that client hasn't gotten the Red value yet { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(clientPart->getName(), "Blue"); } // Re-open the pipe from server to client clientDm.execute("game:GetService('NetworkClient'):GetChildren()[1]:EnableProcessPackets()"); BOOST_MESSAGE("Waiting for server to replicate Red to client"); G3D::System::sleep(SLEEPTIME); // Confirm that the parts agree on Red { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(serverPart->getName(), "Red"); } { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(clientPart->getName(), "Red"); } { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); clientPart->setName("Yellow"); } BOOST_MESSAGE("Waiting for client to replicate Yellow to server"); G3D::System::sleep(SLEEPTIME); { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(serverPart->getName(), "Yellow"); } } // This test deals with the case where a script changes a property to a new value once it changes. // The networking code should honor this case and make sure all peers get the right value. BOOST_AUTO_TEST_CASE(PropertyBounceBack) { DataModelFixture serverDm; NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm); // Create a part on the server shared_ptr serverPart; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); serverPart = Creatable::create(); serverPart->setAnchored(true); serverPart->setName("TestPart"); serverPart->setParent(serverDm->getWorkspace()); } BOOST_MESSAGE("Waiting for TestPart to replicate"); G3D::System::sleep(SLEEPTIME); // Confirm the client got it Instance* clientPart; { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); clientPart = clientDm->getWorkspace()->findFirstChildByName("TestPart"); BOOST_REQUIRE(clientPart); } // set up a bounceback on the server serverDm.execute("p = workspace.TestPart p.Changed:connect(function() p.Name = 'Blue' end)"); // Now make the client change a property { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); clientPart->setName("Red"); } BOOST_MESSAGE("Waiting for client to replicate Red to server"); G3D::System::sleep(SLEEPTIME); // Confirm that both client and server are Blue { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(serverPart->getName(), "Blue"); } { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); BOOST_CHECK_EQUAL(clientPart->getName(), "Blue"); } } class ParentChangeRecorder { public: std::vector parentList; boost::shared_ptr instance; rbx::signals::scoped_connection propChangedConnection; rbx::signals::scoped_connection ancestryChangedConnection; ParentChangeRecorder(boost::shared_ptr_instance) : instance(_instance) { propChangedConnection = instance->propertyChangedSignal.connect(boost::bind(&ParentChangeRecorder::onParentChanged, this, _1)); ancestryChangedConnection = instance->ancestryChangedSignal.connect(boost::bind(&ParentChangeRecorder::onAncestryChanged, this, _1, _2)); } ~ParentChangeRecorder() {} void disconnectListeners() { propChangedConnection.disconnect(); ancestryChangedConnection.disconnect(); } void onParentChanged(const RBX::Reflection::PropertyDescriptor* desc) { if (*desc == RBX::Instance::propParent) { if (instance->getParent()) parentList.push_back(instance->getParent()->getName()); else parentList.push_back("NULL"); } } void onAncestryChanged(shared_ptr child, shared_ptr newParent) { BOOST_CHECK(Workspace::getWorkspaceIfInWorkspace(instance.get()) != NULL); } }; BOOST_AUTO_TEST_CASE(ParentPropChangeArriveInOrder) { DataModelFixture serverDm; NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm); // Create a part with children on the server shared_ptr serverPart; shared_ptr childPart; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); serverPart = Creatable::create(); serverPart->setAnchored(true); serverPart->setName("TestPart"); serverPart->setParent(serverDm->getWorkspace()); childPart = Creatable::create(); childPart->setAnchored(true); childPart->setName("child"); childPart->setParent(serverPart.get()); } BOOST_MESSAGE("Waiting for TestPart to replicate"); G3D::System::sleep(SLEEPTIME); // Confirm the client got it Instance* clientPart; shared_ptr parentChangeRecorder; { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); clientPart = clientDm->getWorkspace()->findFirstChildByName("TestPart"); BOOST_REQUIRE(clientPart); Instance* clientChildPart = clientPart->findFirstChildByName("child"); BOOST_REQUIRE(clientChildPart); parentChangeRecorder.reset(new ParentChangeRecorder(shared_from(clientChildPart))); } BOOST_MESSAGE("Modifying parent on Server"); // modify children's parent on server shared_ptr serverPart2; shared_ptr serverPart3; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); BOOST_MESSAGE("Creating serverPart2"); serverPart2 = Creatable::create(); serverPart2->setAnchored(true); serverPart2->setName("TestPart2"); serverPart2->setParent(serverDm->getWorkspace()); BOOST_MESSAGE("Setting parent to serverPart2"); childPart->setParent(serverPart2.get()); serverPart->setParent(NULL); BOOST_MESSAGE("Creating serverPart3"); serverPart3 = Creatable::create(); serverPart3->setAnchored(true); serverPart3->setName("TestPart3"); serverPart3->setParent(serverDm->getWorkspace()); BOOST_MESSAGE("Setting parent to serverPart3"); childPart->setParent(serverPart3.get()); } G3D::System::sleep(SLEEPTIME); // Comfirm parent of all children is set correctly on client { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); parentChangeRecorder->disconnectListeners(); BOOST_CHECK_EQUAL(parentChangeRecorder->parentList.size(), 2); // validate parents were set in correct order BOOST_CHECK_EQUAL(parentChangeRecorder->parentList[0], "TestPart2"); BOOST_CHECK_EQUAL(parentChangeRecorder->parentList[1], "TestPart3"); } } static void NewInstanceParentAlwaysValid_onChildAdded(shared_ptr child, shared_ptr event) { if (child->getName() == "TestPart2") event->Set(); } BOOST_AUTO_TEST_CASE(NewInstanceParentAlwaysValid) { DataModelFixture serverDm; NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm); // Create a part with children on the server shared_ptr serverPart; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); serverPart = Creatable::create(); serverPart->setAnchored(true); serverPart->setName("TestPart"); serverPart->setParent(serverDm->getWorkspace()); } BOOST_MESSAGE("Waiting for TestPart to replicate"); G3D::System::sleep(SLEEPTIME); // Confirm the client got it Instance* clientPart; shared_ptr event(new RBX::CEvent(true)); { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); clientPart = clientDm->getWorkspace()->findFirstChildByName("TestPart"); BOOST_REQUIRE(clientPart); clientPart->onDemandWrite()->childAddedSignal.connect(boost::bind(&NewInstanceParentAlwaysValid_onChildAdded, _1, event)); } // Create new parts on server shared_ptr serverPart2; shared_ptr serverPart3; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); serverPart2 = Creatable::create(); serverPart2->setAnchored(true); serverPart2->setName("TestPart2"); serverPart2->setParent(serverPart.get()); // this should create a NewInstanceItem in replicator serverPart3 = Creatable::create(); serverPart3->setAnchored(true); serverPart3->setName("TestPart3"); serverPart3->setParent(serverDm->getWorkspace()); serverPart2->setParent(serverPart3.get()); // this should create a ParentPropChangeItem in replicator } // Confirm the client got it and set parent correctly BOOST_CHECK(event->Wait(RBX::Time::Interval::from_seconds(60))); G3D::System::sleep(SLEEPTIME); { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); Instance* clientTestPart3 = clientDm->getWorkspace()->findFirstChildByName("TestPart3"); BOOST_REQUIRE(clientTestPart3); BOOST_CHECK(clientTestPart3->findFirstChildByName("TestPart2") != NULL); } } static void onChildAdded(shared_ptr child, shared_ptr event) { if (child->getName() == "LastPart") event->Set(); }; BOOST_AUTO_TEST_CASE(GuidsAreAssignedImmeidatelyOnCreation) { shared_ptr part = Creatable::create(); Guid::Data data; part->getGuid().extract(data); BOOST_CHECK(!data.scope.isNull()); } BOOST_AUTO_TEST_CASE(ReplicationDoesNotAllowRedundantNewJointsToBeSeen) { DataModelFixture serverDm; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); shared_ptr part0 = Creatable::create(); shared_ptr part1 = Creatable::create(); shared_ptr motor = Creatable::create(); motor->setName("Motor6D"); motor->setPart0(part0.get()); motor->setPart1(part1.get()); motor->setParent(part0.get()); part1->setName("Part1"); part1->setParent(serverDm->getWorkspace()); part0->setName("Part0"); part0->setParent(serverDm->getWorkspace()); } NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm); // wait for client to get initial parts G3D::System::sleep(SLEEPTIME); { RBX::DataModel::LegacyLock lock(&clientDm, RBX::DataModelJob::Write); Workspace* workspace = clientDm->getWorkspace(); BasicPartInstance* part0 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part0")); BOOST_REQUIRE(part0); BasicPartInstance* part1 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part1")); BOOST_REQUIRE(part1); Motor6D* motor = Instance::fastDynamicCast( part0->findFirstChildByName("Motor6D")); BOOST_REQUIRE(motor); // This will temporarily unconnect the existing motor, attach the // parts with a manual weld, then destroy the manual weld and reattach // the motor. motor->setPart1(NULL); shared_ptr tempWeld = Creatable::create(); tempWeld->setPart0(part0); tempWeld->setPart1(part1); tempWeld->setName("ManualWeld"); tempWeld->setParent(part0); tempWeld->setParent(NULL); motor->setPart1(part1); // sanity check that the engine has not kicked motor out BOOST_CHECK(motor->getParent()); } // Wait of sequence to replicate to server G3D::System::sleep(SLEEPTIME); // make sure that the server joint state is valid { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); Workspace* workspace = serverDm->getWorkspace(); BasicPartInstance* part0 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part0")); BasicPartInstance* part1 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part1")); Motor6D* motor = Instance::fastDynamicCast( part0->findFirstChildByName("Motor6D")); Instance* tempWeld = part0->findFirstChildByName("ManualWeld"); // verify everything is preserved in the correct state BOOST_CHECK(part0); BOOST_CHECK(part1); BOOST_CHECK(motor); BOOST_CHECK(tempWeld == NULL); } } BOOST_AUTO_TEST_CASE(ReplicationDoesNotAllowRedundantExistingJointsToBeSeen) { DataModelFixture serverDm; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); shared_ptr part0 = Creatable::create(); shared_ptr part1 = Creatable::create(); shared_ptr motor = Creatable::create(); shared_ptr weld = Creatable::create(); Lighting* lighting = ServiceProvider::create(&serverDm); BOOST_REQUIRE(lighting); motor->setName("Motor6D"); motor->setPart0(part0.get()); motor->setPart1(part1.get()); motor->setParent(serverDm->getWorkspace()); part1->setName("Part1"); part1->setParent(serverDm->getWorkspace()); part0->setName("Part0"); part0->setParent(serverDm->getWorkspace()); weld->setName("ManualWeld"); weld->setPart0(part0.get()); weld->setPart1(part0.get()); weld->setParent(lighting); } NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm); // wait for client to get initial parts G3D::System::sleep(SLEEPTIME); { DataModel::LegacyLock lock(&clientDm, DataModelJob::Write); Lighting* lighting = ServiceProvider::find(&clientDm); BOOST_REQUIRE(lighting); Motor6D* motor = Instance::fastDynamicCast( clientDm->getWorkspace()->findFirstChildByName("Motor6D")); BOOST_REQUIRE(motor); ManualWeld* weld = Instance::fastDynamicCast( lighting->findFirstChildByName("ManualWeld")); BOOST_REQUIRE(weld); weld->setPart1(NULL); weld->setParent(clientDm->getWorkspace()); PartInstance* part1 = motor->getPart1(); BOOST_REQUIRE(part1); motor->setPart1(NULL); weld->setPart1(part1); } // let server catch up G3D::System::sleep(SLEEPTIME); // verify that everything is intact { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); Workspace* workspace = serverDm->getWorkspace(); BasicPartInstance* part0 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part0")); BasicPartInstance* part1 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part1")); Motor6D* motor = Instance::fastDynamicCast( workspace->findFirstChildByName("Motor6D")); ManualWeld* weld = Instance::fastDynamicCast( workspace->findFirstChildByName("ManualWeld")); // verify everything is preserved in the correct state BOOST_CHECK(part0); BOOST_CHECK(part1); BOOST_CHECK(motor); BOOST_CHECK(weld); BOOST_CHECK_EQUAL(motor->getPart0(), part0); BOOST_CHECK(motor->getPart1() == NULL); BOOST_CHECK_EQUAL(weld->getPart0(), part0); BOOST_CHECK_EQUAL(weld->getPart1(), part1); } } BOOST_AUTO_TEST_CASE(ReplicationDoesNotAllowRedundantExistingJointsWithCharacterToBeSeen) { DataModelFixture serverDm; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); shared_ptr part0 = Creatable::create(); shared_ptr part1 = Creatable::create(); shared_ptr motor = Creatable::create(); shared_ptr weld = Creatable::create(); Lighting* lighting = ServiceProvider::create(&serverDm); // Sanity check: actually have lighting created BOOST_REQUIRE(lighting); motor->setName("Motor6D"); motor->setPart0(part0.get()); motor->setPart1(part1.get()); motor->setParent(serverDm->getWorkspace()); part1->setName("Part1"); part1->setParent(serverDm->getWorkspace()); part0->setName("Part0"); part0->setParent(serverDm->getWorkspace()); weld->setName("ManualWeld"); weld->setPart0(part0.get()); weld->setPart1(part0.get()); weld->setParent(lighting); } NetworkFixture::startServer(serverDm); DataModelFixture clientDm; NetworkFixture::startClient(clientDm, true); // wait for client to get initial parts G3D::System::sleep(SLEEPTIME); ModelInstance* clientCharacterModel; { DataModel::LegacyLock lock(&clientDm, DataModelJob::Write); Lighting* lighting = ServiceProvider::find(&clientDm); BOOST_REQUIRE(lighting); Player* player = Players::findLocalPlayer(&clientDm); BOOST_REQUIRE(player); clientCharacterModel = player->getCharacter(); BOOST_REQUIRE(clientCharacterModel); Motor6D* motor = Instance::fastDynamicCast( clientDm->getWorkspace()->findFirstChildByName("Motor6D")); BOOST_REQUIRE(motor); ManualWeld* weld = Instance::fastDynamicCast( lighting->findFirstChildByName("ManualWeld")); BOOST_REQUIRE(weld); weld->setParent(clientCharacterModel); PartInstance* part1 = motor->getPart1(); BOOST_REQUIRE(part1); motor->setPart1(NULL); weld->setPart1(part1); // Sanity check that the motor and weld haven't been kicked out // due to the duplicate-joint constraint BOOST_REQUIRE(motor->getParent()); BOOST_REQUIRE(motor->getPart0() != NULL); BOOST_REQUIRE(motor->getPart1() == NULL); BOOST_REQUIRE(weld->getParent()); BOOST_REQUIRE(weld->getPart0() == motor->getPart0()); BOOST_REQUIRE(weld->getPart1() == part1); } // let server catch up G3D::System::sleep(SLEEPTIME); // verify that everything is intact on the server { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); Workspace* workspace = serverDm->getWorkspace(); BasicPartInstance* part0 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part0")); BasicPartInstance* part1 = Instance::fastDynamicCast( workspace->findFirstChildByName("Part1")); Motor6D* motor = Instance::fastDynamicCast( workspace->findFirstChildByName("Motor6D")); ModelInstance* playerModel = Instance::fastDynamicCast( workspace->findFirstChildByName(clientCharacterModel->getName())); BOOST_REQUIRE(playerModel); ManualWeld* weld = Instance::fastDynamicCast( playerModel->findFirstChildByName("ManualWeld")); // verify everything is preserved in the correct state BOOST_CHECK(part0); BOOST_CHECK(part1); BOOST_CHECK(motor); BOOST_CHECK(weld); BOOST_CHECK_EQUAL(motor->getPart0(), part0); BOOST_CHECK(motor->getPart1() == NULL); BOOST_CHECK_EQUAL(weld->getPart0(), part0); BOOST_CHECK_EQUAL(weld->getPart1(), part1); } } static void onScriptLockGrantedOrNot(bool lockGranted, bool* state, RBX::CEvent* event) { *state = lockGranted; event->Set(); } BOOST_AUTO_TEST_CASE(ReplicationExplicitlyAssignDefaultPropertyValues) { ScopedFastFlagSetting flag("ExplicitlyAssignDefaultPropVal", true); const PartMaterial kDefaultPartMaterial(PLASTIC_MATERIAL); const PartMaterial kPartMaterial(GRANITE_MATERIAL); const std::string kDefaultPartName("Part"); const std::string kDefaultScriptName("Script"); const std::string kScriptName("Script_New"); DataModelFixture serverDm; { RBX::DataModel::LegacyLock lock(&serverDm, RBX::DataModelJob::Write); shared_ptr