null nil RBX1 0 0 0 0 1 0 0 0 1 0 0 0 1 Workspace null null 0 0.105249323 2.93674922 5.02452087 0.999780655 -0.0105661219 0.0180816781 -9.31322464e-10 0.863394618 0.504529178 -0.0209425408 -0.504418492 0.863205314 70 0 0 0 1 0 0 0 1 0 0 0 1 Camera true -0.5 0.5 0 0 -0.5 0.5 4 0 194 -2 126 -2 1 0 0 0 1 0 0 0 1 true 0.5 0.300000012 -0.5 0.5 0 0 -0.5 0.5 0 0 true 256 Terrain 0 -0.5 0.5 0 0 0 0 0 -0.5 0.5 3 0 0 0 0 0 2044 252 2044 StarterPack StarterGui true 0 10 1 Soundscape 1 CollectionService PhysicsService BadgeService Geometry RenderHooksService SocialService 1000 Debris Instance Instance CookiesService Teleport Service true Players Selection 4286611584 1 4278190080 4278190080 4290822336 100000 0 41.7332993 Lighting 4289967032 14:00:00 ChangeHistoryService false true true true TestService 10 false Script test = game:GetService("TestService") workspace = game.Workspace terrain = game.Workspace.Terrain history = game:GetService("ChangeHistoryService") --explicitly enable history history:SetEnabled(1) XZ_CHUNK = 32 Y_CHUNK = 4 cellCountList = {} materialList = {} indexList = {} wksChildList = {} local function verifyPartUndo(index) local part = workspace:findFirstChild("Part"..(index-1)) test:Check(part ~= nil, "Undo: Part not present") history:Undo() history:Undo() part = workspace:findFirstChild("Part"..index) test:Check(part == nil, "Undo: Part still present") test:Check(wksChildList[index-1] == #workspace:GetChildren(), "Undo: Instances count mismatch") end local function verifyPartRedo(index) history:Redo() history:Redo() test:Check(wksChildList[index] == #workspace:GetChildren(), "Redo: Instances count mismatch") end local function modifyName(part, name) part.Name = name history:SetWaypoint("PropName") end local function createAndRenamePart(name) -- create part and set workspace as parent local part = Instance.new("Part") part.Parent = game.Workspace history:SetWaypoint("Create") table.insert(wksChildList, #workspace:GetChildren()) -- modify name and create another way point for property modifyName(part, name) end function checkLayerData(layerIndex) local index = indexList[layerIndex] local newM, newB, newO = terrain:GetCell(XZ_CHUNK, Y_CHUNK*index, XZ_CHUNK) test:Check(materialList[layerIndex] == (newM.Value), "Material mismatch") test:Check(cellCountList[layerIndex] == terrain:CountCells(), "Terrain cell count mismatch") end function verifyLayersUndo(index, numLayers) for count = index, (index-numLayers+1), -1 do --do an undo history:Undo() checkLayerData(count-1) end --this is last undo (after this there should not be any cells present in the terrain) if ((index - numLayers) <= 1) then history:Undo() test:Check(0 == terrain:CountCells(), "Cell count") end end function verifyLayersRedo(index, numLayers) for count = index, (index+numLayers-1) do --do a redo history:Redo() checkLayerData(count) end end function generateLayers(numLayers, material) for index=1, numLayers do local start = Vector3int16.new(-XZ_CHUNK, Y_CHUNK*index, -XZ_CHUNK) local finish = Vector3int16.new(XZ_CHUNK, Y_CHUNK*index, XZ_CHUNK) terrain:SetCells(Region3int16.new(start,finish),material, 0, 0) table.insert(cellCountList, terrain:CountCells()) table.insert(materialList, material) table.insert(indexList, index) history:SetWaypoint("GenerateLayers") end end table.insert(wksChildList, #workspace:GetChildren()) generateLayers(2, 1) createAndRenamePart("Part1") generateLayers(4, 7) createAndRenamePart("Part2") verifyPartUndo(3) verifyLayersUndo(6, 4) verifyPartUndo(2) verifyLayersUndo(2, 1) verifyLayersRedo(1, 2) verifyPartRedo(2) verifyLayersRedo(3, 4) verifyPartRedo(3)