local x=game:GetService("\067\111\108\108\101\099\116\105\111\110\083\101\114\118\105\099\101")local N=script.Parent local M=require(script.Parent.Utils)local f=require(script.Parent.BounceTemplateUtils)local R={}R.DEPENDENCY_FOLDER_NAME="\110\111\100\101\095\109\111\100\117\108\101\115"R.ModuleReplicationTypes=M.readonly({CLIENT="\099\108\105\101\110\116";SERVER="\115\101\114\118\101\114",SHARED="\115\104\097\114\101\100",IGNORE="\105\103\110\111\114\101",PLUGIN="\112\108\117\103\105\110"})function R.createScriptInfo(x,N,f)assert(typeof(x)=="\073\110\115\116\097\110\099\101","\066\097\100 \105\110\115\116\097\110\099\101")assert(type(N)=="\115\116\114\105\110\103","\066\097\100 \110\097\109\101")assert(type(f)=="\115\116\114\105\110\103","\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")return M.readonly({name=N;replicationMode=f,instance=x})end function R.createScriptInfoLookup()return M.readonly({[R.ModuleReplicationTypes.SERVER]={},[R.ModuleReplicationTypes.CLIENT]={},[R.ModuleReplicationTypes.SHARED]={},[R.ModuleReplicationTypes.PLUGIN]={}})end function R.getScriptInfoLookupForMode(x,N)assert(type(x)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(N)=="\115\116\114\105\110\103","\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")return x[N]end function R.populateScriptInfoLookup(x,M,p)assert(typeof(x)=="\073\110\115\116\097\110\099\101","\066\097\100 \105\110\115\116\097\110\099\101")assert(type(M)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(p)=="\115\116\114\105\110\103","\066\097\100 \108\097\115\116\082\101\112\108\105\099\097\116\105\111\110\077\111\100\101")if x:IsA("\070\111\108\100\101\114")or x:IsA("\067\097\109\101\114\097")then local N=R.getFolderReplicationMode(x.Name,p)if N~=R.ModuleReplicationTypes.IGNORE then for x,p in pairs(x:GetChildren())do if not f.isBounceTemplate(p)then if p:IsA("\070\111\108\100\101\114")or p:IsA("\067\097\109\101\114\097")then R.populateScriptInfoLookup(p,M,N)elseif p:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then R.addToInfoMap(M,R.createScriptInfo(p,p.Name,N))end end end end elseif x:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then if not f.isBounceTemplate(x)then if x==N then R.addToInfoMap(M,R.createScriptInfo(x,"\108\111\097\100\101\114",p))else R.addToInfoMap(M,R.createScriptInfo(x,x.Name,p))end end elseif x:IsA("\079\098\106\101\099\116\086\097\108\117\101")then error("\079\098\106\101\099\116\086\097\108\117\101 \108\105\110\107\115 \097\114\101 \110\111\116 \115\117\112\112\111\114\116\101\100 \097\116 \116\104\105\115 \116\105\109\101 \102\111\114 \114\101\116\114\105\101\118\105\110\103 \105\110\108\105\110\101 \109\111\100\117\108\101 \115\099\114\105\112\116\115")end end local p={HoldingBindersServer=true,HoldingBindersClient=true,IKService=true;IKServiceClient=true}function R.isAvailableInShared(N)if x:HasTag(N.instance,"\076\105\110\107\084\111\083\104\097\114\101\100")then return true end return p[N.name]end function R.addToInfoMap(x,N)assert(type(x)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(N)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111")local M=assert(N.replicationMode,"\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")local f=assert(x[M],"\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")R.addToInfoMapForMode(f,N)if M==R.ModuleReplicationTypes.SHARED then R.addToInfoMapForMode(x[R.ModuleReplicationTypes.SERVER],N)R.addToInfoMapForMode(x[R.ModuleReplicationTypes.CLIENT],N)elseif R.isAvailableInShared(N)then R.addToInfoMapForMode(x[R.ModuleReplicationTypes.SHARED],N)end end function R.addToInfoMapForMode(x,N)if x[N.name]then warn(("\068\117\112\108\105\099\097\116\101 \109\111\100\117\108\101 \037\113 \105\110 \115\097\109\101 \112\097\099\107\097\103\101 \117\110\100\101\114 \115\097\109\101 \114\101\112\108\105\099\097\116\105\111\110 \115\099\111\112\101\046 \079\110\108\121 \117\115\105\110\103 \102\105\114\115\116 \111\110\101\046 \n- \037\113\n- \037\113"):format(N.name,N.instance:GetFullName(),x[N.name].instance:GetFullName()))return end x[N.name]=N end function R.getFolderReplicationMode(x,N)assert(type(x)=="\115\116\114\105\110\103","\066\097\100 \102\111\108\100\101\114\078\097\109\101")assert(type(N)=="\115\116\114\105\110\103","\066\097\100 \108\097\115\116\082\101\112\108\105\099\097\116\105\111\110\077\111\100\101")if x==R.DEPENDENCY_FOLDER_NAME then return R.ModuleReplicationTypes.IGNORE elseif N==R.ModuleReplicationTypes.PLUGIN then return N elseif x=="\083\104\097\114\101\100"then return R.ModuleReplicationTypes.SHARED elseif x=="\067\108\105\101\110\116"then return R.ModuleReplicationTypes.CLIENT elseif x=="\083\101\114\118\101\114"then return R.ModuleReplicationTypes.SERVER else return N end end return R
--[=[
	@private
	@class ScriptInfoUtils
]=]

local CollectionService = game:GetService("CollectionService")

local loader = script.Parent
local Utils = require(script.Parent.Utils)
local BounceTemplateUtils = require(script.Parent.BounceTemplateUtils)

local ScriptInfoUtils = {}

ScriptInfoUtils.DEPENDENCY_FOLDER_NAME = "node_modules"
ScriptInfoUtils.ModuleReplicationTypes = Utils.readonly({
	CLIENT = "client",
	SERVER = "server",
	SHARED = "shared",
	IGNORE = "ignore",
	PLUGIN = "plugin",
})

function ScriptInfoUtils.createScriptInfo(instance, name, replicationMode)
	assert(typeof(instance) == "Instance", "Bad instance")
	assert(type(name) == "string", "Bad name")
	assert(type(replicationMode) == "string", "Bad replicationMode")

	return Utils.readonly({
		name = name,
		replicationMode = replicationMode,
		instance = instance,
	})
end

function ScriptInfoUtils.createScriptInfoLookup()
	-- Server/client also contain shared entries
	return Utils.readonly({
		[ScriptInfoUtils.ModuleReplicationTypes.SERVER] = {}, -- [string name] = scriptInfo
		[ScriptInfoUtils.ModuleReplicationTypes.CLIENT] = {},
		[ScriptInfoUtils.ModuleReplicationTypes.SHARED] = {},
		[ScriptInfoUtils.ModuleReplicationTypes.PLUGIN] = {},
	})
end

function ScriptInfoUtils.getScriptInfoLookupForMode(scriptInfoLookup, replicationMode)
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(replicationMode) == "string", "Bad replicationMode")

	return scriptInfoLookup[replicationMode]
end

function ScriptInfoUtils.populateScriptInfoLookup(instance, scriptInfoLookup, lastReplicationMode)
	assert(typeof(instance) == "Instance", "Bad instance")
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(lastReplicationMode) == "string", "Bad lastReplicationMode")

	if instance:IsA("Folder") or instance:IsA("Camera") then
		local replicationMode = ScriptInfoUtils.getFolderReplicationMode(instance.Name, lastReplicationMode)
		if replicationMode ~= ScriptInfoUtils.ModuleReplicationTypes.IGNORE then
			for _, item in pairs(instance:GetChildren()) do
				if not BounceTemplateUtils.isBounceTemplate(item) then
					if item:IsA("Folder") or item:IsA("Camera") then
						ScriptInfoUtils.populateScriptInfoLookup(item, scriptInfoLookup, replicationMode)
					elseif item:IsA("ModuleScript") then
						ScriptInfoUtils.addToInfoMap(
							scriptInfoLookup,
							ScriptInfoUtils.createScriptInfo(item, item.Name, replicationMode)
						)
					end
				end
			end
		end
	elseif instance:IsA("ModuleScript") then
		if not BounceTemplateUtils.isBounceTemplate(instance) then
			if instance == loader then
				-- STRICT hack to support this module script as "loader" over "Nevermore" in replicated scenario
				ScriptInfoUtils.addToInfoMap(
					scriptInfoLookup,
					ScriptInfoUtils.createScriptInfo(instance, "loader", lastReplicationMode)
				)
			else
				ScriptInfoUtils.addToInfoMap(
					scriptInfoLookup,
					ScriptInfoUtils.createScriptInfo(instance, instance.Name, lastReplicationMode)
				)
			end
		end
	elseif instance:IsA("ObjectValue") then
		error("ObjectValue links are not supported at this time for retrieving inline module scripts")
	end
