When I update one script update all copies of that script in my level

I have a bunch of scripts in my level that do nothing except for copy other scripts places so that I can edit, for example, a jump pad script, in one place and have those changes propagate everywhere in the level to all my jump pads.

I’m writing my nth “InstallAllTheThings” script to do this and it sucks.

What if I just had 100 Jump Pad scripts everywhere and Studio was smart enough to update them all when I update one of them? Possibly based on a hash of the script.source, or possibly using the attribute system, or something even smarter than that.

Using the Explorer Pane to manage code sucks. There needs to be another index into all the code in an .rblx file. It’s everywhere. There is code everywhere and it feels like I’m programming in Flash.

13 Likes

Why not use CollectionService instead of doing this? Would there be a case where CollectionService is inadequate?

This sounds like a service that makes it easier to write an InstallAllTheThings script.

I want Studio to manage my code assets smarter so I don’t have to even write an InstallAllTheThings script.

CollectionService sounds like a hack. I see things in the docs like “At the moment, tags are not visible within Roblox Studio except with the use of a tag-editing plugin.” and that makes me nope out. Do I want to debug some invisible #### in my level file? No.

I want the default Studio to work great without 3rd party plugins, because I own RBLX stock and that stock only goes up when devs are productive and we have lots of them and they are having fun because their tools are so sick and so powerful that they feel like tiny little gods building their own worlds.

I don’t feel that way when I’m writing crappy code to copy other crappy code around my Workspace.

Even ZZT had BIND.

3 Likes

There’s actually already a thing for this. Right-click on a script and click “Convert to Package.”

If you want the script to auto-update, enable the “AutoUpdate” property on the PackageLink object.

After performing an edit, you can right-click on the script and select “Publish Changes to Package” to update every script.

To view all your packages, go to the Inventory tab on the Toolbox and select “My Packages” on the dropdown. You can easily insert previously made packages through this menu.

image

5 Likes

There’s a WorldView option for the tag editor, which lets you visualize all the objects tagged within the workspace:

image

Though I understand you want to develop with only the tools that studio provides, it’s not always the best way to approach the problem.

I think CollectionService is a great service instead of implementing this feature. It’s not hacky at all, in fact, you can literally code the entire service behavior with Lua in a ModuleScript and require it. There’s also the benefit of having less instances and generally a cleaner explorer to work with (every “brick” you’re controlling can be changed within that one script).

1 Like

I’m not trying to sync scripts between games though, I’m trying to sync scripts within a game.

Having a separate scripts for all the game objects in my game in the My Packages section of the toolbox seems to add clutter, not remove it.

I don’t want to publish pieces of my game logic anywhere. The last thing I want is more not-particularly-strongly-typed assets to manage.

In short, I think my original suggestion is better than all of this stuff.

1 Like

Yea I agree that having no way to organize packages is an issue. It’d be nice to have folders of some kind. Also, I can’t figure out how to delete a package after making it…

1 Like

A workaround could be to have a Folder under ServerStorage with a copy of all linked objects relevant to the game.

That is what InstallAllTheThings does.

Sometimes it installs other scripts that themselves have to propagate more scripts and stick those in parts or instances they they create and it feels very much like I’m writing a dependency manager instead of programming a video game.

3 Likes

It’s not really a hack.

Your suggestion sounds exactly like a ModuleScript. You could have those 100 scripts require a ModuleScript and you can change that ModuleScript, which will affect all those scripts reading the ModuleScript.

Maybe even use one script to control all the jump pads entirely and you’d keep track of all of them inside a folder as one suggestion.

4 Likes

It really does seem like you’re stuck in 2008 here. Having hundreds of copies of the exact same script will never be a good thing and will only ever lead to a huge mess.

CollectionService is the proper solution to your problem. It allows you to tag instances and then iterate through them in a single place, allowing you to have all logic in one script. Utilizing a plugin for this is not a problem, in fact, the plugin is literally made by a Roblox engineer. I’m all for the Tag Editor being a part of Studio from the get-go, but it’s really not a big deal that it isn’t right now.

This just seems like terrible practice. Why would you clone and move around scripts during runtime? It’s much nicer and more scalable to just grab all instances with a tag and run logic against them in a single place.

24 Likes

FWIW this is what we feel like when using CollectionService instead of copy-pasting duplicate scripts around everywhere like cave people :slightly_smiling_face:

16 Likes

This seems more like a ModuleScript usage case imo.

3 Likes

FWIW the standard pattern in Unity with ECS + DOTS is to have a System that manages a bunch of entities that all have components attached to them. You literally iterate over every single entity that matches a component filter each iteration of the update loop. This is shockingly more efficient than using monobehaviors (script components) on individual gameobjects for so many reasons. Not all reasons apply to Roblox, but some likely do.

