F3X Building Tools Importer
Key RequiredUniversal

F3X Building Tools Importer

Universal Script57zigzag57zigzag 12.1K views 308 executesUpdated Apr 10, 2025
5.0/ 5(1 votes)
10100% Positive
Lua Script•695 lines (16623 chars)
local CollectionService = game:GetService('CollectionService')
local httpservice = game:GetService("HttpService")
local Player = game:GetService("Players").LocalPlayer

-------- SETTINGS ----------
local F3X_ImportCode = "" --Leave blank for UI
local Parent = workspace
local RealativeCFrame = nil

local Group = false -- Slow :v
local ForcedAnchor = true
------ END SETTINGS --------

local Log = print

local function print(...)
	if Log and type(Log) == 'function' then
		Log(...)		
	end
end

local cccc = coroutine.running()
function STOP()
	task.defer(task.cancel, cccc)
end

local function assert(cond, ...)
	if not cond then
		if Log and type(Log) == 'function' then
			Log(...)		
		end
		STOP()
		task.defer(task.cancel, coroutine.running())
		wait()
	end
end

local Types = {
	Part = 0,
	WedgePart = 1,
	CornerWedgePart = 2,
	VehicleSeat = 3,
	Seat = 4,
	TrussPart = 5,
	SpecialMesh = 6,
	Texture = 7,
	Decal = 8,
	PointLight = 9,
	SpotLight = 10,
	SurfaceLight = 11,
	Smoke = 12,
	Fire = 13,
	Sparkles = 14,
	Model = 15,
	Folder = 18
};

local PartTypes = {
	[Enum.PartType.Block.Value] = "Normal",
	[Enum.PartType.Ball.Value] = "Ball",
	[Enum.PartType.Wedge.Value] = "Wedge",
	[Enum.PartType.Cylinder.Value] = "Cylinder",
	[Enum.PartType.CornerWedge.Value] = "Corner",
	FromTypes = {
		[Types.WedgePart] = "Wedge",
		[Types.CornerWedgePart] = "Corner",
		[Types.VehicleSeat] = "Vehicle Seat",
		[Types.Seat] = "Seat",
		[Types.TrussPart] = "Truss",
	}
}

local Events = {}
local MaxThreads = 1000
local ThreadWatch = {Threads = {}}

function ThreadWatch:Add(Thread:thread)
	if typeof(Thread) == 'thread' then
		table.insert(ThreadWatch.Threads,Thread)
	end
end

local GabrageDebounce = false
function ThreadWatch:GarbageCollect()
	
	if GabrageDebounce then
		return
	end
	
	GabrageDebounce = true
	
	local NList = {}

	for Index,Thread in ipairs(ThreadWatch.Threads) do
		if typeof(Thread) == 'thread' and coroutine.status(Thread) ~= "dead" then
			table.insert(NList,Thread)
		end
	end

	ThreadWatch.Threads = NList
	
	GabrageDebounce = false
end

function tcall(func,...)
	if #ThreadWatch.Threads > MaxThreads then
		repeat task.wait(); ThreadWatch:GarbageCollect() until #ThreadWatch.Threads < MaxThreads
	end
	ThreadWatch:Add(task.spawn(func,...))
end



local Support = {
	Slice = function (Table, Start, End)
		-- Returns values from `Start` to `End` in `Table`

		local Slice = {};

		-- Go through the given indices
		for Index = Start, End do
			table.insert(Slice, Table[Index]);
		end;

		-- Return the slice
		return Slice;

	end;
}

function filterProperties(tbl, ...)
	local newtbl = {}
	for i,iv in pairs(tbl) do
		for _,v in ipairs({...}) do
			if iv[v] then
				if not newtbl[i] then
					newtbl[i] = {}
				end
				newtbl[i][v] = tbl[i][v]
			end
		end
	end
	return newtbl
end