end

local AVAILABLE_IN_SHARED = {
	["HoldingBindersServer"] = true,
	["HoldingBindersClient"] = true,
	["IKService"] = true,
	["IKServiceClient"] = true,
}

function ScriptInfoUtils.isAvailableInShared(scriptInfo)
	if CollectionService:HasTag(scriptInfo.instance, "LinkToShared") then
		return true
	end

	-- Hack because we can't tag things in Rojo yet
	return AVAILABLE_IN_SHARED[scriptInfo.name]
end

function ScriptInfoUtils.addToInfoMap(scriptInfoLookup, scriptInfo)
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(scriptInfo) == "table", "Bad scriptInfo")

	local replicationMode = assert(scriptInfo.replicationMode, "Bad replicationMode")
	local replicationMap = assert(scriptInfoLookup[replicationMode], "Bad replicationMode")

	ScriptInfoUtils.addToInfoMapForMode(replicationMap, scriptInfo)

	if replicationMode == ScriptInfoUtils.ModuleReplicationTypes.SHARED then
		ScriptInfoUtils.addToInfoMapForMode(scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.SERVER], scriptInfo)
		ScriptInfoUtils.addToInfoMapForMode(scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.CLIENT], scriptInfo)
	elseif ScriptInfoUtils.isAvailableInShared(scriptInfo) then
		ScriptInfoUtils.addToInfoMapForMode(scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.SHARED], scriptInfo)
	end
end

function ScriptInfoUtils.addToInfoMapForMode(replicationMap, scriptInfo)
	if replicationMap[scriptInfo.name] then
		warn(
			("Duplicate module %q in same package under same replication scope. Only using first one. \n- %q\n- %q"):format(
				scriptInfo.name,
				scriptInfo.instance:GetFullName(),
				replicationMap[scriptInfo.name].instance:GetFullName()
			)
		)
		return
	end

	replicationMap[scriptInfo.name] = scriptInfo
end

function ScriptInfoUtils.getFolderReplicationMode(folderName, lastReplicationMode)
	assert(type(folderName) == "string", "Bad folderName")
	assert(type(lastReplicationMode) == "string", "Bad lastReplicationMode")

	--Plugin always replicates further
	if folderName == ScriptInfoUtils.DEPENDENCY_FOLDER_NAME then
		return ScriptInfoUtils.ModuleReplicationTypes.IGNORE
	elseif lastReplicationMode == ScriptInfoUtils.ModuleReplicationTypes.PLUGIN then
		return lastReplicationMode
	elseif folderName == "Shared" then
		return ScriptInfoUtils.ModuleReplicationTypes.SHARED
	elseif folderName == "Client" then
		return ScriptInfoUtils.ModuleReplicationTypes.CLIENT
	elseif folderName == "Server" then
		return ScriptInfoUtils.ModuleReplicationTypes.SERVER
	else
		return lastReplicationMode
	end
end

return ScriptInfoUtils
