local F=false local G=game:GetService("\082\117\110\083\101\114\118\105\099\101")local h=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local l=game:GetService("\083\101\114\118\101\114\083\099\114\105\112\116\083\101\114\118\105\099\101")assert(script:IsA("\077\111\100\117\108\101\083\099\114\105\112\116"),"\073\110\118\097\108\105\100 \115\099\114\105\112\116 \116\121\112\101\046 \070\111\114 \078\101\118\101\114\109\111\114\101 \116\111 \119\111\114\107 \099\111\114\114\101\099\116\108\121\044 \105\116 \115\104\111\117\108\100 \098\101 \097 \077\111\100\117\108\101\083\099\114\105\112\116 \110\097\109\101\100 \"\078\101\118\101\114\109\111\114\101\" \112\097\114\101\110\116\101\100 \116\111 \082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")assert(script.Name=="\078\101\118\101\114\109\111\114\101","\073\110\118\097\108\105\100 \115\099\114\105\112\116 \110\097\109\101\046 \070\111\114 \078\101\118\101\114\109\111\114\101 \116\111 \119\111\114\107 \099\111\114\114\101\099\116\108\121\044 \105\116 \115\104\111\117\108\100 \098\101 \097 \077\111\100\117\108\101\083\099\114\105\112\116 \110\097\109\101\100 \"\078\101\118\101\114\109\111\114\101\" \112\097\114\101\110\116\101\100 \116\111 \082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")assert(script.Parent==h,"\073\110\118\097\108\105\100 \112\097\114\101\110\116\046 \070\111\114 \078\101\118\101\114\109\111\114\101 \116\111 \119\111\114\107 \099\111\114\114\101\099\116\108\121\044 \105\116 \115\104\111\117\108\100 \098\101 \097 \077\111\100\117\108\101\083\099\114\105\112\116 \110\097\109\101\100 \"\078\101\118\101\114\109\111\114\101\" \112\097\114\101\110\116\101\100 \116\111 \082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local function d(F,G,h)assert(type(F)=="\116\097\098\108\101","\069\114\114\111\114\058 \114\101\116\114\105\101\118\105\110\103 \109\117\115\116 \098\101 \097 \116\097\098\108\101")assert(type(G)=="\102\117\110\099\116\105\111\110","\069\114\114\111\114\058 \102\117\110\099 \109\117\115\116 \098\101 \097 \102\117\110\099\116\105\111\110")local l=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local d F[h]=function()if d~=nil and d then return d end l.Event:Wait()return d end d=G(h)assert(d~=nil,"\114\101\115\117\108\116 \099\097\110\110\111\116 \098\101 \110\105\108")F[h]=nil l:Fire()l:Destroy()return d end local function o(F)assert(type(F)=="\102\117\110\099\116\105\111\110","\069\114\114\111\114\058 \102\117\110\099 \109\117\115\116 \098\101 \097 \117\115\101\114\100\097\116\097")local G={}local h={}return function(l)assert(l~=nil,"\069\114\114\111\114\058 \065\114\103\117\109\101\110\116")if G[l]~=nil then return G[l]elseif h[l]then return h[l]()else G[l]=d(h,F,l)return G[l]end end end local function O(F,h)assert(type(h)=="\115\116\114\105\110\103","\069\114\114\111\114\058 \099\108\097\115\115\078\097\109\101 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103")assert(typeof(F)=="\073\110\115\116\097\110\099\101",("\069\114\114\111\114\058 \112\097\114\101\110\116 \109\117\115\116 \098\101 \097\110 \073\110\115\116\097\110\099\101\044 \103\111\116 \'\037\115\'"):format(typeof(F)))return G:IsServer()and function(G)local l=F:FindFirstChild(G)if not l then l=Instance.new(h)l.Archivable=false l.Name=G l.Parent=F end return l end or function(G)local l=F:WaitForChild(G,5)if l then return l end warn(("\087\097\114\110\105\110\103\058 \078\111 \'\037\115\' \102\111\117\110\100\044 \098\101 \115\117\114\101 \116\111 \114\101\113\117\105\114\101 \'\037\115\' \111\110 \116\104\101 \115\101\114\118\101\114\046 \089\105\101\108\100\105\110\103 \102\111\114 \'\037\115\'"):format(tostring(G),tostring(G),h))return F:WaitForChild(G)end end local function y(F)if G:IsServer()then local F=l:FindFirstChild("\078\101\118\101\114\109\111\114\101")if not F then warn("\087\097\114\110\105\110\103\058 \078\111 \114\101\112\111\115\105\116\111\114\121 \111\102 \078\101\118\101\114\109\111\114\101 \109\111\100\117\108\101\115 \102\111\117\110\100 \040\069\120\112\101\099\116\101\100 \105\110 \083\101\114\118\101\114\083\099\114\105\112\116\083\101\114\118\105\099\101 \119\105\116\104 \110\097\109\101 \"\078\101\118\101\114\109\111\114\101\"\041\046 \076\105\098\114\097\114\121 \114\101\116\114\105\101\118\097\108 \119\105\108\108 \102\097\105\108\046")F=Instance.new("\070\111\108\100\101\114")F.Name="\078\101\118\101\114\109\111\114\101"end return F else return F("\077\111\100\117\108\101\115")end end local function M(F)local G={}for F,h in pairs(F:GetDescendants())do if h:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")and not h:FindFirstAncestorOfClass("\077\111\100\117\108\101\083\099\114\105\112\116")then if G[h.Name]then warn(("\087\097\114\110\105\110\103\058 \068\117\112\108\105\099\097\116\101 \110\097\109\101 \111\102 \'\037\115\' \097\108\114\101\097\100\121 \101\120\105\115\116\115\033 \085\115\105\110\103 \102\105\114\115\116 \102\111\117\110\100\033"):format(h.Name))end G[h.Name]=h end end return G end local e={BOTH=0;SERVER=1;CLIENT=2}local function t(F,G,h)local l=G.Parent while l and l~=h do local F=l.Name:lower()if F:find("\115\101\114\118\101\114")then return e.SERVER elseif F:find("\099\108\105\101\110\116")then return e.CLIENT end l=l.Parent end return e.BOTH end local function L(F,G,h)local l={}for G,d in pairs(G)do local o=t(G,d,h)if o==e.BOTH then d.Parent=F elseif o==e.CLIENT then d.Parent=F table.insert(l,G)end end for F,G in pairs(l)do l[G]=nil end end local function n(G)local h=0 local l=0 return function(d,...)h=h+1 local o=h local O=tick()if F then print(("\t"):rep(l),o,"\076\111\097\100\105\110\103\058 ",d)l=l+1 end local y=G(d,...)if F then l=l-1 print(("\t"):rep(l),o,"\068\111\110\101 \108\111\097\100\105\110\103\058 ",d,"\105\110",tick()-O)end return y end end local function s(F)return function(G)if typeof(G)=="\073\110\115\116\097\110\099\101"and G:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then return require(G)elseif type(G)=="\115\116\114\105\110\103"then local h=F[G]or error("\069\114\114\111\114\058 \076\105\098\114\097\114\121 \'"..(G.."\' \100\111\101\115 \110\111\116 \101\120\105\115\116\046"),2)return require(h)else error(("\069\114\114\111\114\058 \109\111\100\117\108\101 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103 \111\114 \077\111\100\117\108\101\083\099\114\105\112\116\044 \103\111\116 \'\037\115\' \102\111\114 \'\037\115\'"):format(typeof(G),tostring(G)))end end end local S=(O(h,"\070\111\108\100\101\114"))("\078\101\118\101\114\109\111\114\101\082\101\115\111\117\114\099\101\115")local X=O(S,"\070\111\108\100\101\114")local f=y(X)local u=M(f)if G:IsServer()and not G:IsClient()then L(X("\077\111\100\117\108\101\115"),u,f)end local E={}E.require=o(n(s(u)))setmetatable(E,{__call=function(F,...)return E.require(...)end;__index=function(F,G)return E.require(G)end})return E
--- Nevermore module loader.
-- Used to simply resource loading and networking so a more unified server / client codebased can be used
-- @module Nevermore

local DEBUG_MODE = false -- Set to true to help identify what libraries have circular dependencies

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")

assert(script:IsA("ModuleScript"), "Invalid script type. For Nevermore to work correctly, it should be " ..
	"a ModuleScript named \"Nevermore\" parented to ReplicatedStorage")
assert(script.Name == "Nevermore", "Invalid script name. For Nevermore to work correctly, it should be " ..
	"a ModuleScript named \"Nevermore\" parented to ReplicatedStorage")
assert(script.Parent == ReplicatedStorage, "Invalid parent. For Nevermore to work correctly, it should be " ..
	"a ModuleScript named \"Nevermore\" parented to ReplicatedStorage")

--- Handles yielded operations by caching the retrieval process
local function handleRetrieving(retrieving, func, argument)
	assert(type(retrieving) == "table", "Error: retrieving must be a table")
	assert(type(func) == "function", "Error: func must be a function")

	local bindableEvent = Instance.new("BindableEvent")
	local result
	retrieving[argument] = function()
		if result ~= nil and result then
			return result
		end

		bindableEvent.Event:Wait()
		return result
	end

	result = func(argument)
	assert(result ~= nil, "result cannot be nil")
	retrieving[argument] = nil
	bindableEvent:Fire()
	bindableEvent:Destroy()

	return result
end

--- Caches single argument, single output only
local function asyncCache(func)
	assert(type(func) == "function", "Error: func must be a userdata")

	local cache = {}
	local retrieving = {}

	return function(argument)
		assert(argument ~= nil, "Error: Argument")
		if cache[argument] ~= nil then
			return cache[argument]
		elseif retrieving[argument] then
			return retrieving[argument]()
		else
			cache[argument] = handleRetrieving(retrieving, func, argument)
			return cache[argument]
		end
	end
end


--- Retrieves an instance from a parent
local function retrieve(parent, className)
	assert(type(className) == "string", "Error: className must be a string")
	assert(typeof(parent) == "Instance", ("Error: parent must be an Instance, got '%s'"):format(typeof(parent)))

	return RunService:IsServer() and function(name)
		local rbxObject = parent:FindFirstChild(name)
		if not rbxObject then
			rbxObject = Instance.new(className)
			rbxObject.Archivable = false
			rbxObject.Name = name
			rbxObject.Parent = parent
		end
		return rbxObject
	end or function(name)
		local resource = parent:WaitForChild(name, 5)

		if resource then
			return resource
		end

		warn(("Warning: No '%s' found, be sure to require '%s' on the server. Yielding for '%s'")
			:format(tostring(name), tostring(name), className))

		return parent:WaitForChild(name)
	end
end

local function getRepository(getSubFolder)
	if RunService:IsServer() then
		local repositoryFolder = ServerScriptService:FindFirstChild("Nevermore")

		if not repositoryFolder then
			warn("Warning: No repository of Nevermore modules found (Expected in ServerScriptService with name \"Nevermore\")" ..
				". Library retrieval will fail.")

			repositoryFolder = Instance.new("Folder")
			repositoryFolder.Name = "Nevermore"
		end

		return repositoryFolder
	else
		return getSubFolder("Modules")
	end
end

local function getLibraryCache(repositoryFolder)
	local libCache = {}

	for _, child in pairs(repositoryFolder:GetDescendants()) do
		if child:IsA("ModuleScript") and not child:FindFirstAncestorOfClass("ModuleScript") then
			if libCache[child.Name] then
				warn(("Warning: Duplicate name of '%s' already exists! Using first found!"):format(child.Name))
			end

			libCache[child.Name] = child
		end
	end

	return libCache
end

local ReplicateMode = {
	BOTH = 0;
	SERVER = 1;
	CLIENT = 2;
}
local function doReplicate(name, library, topParent)
	local parent = library.Parent
	while parent and parent ~= topParent do
		local parentName = parent.Name:lower()
		if parentName:find("server") then
			return ReplicateMode.SERVER
		elseif parentName:find("client") then
			return ReplicateMode.CLIENT
		end
		parent = parent.Parent
	end

	return ReplicateMode.BOTH
end

local function replicateRepository(replicationFolder, libCache, topParent)
	local toRemove = {}
	for name, library in pairs(libCache) do
		local mode = doReplicate(name, library, topParent)
		if mode == ReplicateMode.BOTH then
			library.Parent = replicationFolder
		elseif mode == ReplicateMode.CLIENT then
			library.Parent = replicationFolder
			table.insert(toRemove, name)
		end
	end

	-- Remove client-only libraries
	for _, name in pairs(toRemove) do
		toRemove[name] = nil
	end
end

local function debugLoading(func)
	local count = 0
	local depth = 0

	return function(module, ...)
		count = count + 1
		local libraryID = count
		local startTime = tick()

		if DEBUG_MODE then
			print(("\t"):rep(depth), libraryID, "Loading: ", module)
			depth = depth + 1
		end

		local result = func(module, ...)

		if DEBUG_MODE then
			depth = depth - 1
			print(("\t"):rep(depth), libraryID, "Done loading: ", module, "in", tick() - startTime)
		end

		return result
	end
end

local function getLibraryLoader(libCache)
	return function(module)
		if typeof(module) == "Instance" and module:IsA("ModuleScript") then
			return require(module)
		elseif type(module) == "string" then
			local moduleScript = libCache[module] or error("Error: Library '" .. module .. "' does not exist.", 2)
			return require(moduleScript)
		else
			error(("Error: module must be a string or ModuleScript, got '%s' for '%s'"):format(typeof(module), tostring(module)))
		end
	end
end

local resourceFolder = retrieve(ReplicatedStorage, "Folder")("NevermoreResources")
local getSubFolder = retrieve(resourceFolder, "Folder")
local repositoryFolder = getRepository(getSubFolder)
local libCache = getLibraryCache(repositoryFolder)
if RunService:IsServer() and not RunService:IsClient() then -- Don't move in SoloTestMode
	replicateRepository(getSubFolder("Modules"), libCache, repositoryFolder)
end

local lib = {}

--- Load a library
-- @function require
-- @tparam Variant LibraryName Can either be a ModuleScript or string
-- @treturn Variant Library
lib.require = asyncCache(debugLoading(getLibraryLoader(libCache)))

setmetatable(lib, {
	__call = function(self, ...)
		return lib.require(...)
	end;
	__index = function(self, index)
		return lib.require(index)
	end;
})

return lib