local T={Controllers={};Modules={},Shared={},Services={};Player=(game:GetService("\080\108\097\121\101\114\115")).LocalPlayer}local q={__index=T}local H=script.Parent.Parent:WaitForChild("\067\111\110\116\114\111\108\108\101\114\115")local r=script.Parent.Parent:WaitForChild("\077\111\100\117\108\101\115")local n=((game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\065\101\114\111")):WaitForChild("\083\104\097\114\101\100")local s=(game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")).Aero:WaitForChild("\073\110\116\101\114\110\097\108")local W=require(s:WaitForChild("\070\097\115\116\083\112\097\119\110"))local function a()error("\067\097\110\110\111\116 \114\101\103\105\115\116\101\114 \101\118\101\110\116 \097\102\116\101\114 \073\110\105\116 \109\101\116\104\111\100")end function T.RegisterEvent(q,T)local H=q.Shared.Event.new()q._events[T]=H return H end function T.FireEvent(q,T,...)q._events[T]:Fire(...)end function T.ConnectEvent(H,T,q)return H._events[T]:Connect(q)end function T.WaitForEvent(q,T)return q._events[T]:Wait()end function T.WrapModule(H,T)assert(type(T)=="\116\097\098\108\101","\069\120\112\101\099\116\101\100 \116\097\098\108\101 \102\111\114 \097\114\103\117\109\101\110\116")T._events={}setmetatable(T,q)if type(T.Init)=="\102\117\110\099\116\105\111\110"and not T.__aeroPreventInit then T:Init()end if type(T.Start)=="\102\117\110\099\116\105\111\110"and not T.__aeroPreventStart then W(T.Start,T)end end local function F(q,H)local r={}H[q.Name]=r for q,H in pairs(q:GetChildren())do if H:IsA("\082\101\109\111\116\101\069\118\101\110\116")then local q=T.Shared.Event.new()local n=q.Fire function q.Fire(T,...)H:FireServer(...)end H.OnClientEvent:Connect(function(...)n(q,...)end)r[H.Name]=q elseif H:IsA("\082\101\109\111\116\101\070\117\110\099\116\105\111\110")then r[H.Name]=function(T,...)return H:InvokeServer(...)end end end return r end local function b()local q=((game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\065\101\114\111")):WaitForChild("\065\101\114\111\082\101\109\111\116\101\083\101\114\118\105\099\101\115")local function H(T,q)for T,r in pairs(T:GetChildren())do if r:IsA("\070\111\108\100\101\114")then local T=F(r,q)if next(T)==nil then H(r,T)end end end end H(q,T.Services)end local function U(q,H)setmetatable(q,{__index=function(q,r)local n=H[r]if n:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then local H=require(n)if type(H)=="\116\097\098\108\101"then T:WrapModule(H)end rawset(q,r,H)return H elseif n:IsA("\070\111\108\100\101\114")then local T={}rawset(q,r,T)U(T,n)return T end end})end local function h(T,H)local r=require(T)H[T.Name]=r r._events={}setmetatable(r,q)end local function z(T)if type(T.Init)=="\102\117\110\099\116\105\111\110"then T:Init()end T.RegisterEvent=a end local function e(T)if type(T.Start)=="\102\117\110\099\116\105\111\110"then W(T.Start,T)end end local function v()local function s(T,q)for T,H in pairs(T:GetChildren())do if H:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then h(H,q)elseif H:IsA("\070\111\108\100\101\114")then local T={}q[H.Name]=T s(H,T)end end end local function W(T)for T,H in pairs(T)do if getmetatable(H)==q then z(H)else W(H)end end end local function a(T)for T,H in pairs(T)do if getmetatable(H)==q then e(H)else a(H)end end end U(T.Modules,r)U(T.Shared,n)b()s(H,T.Controllers)W(T.Controllers)a(T.Controllers)_G.Aero=T end v()
-- Aero Client
-- Crazyman32
-- July 21, 2017



local Aero = {
	Controllers = {};
	Modules = {};
	Shared = {};
	Services = {};
	Player = game:GetService("Players").LocalPlayer;
}

local mt = {__index = Aero}

local controllersFolder = script.Parent.Parent:WaitForChild("Controllers")
local modulesFolder = script.Parent.Parent:WaitForChild("Modules")
local sharedFolder = game:GetService("ReplicatedStorage"):WaitForChild("Aero"):WaitForChild("Shared")
local internalFolder = game:GetService("ReplicatedStorage").Aero:WaitForChild("Internal")

local FastSpawn = require(internalFolder:WaitForChild("FastSpawn"))

local function PreventEventRegister()
	error("Cannot register event after Init method")
end


function Aero:RegisterEvent(eventName)
	local event = self.Shared.Event.new()
	self._events[eventName] = event
	return event
end


function Aero:FireEvent(eventName, ...)
	self._events[eventName]:Fire(...)
end


function Aero:ConnectEvent(eventName, func)
	return self._events[eventName]:Connect(func)
end


function Aero:WaitForEvent(eventName)
	return self._events[eventName]:Wait()
end


function Aero:WrapModule(tbl)
	assert(type(tbl) == "table", "Expected table for argument")
	tbl._events = {}
	setmetatable(tbl, mt)
	if (type(tbl.Init) == "function" and not tbl.__aeroPreventInit) then
		tbl:Init()
	end
	if (type(tbl.Start) == "function" and not tbl.__aeroPreventStart) then
		FastSpawn(tbl.Start, tbl)
	end
end


local function LoadService(serviceFolder, servicesTbl)
	local service = {}
	servicesTbl[serviceFolder.Name] = service
	for _,v in pairs(serviceFolder:GetChildren()) do
		if (v:IsA("RemoteEvent")) then
			local event = Aero.Shared.Event.new()
			local fireEvent = event.Fire
			function event:Fire(...)
				v:FireServer(...)
			end
			v.OnClientEvent:Connect(function(...)
				fireEvent(event, ...)
			end)
			service[v.Name] = event
		elseif (v:IsA("RemoteFunction")) then
			service[v.Name] = function(self, ...)
				return v:InvokeServer(...)
			end
		end
	end
	return service
end


local function LoadServices()
	local remoteServices = game:GetService("ReplicatedStorage"):WaitForChild("Aero"):WaitForChild("AeroRemoteServices")
	local function LoadAllServices(folder, servicesTbl)
		for _,serviceFolder in pairs(folder:GetChildren()) do
			if (serviceFolder:IsA("Folder")) then
				local service = LoadService(serviceFolder, servicesTbl)
				if (next(service) == nil) then
					LoadAllServices(serviceFolder, service)
				end
			end
		end
	end
	LoadAllServices(remoteServices, Aero.Services)
end


-- Setup table to load modules on demand:
local function LazyLoadSetup(tbl, folder)
	setmetatable(tbl, {
		__index = function(t, i)
			local child = folder[i]
			if (child:IsA("ModuleScript")) then
				local obj = require(child)
				if (type(obj) == "table") then
					Aero:WrapModule(obj)
				end
				rawset(t, i, obj)
				return obj
			elseif (child:IsA("Folder")) then
				local nestedTbl = {}
				rawset(t, i, nestedTbl)
				LazyLoadSetup(nestedTbl, child)
				return nestedTbl
			end
		end;
	})
end


local function LoadController(module, controllersTbl)
	local controller = require(module)
	controllersTbl[module.Name] = controller
	controller._events = {}
	setmetatable(controller, mt)
end


local function InitController(controller)
	if (type(controller.Init) == "function") then
		controller:Init()
	end
	controller.RegisterEvent = PreventEventRegister
end


local function StartController(controller)
	-- Start controllers on separate threads:
	if (type(controller.Start) == "function") then
		FastSpawn(controller.Start, controller)
	end
end


local function Init()
	
	-- Load controllers:
	local function LoadAllControllers(parent, controllersTbl)
		for _,child in pairs(parent:GetChildren()) do
			if (child:IsA("ModuleScript")) then
				LoadController(child, controllersTbl)
			elseif (child:IsA("Folder")) then
				local tbl = {}
				controllersTbl[child.Name] = tbl
				LoadAllControllers(child, tbl)
			end
		end
	end
	
	-- Initialize controllers:
	local function InitAllControllers(controllers)
		for _,controller in pairs(controllers) do
			if (getmetatable(controller) == mt) then
				InitController(controller)
			else
				InitAllControllers(controller)
			end
		end
	end
	
	-- Start controllers:
	local function StartAllControllers(controllers)
		for _,controller in pairs(controllers) do
			if (getmetatable(controller) == mt) then
				StartController(controller)
			else
				StartAllControllers(controller)
			end
		end
	end

	------------------------------------------------------
	
	-- Lazy load modules:
	LazyLoadSetup(Aero.Modules, modulesFolder)
	LazyLoadSetup(Aero.Shared, sharedFolder)
	
	-- Load server-side services:
	LoadServices()

	-- Load, init, and start controllers:
	LoadAllControllers(controllersFolder, Aero.Controllers)
	InitAllControllers(Aero.Controllers)
	StartAllControllers(Aero.Controllers)

	-- Expose client framework globally:
	_G.Aero = Aero
	
end


Init()