if not F3X_ImportCode or F3X_ImportCode == "" then
	local BTImportUI = Instance.new("ScreenGui")
	local Boarder = Instance.new("Frame",BTImportUI)
	local UICorner = Instance.new("UICorner",Boarder)
	local AspectRatio = Instance.new("UIAspectRatioConstraint",Boarder)

	local TextBox = Instance.new("TextBox",Boarder)
	local ButtonBGDecor = Instance.new("Frame",Boarder)
	local ImportButton = Instance.new("TextButton",ButtonBGDecor)

	local ToggleAnchor = Instance.new("ImageButton",ButtonBGDecor)
	local ToggleGroups = Instance.new("ImageButton",ButtonBGDecor)
	
	local txt = Instance.new("TextLabel",Boarder)
	
	local function t(I)
		if I.Rotation == 0 then
			I.Rotation = 21
			I.BackgroundColor3 = Color3.new(0.2, 1, 0.3)
		else
			I:ResetPropertyToDefault("BackgroundColor3")
			I:ResetPropertyToDefault("Rotation")
		end
	end
	
	BTImportUI.Parent = game.Players.LocalPlayer.PlayerGui

	Boarder.AnchorPoint = Vector2.one/2
	Boarder.Position = UDim2.fromScale(.5,.5)

	Boarder:TweenSize(UDim2.fromOffset(300,300))

	Boarder.BackgroundColor3 = Color3.new()
	Boarder.Transparency = 0.3

	UICorner.CornerRadius = UDim.new(.15,0)

	AspectRatio.AspectRatio = 2

	TextBox.Size = UDim2.fromScale(0.8,0.3)
	TextBox.Position = UDim2.fromScale(0.1,0.1)
	TextBox.TextScaled = true
	TextBox.BackgroundColor3 = Color3.new(1, 1, 1)
	TextBox.Text = ""
	TextBox.PlaceholderText = "Whats your Creation ID?\nLeave blank to cancel"
	UICorner:Clone().Parent = TextBox

	ButtonBGDecor.Size = UDim2.fromScale(0.5,0.3)
	ButtonBGDecor.Position = UDim2.fromScale(0.25,0.55)
	ButtonBGDecor.BackgroundColor3 = Color3.new(1, .5)

	ImportButton.Size = UDim2.fromScale(1,0.9)
	ImportButton.BackgroundColor3 = Color3.new(1, 1)
	ImportButton.TextScaled = true
	ImportButton.Text = "Import"

	ToggleAnchor.Image = "rbxasset://textures/TagEditor/famfamfam.png"
	ToggleAnchor.ImageRectSize = Vector2.one*16
	ToggleAnchor.ImageRectOffset = Vector2.yAxis*16
	ToggleAnchor.Size = UDim2.fromOffset(16,16)
	ToggleAnchor.Position = UDim2.fromScale(1.05,0)
	ToggleAnchor.MouseButton1Click:Connect(function() t(ToggleAnchor) end)
	if ForcedAnchor then t(ToggleAnchor) end
	
	ToggleGroups.Image = "rbxasset://textures/ClassImages.png"
	ToggleGroups.ImageRectSize = Vector2.one*16
	ToggleGroups.ImageRectOffset = Vector2.xAxis*2*16
	ToggleGroups.Size = ToggleAnchor.Size
	ToggleGroups.Position = ToggleAnchor.Position + UDim2.fromOffset(0,16)
	ToggleGroups.MouseButton1Click:Connect(function() t(ToggleGroups) end)
	if Group then t(ToggleGroups) end
	
	txt.Text = "This wont work on all F3X Building Tools."
	txt.TextXAlignment = Enum.TextXAlignment.Left
	txt.TextYAlignment = Enum.TextYAlignment.Bottom
	txt.TextColor3 = Color3.new(1,1,1)
	txt.TextStrokeTransparency = 0.5
	
	local function CloseUI()
		Boarder:TweenSizeAndPosition(UDim2.fromScale(0,0),UDim2.fromScale(.5,.5),nil,nil,0.5)
		wait(1)
		BTImportUI:Destroy()
	end

	repeat
		ImportButton.MouseButton1Click:Wait()
		F3X_ImportCode = TextBox.Text

		if F3X_ImportCode == "" then
			TextBox.Text = "Are you sure you want to cancel?"
		end
	until F3X_ImportCode ~= ""

	if F3X_ImportCode == "Are you sure you want to cancel?" then
		CloseUI()
		return
	end

	ImportButton.Text = "Close GUI"
	ImportButton.MouseButton1Click:Once(CloseUI)

	TextBox.ClearTextOnFocus = false
	TextBox.TextEditable = false
	ToggleAnchor.Visible = false
	ToggleGroups.Visible = false
	
	Log = function(...)
		TextBox.Text = ""
		for _,v in ipairs{...} do
			TextBox.Text = TextBox.Text.. tostring(v)
		end
	end
	
	Group = ToggleGroups == 21
	ForcedAnchor = ToggleAnchor == 21
end



print("FETCHING F3X SERVER...")
local build
if isfile("f3x/"..F3X_ImportCode) then
	build = readfile("f3x/"..F3X_ImportCode)
else
	build = game:HttpGet('http://www.f3xteam.com/bt/export/'..F3X_ImportCode)
	writefile("f3x/"..F3X_ImportCode, build)
end

local success, Data = pcall(httpservice.JSONDecode,httpservice,build)
assert(success, "ERROR: FAILED TO DECODE JSON STRING\nDid You write the correct code?")
assert(Data.Version == 3, "ERROR: ONLY SUPPORTED SerializationV3.")
build = nil


function GetSyncAPI()
	return  if Player.Character 
		and Player.Character:FindFirstChildOfClass("Tool") 
		and Player.Character:FindFirstChildOfClass("Tool"):FindFirstChildOfClass("BindableFunction")
		then Player.Character:FindFirstChildOfClass("Tool"):FindFirstChildOfClass("BindableFunction")
		else Player.Backpack:FindFirstChild("SyncAPI",true)
end

local SyncAPI

print("Grabbing SyncAPI...")
repeat SyncAPI = GetSyncAPI(); wait() until SyncAPI 

if not SyncAPI.Parent.Loaded.value then
	SyncAPI.Parent.Loaded.Changed:wait()
end

local function f(...)
	return { SyncAPI:Invoke(...) }
end

function Invoke(...)
	local s, r = pcall(f,...)
	
	if not s then
		warn(r)
	end
	assert(s,"ERROR: The server returned an error.")
	
	return table.unpack(r)
end

local MappingInstances = {}

local Parts = {}

Parent.ChildAdded:Connect(function(Part)
	if Part:IsA("BasePart") and not Part:IsA("MeshPart") then
		table.insert(Parts,Part)
	end 
end)

function GetSparePart(PartType)
	for i,Part in ipairs(Parts) do
		if not table.find(MappingInstances,Part) then
			if (Part:IsA("Seat") and PartTypes.FromTypes[Types.Seat]) == PartType
				or (Part:IsA("VehicleSeat") and PartTypes.FromTypes[Types.VehicleSeat]) == PartType
				or (Part:IsA("TrussPart") and PartTypes.FromTypes[Types.TrussPart]) == PartType
				or PartTypes[Part.Shape.Value] == PartType then
				return Part
			end
		end
	end
end

function CreatePart(PartType, CF, Parent)
	CF = CF or RealativeCFrame

	local Part, replicationTag = Invoke('CreatePart', PartType, CF, Parent)	

	if (Part == nil) and replicationTag then
		local existingMarker = CollectionService:GetTagged(replicationTag)[1]
		if existingMarker then
			Part = existingMarker.Parent
		else
			Part = CollectionService:GetInstanceAddedSignal(replicationTag):Wait().Parent
			if not Part then
				repeat wait((tick()%0.000001) / 0.000001); Part = GetSparePart(PartType) until Part
				
			end
		end
	end

	return Part
end

function IsAPart(Id)
	local Datum = Data.Items[Id]
	if Datum
		and (Datum[1] == Types.Part
			or Datum[1] == Types.WedgePart
			or Datum[1] == Types.CornerWedgePart
			or Datum[1] == Types.VehicleSeat
			or Datum[1] == Types.Seat
			or Datum[1] == Types.TrussPart)
	then
		return true
	else
		return false
	end
end

print("Checking Configuration")

if not Parent then
	Parent = workspace
end

if not RealativeCFrame then
	RealativeCFrame = 
		(Parent:FindFirstChild(F3X_ImportCode) and Parent[F3X_ImportCode]:IsA("BasePart") and Parent[F3X_ImportCode].CFrame)
		or CFrame.new() + (Player.Character or Player.CharacterAdded:Wait()).PrimaryPart.Position
end

Parts[1] = CreatePart("Normal",RealativeCFrame,Parent)

print("Generating Parts...")

local PartCount = 0
local FinPart = 0

for Index,Datum in ipairs(Data.Items) do
	if IsAPart(Index) then
		PartCount += 1
		local PartType = PartTypes.FromTypes[Datum[1]] or PartTypes[Datum[33]]
		tcall(function()
			local Item =  CreatePart(PartType,RealativeCFrame,Parent)
			
			MappingInstances[Index] = Item
			FinPart += 1
		end)
	end
end

repeat wait(); ThreadWatch:GarbageCollect() until #ThreadWatch.Threads <= 0

--Update Part Properties

function ChangeSet(Table,Value)
	if not Table.ns then
		Table.ns = {}
	end

	local Offset = Table.ns[Value.Part] or 0
	Offset += 1

	if not Table[Offset] then
		Table[Offset] = {}
	end
	
	Table.ns[Value.Part] = Offset
	
	table.insert(Table[Offset],Value)
end

function ApplyChange(Call,Table,Filter)
	if Filter then
		for _,v in ipairs(Table) do
			Invoke(Call,filterProperties(v,table.unpack(Filter)))
		end
	else
		for _,v in ipairs(Table) do
			Invoke(Call,v)
		end
	end
end

print("Grouping...")

local Childrens = {}

-- "buffer"?
for Index,Datum in ipairs(Data.Items) do
	if IsAPart(Index)
		or Datum[1] == Types.Model
		or Datum[1] == Types.Folder
	then

		if not Childrens[Datum[2]] then
			Childrens[Datum[2]] = {Index}
		else
			table.insert(Childrens[Datum[2]],Index)
		end
		
	end
end

function Proc(Index)
	if not Childrens[Index] then
		return
	end

	local Object = MappingInstances[Index]
	local Instances = {}
	
	if Index == 0 then
		Object = Parent
	end
	
	for _,v in ipairs(Childrens[Index]) do
		table.insert(Instances,MappingInstances[v])
	end

	if Object then
		tcall(Invoke,"SetParent",Instances,Object)
	else
		local Datum = Data.Items[Index]
		local P = MappingInstances[Datum[2]]
		
		if Datum[2] == 0 then
			P = Parent
		end
		
		local groupType
		if Datum[1] == Types.Model then
			groupType = "Model"
		elseif Datum[1] == Types.Folder then
			groupType = "Folder"
		end
		
		local Model = Invoke("CreateGroup",groupType,P,Instances)
		
		MappingInstances[Index] = Model
	end

	for _,v in ipairs(Childrens[Index]) do
		tcall(Proc,v)
	end
end

if Group then
	Proc(0)
end

repeat wait(); ThreadWatch:GarbageCollect() until #ThreadWatch.Threads <= 0

print("Updating Part Properties...")

local Meshes = {}
local Textures = {}
local Lights = {}
local Decorations = {}
local Parts = {}


local naming = {}

for Index,Datum in ipairs(Data.Items) do
	
	if IsAPart(Index) then

		local Anchored = ForcedAnchor or Datum[23] == 1

		ChangeSet(Parts,{
			["Part"] = MappingInstances[Index];
			["Size"] = Vector3.new(unpack(Support.Slice(Datum, 4, 6)));
			["CFrame"] = RealativeCFrame:ToWorldSpace(CFrame.new(unpack(Support.Slice(Datum, 7, 18))));
			["Color"] = Color3.new(Datum[19], Datum[20], Datum[21]);
			["Material"] = Enum.Material:FromValue(Datum[22]);
			["Anchored"] = Anchored;
			["CanCollide"] = Datum[24] == 1;
			["Reflectance"] = Datum[25];
			["Transparency"] = Datum[26];
			["Surfaces"]={
				["Top"] = Enum.SurfaceType:FromValue(Datum[27]);
				["Bottom"] = Enum.SurfaceType:FromValue(Datum[28]);
				["Front"] = Enum.SurfaceType:FromValue(Datum[29]);
				["Back"] = Enum.SurfaceType:FromValue(Datum[30]);
				["Left"] = Enum.SurfaceType:FromValue(Datum[31]);
				["Right"] = Enum.SurfaceType:FromValue(Datum[32]);
			};
		})

	end
	
	if not MappingInstances[Datum[2]] then
		continue
	end

	local Part = MappingInstances[Datum[2]]

	--MESHES
	if Datum[1] == Types.SpecialMesh then
		ChangeSet(Meshes,{
			["Part"] = Part;
			["VertexColor"] = Vector3.new(unpack(Support.Slice(Datum, 13, 15)));
			["MeshType"] = Enum.MeshType:FromValue(Datum[4]);
			["Scale"] = Vector3.new(unpack(Support.Slice(Datum, 10, 12)));
			["Offset"] = Vector3.new(unpack(Support.Slice(Datum, 7, 9)));
			["MeshId"] = Datum[5];
			["TextureId"] = Datum[6];
		})
	end

	--TEXTURES & DECALS
	if (Datum[1] == Types.Texture 
		or Datum[1] == Types.Decal)
	then
		ChangeSet(Textures,{
			["Part"] = Part;
			["TextureType"] = if Datum[1] == Types.Texture then "Texture" else "Decal";
			["Texture"] = Datum[4];
			["Transparency"] = Datum[5];
			["Face"] = Enum.NormalId:FromValue(Datum[6]);
			["StudsPerTileU"] = Datum[7];
			["StudsPerTileV"] = Datum[8];
		})
	end


	-- Lights
	if (Datum[1] == Types.PointLight 
		or Datum[1] == Types.SpotLight
		or Datum[1] == Types.SurfaceLight)
	then
		local LightTypeFromTypes = {
			[Types.PointLight] = "PointLight",
			[Types.SpotLight] = "SpotLight",
			[Types.SurfaceLight] = "SurfaceLight"
		}

		ChangeSet(Lights,{
			["Part"] = Part;
			["LightType"] = LightTypeFromTypes[Datum[1]];
			["Brightness"] = Datum[4];
			["Color"] = Color3.new(unpack(Support.Slice(Datum, 5, 7)));
			["Enabled"] = Datum[8] == 1;
			["Shadows"] = Datum[9] == 1;
			["Range"] = Datum[10];
			["Angle"] = Datum[11];
			["Face"] = if Datum[12] then Enum.NormalId:FromValue(Datum[12]) else nil;
		})
	end

	--DECORATIONS

	if Datum[1] == Types.Smoke then
		ChangeSet(Decorations,{
			["Part"] = Part;
			["DecorationType"] = "Smoke";

			["Enabled"] = Datum[4] == 1;
			["Color"] = Color3.new(unpack(Support.Slice(Datum, 5, 7)));
			["Size"] = Datum[8];
			["RiseVelocity"] = Datum[9];
			["Opacity"] = Datum[10];
		})
	end;

	if Datum[1] == Types.Fire then
		ChangeSet(Decorations,{
			["Part"] = Part;
			["DecorationType"] = "Fire";

			["Enabled"] = Datum[4] == 1;
			["Color"] = Color3.new(unpack(Support.Slice(Datum, 5, 7)));
			["SecondaryColor"] = Color3.new(unpack(Support.Slice(Datum, 8, 10)));
			["Heat"] = Datum[11];
			["Size"] = Datum[12];
		})
	end;

	if Datum[1] == Types.Sparkles then
		ChangeSet(Decorations,{
			["Part"] = Part;
			["DecorationType"] = "Sparkles";

			["Enabled"] = Datum[4] == 1;
			["SparkleColor"] = Color3.new(unpack(Support.Slice(Datum, 5, 7)));
		})
	end;
	
	if IsAPart(Index)
		or Datum[1] == Types.Model
		or Datum[1] == Types.Folder
	then
		if Datum[3] ~= '' and MappingInstances[Index] then
			if not naming[Datum[3]] then
				naming[Datum[3]] = {}
			end
			table.insert(naming[Datum[3]],MappingInstances[Index])
		end
	end
end

tcall(function()
	ApplyChange("CreateMeshes",Meshes,{"Part"})
	ApplyChange("SyncMesh",Meshes)
end)

tcall(function()
	ApplyChange("CreateTextures",Textures,{"Part","TextureType","Face"})
	ApplyChange("SyncTexture",Textures)
end)

tcall(function()
	ApplyChange("CreateLights",Lights,{"Part","LightType"})
	ApplyChange("SyncLighting",Lights)
end)

tcall(function()
	ApplyChange("CreateDecorations",Decorations,{"Part","DecorationType"})
	ApplyChange("SyncDecorate",Decorations,{"Part","Enabled","SparkleColor","Color","SecondaryColor","Heat","Size","RiseVelocity","Opacity"})
end)

do
	local function T(...)
		tcall(ApplyChange,...)
	end
	T("SyncColor",Parts,{"Part","Color"})
	T("SyncMaterial",Parts,{"Part","Material","Transparency","Reflectance"})
	T("SyncCollision",Parts,{"Part","CanCollide"})
	T("SyncSurface",Parts,{"Part","Surfaces"})
	T("SyncResize",Parts,{"Part","Size","CFrame"})
	T("SyncAnchor",Parts,{"Part","Anchored"})
end



for i,v in pairs(naming) do
	tcall(Invoke,"SetName", v, i)
end

repeat wait(); ThreadWatch:GarbageCollect() until #ThreadWatch.Threads <= 0

print("Done!")

Features & Information

Allows you to import f3x builds in game.

Community Comments (2)

Skid_Lord_SupremeSkid_Lord_SupremeApr 10, 2025

bro it only creates a single part

JustAHumanThatLiveJustAHumanThatLiveSep 28, 2026

works

More Scripts for Universal Scripts