Created
June 29, 2022 14:32
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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: