Skip to content

Instantly share code, notes, and snippets.

@Anaminus
Created June 29, 2022 14:32
Show Gist options
  • Save Anaminus/94298bab2685278968d9971c310ee09e to your computer and use it in GitHub Desktop.
Save Anaminus/94298bab2685278968d9971c310ee09e to your computer and use it in GitHub Desktop.
rbxmk script to base a Rojo project off of a place file. MUST BE RUN WITH LATEST UNRELEASED VERSION OF RBXMK
--[[
Usage:
rbxmk run place2project.rbxmk.lua --place PATH --project PATH [--output PATH]
Merges an rbxl file with a template project.json file to produce a final
project.
The place file will be converted to a project file, then the template project
file will be merged into it. The template should have the usual content for a
Rojo project, such as `$path` fields.
Any unsupported property types will be displayed.
Options:
--output PATH Path to write the resulting project.json file. (default
--"default.project.json")
--place PATH Path to a rbxl or rbxlx file.
--project PATH Path to a template project.json file.
]]
local types = {}
function types.Axes(v)
return {Axes = {
v.X and "X" or nil,
v.Y and "Y" or nil,
v.Z and "Z" or nil,
}}
end
function types.BinaryString(v)
return {BinaryString = rbxmk.encodeFormat("base64", v)}
end
function types.bool(v)
return {Bool = v}
end
function types.BrickColor(v)
return {BrickColor = v}
end
function types.CFrame(v)
return {CFrame = {
position = {v.Position.X, v.Position.Y, v.Position.Z},
orientation = {select(4, v:GetComponents())},
}}
end
function types.Color3(v)
return {Color3 = {v.R, v.G, v.B}}
end
function types.Color3uint8(v)
return {Color3 = {
math.floor(v.R)*255,
math.floor(v.G)*255,
math.floor(v.B)*255,
}}
end
function types.ColorSequence(v)
local keypoints = {}
for _, k in ipairs(v.Keypoints) do
table.insert(keypoints, {
time = k.Time,
color = {k.Value.R, k.Value.G, k.Value.B},
})
end
return {ColorSequence = {keypoints = keypoints}}
end
function types.Content(v)
return {Content = v}
end
function types.token(v)
return {Enum = type(v) == "number" and v or v.Value}
end
function types.Faces(v)
return {Faces = {
v.Right and "Right" or nil,
v.Top and "Top" or nil,
v.Back and "Back" or nil,
v.Left and "Left" or nil,
v.Bottom and "Bottom" or nil,
v.Front and "Front" or nil,
}}
end
function types.float(v)
return {Float32 = v}
end
function types.double(v)
return {Float64 = v}
end
function types.int(v)
return {Int32 = v}
end
function types.int64(v)
return {Int64 = v}
end
function types.NumberRange(v)
return {NumberRange = {v.Min,v.Max}}
end
function types.NumberSequence(v)
local keypoints = {}
for _, k in ipairs(v.Keypoints) do
table.insert(keypoints, {
time = k.Time,
value = {k.Value.R, k.Value.G, k.Value.B},
envelope = k.Envelope,
})
end
return {NumberSequence = {keypoints = keypoints}}
end
function types.PhysicalProperties(v)
if v == nil then
return {PhysicalProperties = "Default"}
end
return {
PhysicalProperties = {
density = v.Density,
friction = v.Friction,
elasticity = v.Elasticity,
frictionWeight = v.FrictionWeight,
elasticityWeight = v.ElasticityWeight,
}
}
end
function types.ProtectedString(v)
return {ProtectedString = v}
end
function types.Ray(v)
return {Ray = {
origin = {v.Origin.X, v.Origin.Y, v.Origin.Z},
direction = {v.Direction.X, v.Direction.Y, v.Direction.Z},
}}
end
function types.Rect(v)
return {Rect = {
{v.Min.X, v.Min.Y},
{v.Max.X, v.Max.Y},
}}
end
function types.string(v)
return {String = v}
end
function types.UDim(v)
return {UDim = {v.Scale, v.Offset}}
end
function types.UDim2(v)
return {UDim2 = {
{v.X.Scale, v.X.Offset},
{v.Y.Scale, v.Y.Offset},
}}
end
function types.Vector2(v)
return {Vector2 = {v.X,v.Y}}
end
function types.Vector2int16(v)
return {Vector2int16 = {v.X,v.Y}}
end
function types.Vector3(v)
return {Vector3 = {v.X,v.Y,v.Z}}
end
function types.Vector3int16(v)
return {Vector3int16 = {v.X,v.Y,v.Z}}
end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local options = {}
local args = table.pack(...)
local i = 1
local key = nil
while i <= args.n do
local arg = args[i]
local name
if type(arg) == "string" then
name = arg:match("^%-%-?(.*)$")
end
if key then
if name then
options[key] = true
else
options[key] = arg
i = i + 1
end
key = nil
else
key = name
if not key then
table.insert(options, arg)
end
i = i + 1
end
end
if key then
options[key] = true
end
local projectPath = assert(options.project, "--project option must be path to a template project.json file")
local placePath = assert(options.place, "--place option must be path to a rbxl or rbxlx file")
local project = fs.read(projectPath)
local place = fs.read(placePath)
local unsupported = {}
local function mergeProperties(props, instance)
props = props or {}
local properties = instance[sym.Properties]
for name in pairs(properties) do
if name ~= "Name" and props[name] == nil then
local type = rbxmk.propType(instance, name)
local generateValue = types[type]
if generateValue then
local value = rbxmk.get(instance, name)
props[name] = generateValue(value)
else
unsupported[type] = (unsupported[type] or 0) + 1
end
end
end
if next(props) == nil then
return nil
end
return props
end
local function traverse(instance, tree)
for _, child in ipairs(instance:GetChildren()) do
local name = child.Name
local data = tree[name]
if data == nil then
data = {
["$className"] = child.ClassName,
}
tree[name] = data
end
data["$properties"] = mergeProperties(data["$properties"], child)
traverse(child, data)
end
end
traverse(place, project.tree)
local unsupportedTypes = {}
for type in pairs(unsupported) do
table.insert(unsupportedTypes, type)
end
table.sort(unsupportedTypes)
for _, type in ipairs(unsupportedTypes) do
print(string.format("skipped %d properties with unsupported type %s", unsupported[type], type))
end
local output = options.output or "default.project.json"
fs.write(output, project)
print(string.format("successfully wrote %s", output))
@NeutronFlow
Copy link

Caught a couple typos in the types section --

NumberSequence (line 113) seems to have a copy error from ColorSequence. Line 118 should just be value = k.Value

CFrame (line 44): Rojo project format requires separate tables for each row of the rotation matrix. So the function should be something like this:

function types.CFrame(v)
	local orientation = {select(4, v:GetComponents())}
	local v1 = table.move(orientation, 1, 3, 1, {})
	local v2 = table.move(orientation, 4, 6, 1, {})
	local v3 = table.move(orientation, 7, 9, 1, {})
	return {CFrame = {
		position = {v.Position.X, v.Position.Y, v.Position.Z},
		orientation = {v1, v2, v3},
	}}
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment