Searcher - Easier Instance Searching

🧰 Installation

You can install Searcher via:
Wally Marketplace
The source can be found at
Github

πŸ“• Documentation

Documentation is work in progress. But I have documented some below.

Searcher

Searcher.NewFilter() -> Filter
Searcher.FindOne: (Filter: Filter, Instances: {Instance?}) -> Instance?

Explanation

This can be used to get the first Instance that goes through a filter.

Could for example be used to make a better FindFirstChild() or FindFirstChildWhichIsA().

Example
local Searcher = require(PARENT.Searcher)

local NeckFilter = Searcher.NewFilter()
:IsA("Motor6D") -- IsA Motor6D?
:Named("Neck") -- Named "Neck"?
:Not():Property("Part0", nil) -- Has a Part0?
:Not():Property("Part1", nil) -- Has a Part1?

local Neck: Motor6D? = Searcher.FindOne(NeckFilter, game.Players.Character:GetDescendants())
Searcher.FindAll: (Filter: Filter, Instances: {Instance?}) -> {Instance}

Explanation

This can be used to get all Instances that goes through a filter.

Could for example be used to make a better GetChildren() or GetDescendants().

Example
local Searcher = require(PARENT.Searcher)

local VisibleDecalFilter = Searcher.NewFilter()
:IsA("Decal") -- IsA Decal?
:Not():Property("Transpareny", 1) -- Transparency is NOT 1

-- Get all visible decals in workspace
local VisibleDecals: {Decal} = Searcher.FindAll(VisibleDecalFilter, workspace:GetDescendants())

-- Hide the decals by setting Transparency to 1
for _, Decal: Decal in VisibleDecals do
    Decal.Transparency = 1
end
Searcher.InstanceMatches: (Filter: Filter, Instance: Instance) -> boolean

Explanation

This can be used to check whether a Instance goes through a filter.

Example
local Searcher = require(PARENT.Searcher)

local NeonPartFilter = Searcher.NewFilter()
:IsA("Part") -- IsA Part?
:Property("Material", Enum.Material.Neon) -- Has material set to Neon?