I am doing a similar pattern in my games on Roblox using CollectionService, albeit obviously with different runtime behavior; there is no update loop in most cases, instead I track connections and maintain a table of registered instances. When I do have a loop, it iterates over the table of registered instances. I can use Collection Service to register and deregister as things stream in and out. This is a brilliant standardized way to handle behavior on mass objects, and you can pair it with attributes in a really smooth way.

Doing this with scripts instead creates a lot more crap for the Lua scheduler, and means you have to spend a lot of time cloning scripts into instances. Cloning is just a middleman. You just want to run the same code on a bunch of instances.

This pattern also allows you to have behavior on objects with LocalScripts, so you can have things like particle effects, prompts, etc handled exclusively by data set on the datamodel with attributes. You have to do some hacky stuff to make this happen without CollectionService because LocalScripts can only run in certain places.

It’s something to get used to sure, but slapping hundreds of scripts through the datamodel is horrible when we have alternatives like this. I work with this constantly and having to keep track of tags is not difficult at all like you think.

Example

image

local RS = game:GetService("ReplicatedStorage")
local modules = require(RS:WaitForChild("Modules"))
local CS = modules.require("CollectionService")

local instances = {}

-----------------------------

local function register(basepart)
	if not basepart:IsA("BasePart") then return end
	
	local data = {
		connection = basepart.Touched:Connect(function(hit)
			local character = hit.Parent
			if not character then return end
			
			local humanoid = character:FindFirstChildOfClass("Humanoid")

			if humanoid then
				humanoid:TakeDamage(humanoid.MaxHealth)
			end
		end)
	}

	instances[basepart] = data
end


local function unregister(basepart)
	if not instances[basepart] then return end

	instances[basepart].connection:Disconnect() 

	instances[basepart] = nil
end

-----------------------------

return function()
	for _,instance in pairs(CS.CollectionService:GetTagged(CS.TAG_HAZARD_PART)) do
		register(instance)
	end

	CS.CollectionService:GetInstanceAddedSignal(CS.TAG_HAZARD_PART):Connect(register)
	CS.CollectionService:GetInstanceRemovedSignal(CS.TAG_HAZARD_PART):Connect(unregister)
end
4 Likes

I would also like to add that you do not have to use CollectionService to solve this problem. If not being able to organise things clearly is bothering you, something like a Folder could be used instead.

You can choose which conditions a valid “thing” inside this Folder will meet, but a simple one to start with would be that a “thing” is a child of the Folder. This makes it super easy to see which objects in your game are affected by your code, as they will simply be inside the Folder, which you can inspect with the explorer and implement easily in code.

For example:

local thingsFolder = workspace:FindFirstChild("AllMyThings")

thingsFolder.ChildAdded:Connect(addThing)
thingsFolder.ChildRemoved:Connect(removeThing)

for _, thisThing in pairs(thingsFolder:GetChildren()) do
   task.spawn(addThing, thisThing)
end

You guys are missing the point.

I don’t want to write code to do this.

I want Studio to do it for me. It knows all these scripts are the same. When I update one, I want to update all of them. Whether it’s updating one script that gets cloned into the other locations at runtime or there are always multiple copies of the script and they all get updated is an implementation detail.

My game code is a mountain of doing stuff “by convention” that in other game engines would be strongly typed. So if I misname a property or script somewhere I get a runtime error (expensive to debug) vs. a build error (fast to debug). Scripts like InstallAllTheThings are main culprits.

3 Likes

Then it sounds like you just want local packages (i.e. not published and maintained at the place level).

It’s not reasonably possible for Studio to automatically know whether you want two scripts with the same code to remain separate or to share source, and if you have to explicitly decide you might as well just be using packages.

Regardless, you’re very strongly better off changing your approach to this.

13 Likes

Studio could attempt to do some magic to figure out which scripts it should update, but that falls apart when you start wanting deviations in some scripts but not others. How would it know to update my “Zombie” scripts but not my “Super Zombie” scripts if their source is initially the same?

I’ve actually been working on a plugin that lets you attach a script to multiple instances. It’s similar to Unity’s component system. Rather than adding a script to every single object, you add a component. That component then has some code associated with it that runs as part of your experience. You can find out more here:

Otherwise I think many of the solutions given here are good ones, packages especially.

4 Likes

This sounds exactly like a use case for Linked Sources and why it shouldn’t have ever been removed.

You can’t convice me that Package Links are a worthy replacement.

I made a simulation of LinkedSources (somewhat) as a plugin.

I’m generally against this workflow since there’s better ways of doing this, such as sourcing a shared module script or using the Components thing by woot, but hey the option is there for you.