print(Searcher.InstanceMatches(NeonPartFilter, MyNormalPart) --> false
print(Searcher.InstanceMatches(NeonPartFilter, MyNeonPart) --> true
Searcher.InstanceAdded: (Filter: Filter, Ancestor: Instance?) -> RBXScriptSignal

Explanation

This can be used when you want to detect a new Instance being created that goes through the filter successfully.

Example
local Searcher = require(PARENT.Searcher)

local BasePartFilter = Searcher.NewFilter()
:IsA("BasePart")

-- Detect when a BasePart has been added to workspace
Searcher.InstanceAdded(BasePartFilter, workspace):Connect(function(Part: BasePart)
    print(Part.ClassName) --> BasePart
end)

FilterMethods

Explanation

FilterMethods can be chained after creating a Filter to describe if an Instance goes through the filter.

Built in FilterMethods

These are the current built in FilterMethods:

-- Specials
:Not() -- The next chained Filter Method will be inverted (true = false, false = true)

-- Normal
:IsA(ClassName: string) -- Is the object a 'ClassName'?
:Is(Instance: Instance) -- Is the object 'Instance'?

:Named(Name: string) -- Is object named 'Name'?
:Property(PropertyName: string, Value: any) -- Does the object have the property 'PropertyName' set to 'Value'?
:Attribute(AttributeName: string, Value: any) -- Does the object have the attribute 'AttributeName' set to 'Value'?
:Tagged(TagName: string) -- Is the object tagged with 'TagName'?

:ChildOf(Parent: Instance?) -- Is the object a child of 'Parent'?
:DescendantOf(Ancestor: Instance?) -- Is the object a descendant of 'Ancestor'?

:HasChild(Name: string) -- Does the object have a child named 'Name'?
:HasChildWhichIsA(ClassName: string) -- Does the object have a child that IsA 'ClassName'?

:Custom(Callback: (Object: instance) -> boolean?) -- Add a custom callback to the filter, return a boolean that tells Searcher if the object goes through the filter or not.

(If you have any ideas on FilterMethods I could add then please tell me!)

πŸ“ Usage

The main thing about Searcher is the Filter. To create a Filter you can use the Searcher.NewFilter() method.

local Searcher = require(PARENT.Searcher)

local Filter = Searcher.NewFilter()

You can apply logic to these filters to distinguish from other instances.
For a Filter to distinguish an Instance as a β€œvalid instance”, all of the passed callbacks (FilterMethods) have to return true.

This is an example of a LavaPart filter.

local LavaFilter = Module.NewFilter()
:IsA("BasePart") -- Is a BasePart
:Tagged("Lava") -- Is tagged with Lava
:DescendantOf(workspace) -- Is descendant of workspace
:Property("Material", Enum.Material.Neon) -- Has the material Neon
:Not():Attribute("Disabled", true) -- Isn't disabled

And the LavaFilter can be used something like this:

-- Get all LavaParts in workspace with the Filter, using the .FindAll function
local LavaParts = Searcher.FindAll(LavaFilter, workspace:GetDescendants())

-- Loop through the parts
for _, Part: Part in LavaParts do
    -- Detect collision
    Part.Touched:Connect(function(hit)
        -- Check if hit is a player
        local Humanoid: Humanoid = hit.Parent:FindFirstChildWhichIsA("Humanoid")
        if not (Humanoid) then return end
        
        -- Kill player
        Humanoid.Health = 0
    end
end
Full Script
local Searcher = require(PARENT.Searcher)

local LavaFilter = Searcher.NewFilter()
:IsA("BasePart")
:Tagged("Lava")
:DescendantOf(workspace)
:Property("Material", Enum.Material.Neon)
:Not():Attribute("Disabled", true)

local LavaParts = Searcher.FindAll(LavaFilter, workspace:GetDescendants())

for _, Part: Part in LavaParts do
    Part.Touched:Connect(function(hit)
        local Humanoid: Humanoid = hit.Parent:FindFirstChildWhichIsA("Humanoid")
        if not (Humanoid) then return end
        
        Humanoid.Health = 0
    end
end

πŸ“ Filter Reusage

Filters can be reused infinitely within your code so they are easy to modify and use.
This also means that you can make a ModuleScript that returns a Filter for more organization.

An example of this:

-- CharacterFilter (ModuleScript)

local Searcher = require(PARENT.Searcher)

local CharacterFilter = Searcher.NewFilter()
:IsA("Model")
:HasChildWhichIsA("Humanoid")

return CharacterFilter

πŸ“ Some Use Case Examples

Here are some Use Case Examples I wrote to show you the power of Searcher.

Getting all Players that are close to the LocalPlayer
local Searcher = require(PARENT.Searcher)

local LocalPlayer: Player = game.Players.LocalPlayer

local ClosePlayersFilter = Searcher.NewFilter()
:IsA("Player") -- Is a Player?
:Not():Property("Character", nil) -- Has a character?
:Not():Is(LocalPlayer) -- Is not the LocalPlayer?
:Custom(function(Player: Player) -- Is Close?
    -- Get distance
    local Distance: number = (LocalPlayer.Character.HumanoidRootPart.Position - Player.Character.HumanoidRootPart.Position).Magnitude

    return (Distance < 40) -- If is close then return true
end)

-- Printing all close Players
print(Searcher.FindAll(ClosePlayersFilter, game.Players:GetChildren()))

πŸ“ Feedback is Appreciated!

If you have any idea of features I could add or bugs that you find, please tell me, it would really help!

Would you use this in a project?
  • Yes
  • No
  • Maybe
  • Sometimes

0 voters

10 Likes

I love it, I’ll look through the code and see how I can contribute!

1 Like