Merge branch 'master' of https://github.com/boatbomber/rojo into headless-api

This commit is contained in:
boatbomber 2023-10-30 19:13:31 -04:00
commit 15f5a89525
82 changed files with 5314 additions and 942 deletions

1
plugin/Version.txt Normal file
View file

@ -0,0 +1 @@
7.4.0-rc3

View file

@ -1,25 +1,27 @@
{
"name": "Rojo",
"tree": {
"$className": "Folder",
"Plugin": {
"$path": "src"
},
"Packages": {
"$path": "Packages",
"Log": {
"$path": "log"
},
"Http": {
"$path": "http"
},
"Fmt": {
"$path": "fmt"
},
"RbxDom": {
"$path": "rbx_dom_lua"
}
}
}
}
"name": "Rojo",
"tree": {
"$className": "Folder",
"Plugin": {
"$path": "src"
},
"Packages": {
"$path": "Packages",
"Log": {
"$path": "log"
},
"Http": {
"$path": "http"
},
"Fmt": {
"$path": "fmt"
},
"RbxDom": {
"$path": "rbx_dom_lua"
}
},
"Version": {
"$path": "Version.txt"
}
}
}

View file

@ -20,8 +20,8 @@ local function serializeFloat(value)
return value
end
local ALL_AXES = {"X", "Y", "Z"}
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"}
local ALL_AXES = { "X", "Y", "Z" }
local ALL_FACES = { "Right", "Top", "Back", "Left", "Bottom", "Front" }
local EncodedValue = {}
@ -37,7 +37,10 @@ types = {
if ok then
output[key] = result
else
local warning = ("Could not decode attribute value of type %q: %s"):format(typeof(value), tostring(result))
local warning = ("Could not decode attribute value of type %q: %s"):format(
typeof(value),
tostring(result)
)
warn(warning)
end
end
@ -53,7 +56,10 @@ types = {
if ok then
output[key] = result
else
local warning = ("Could not encode attribute value of type %q: %s"):format(typeof(value), tostring(result))
local warning = ("Could not encode attribute value of type %q: %s"):format(
typeof(value),
tostring(result)
)
warn(warning)
end
end
@ -111,6 +117,7 @@ types = {
local pos = pod.position
local orient = pod.orientation
--stylua: ignore
return CFrame.new(
pos[1], pos[2], pos[3],
orient[1][1], orient[1][2], orient[1][3],
@ -120,17 +127,14 @@ types = {
end,
toPod = function(roblox)
local x, y, z,
r00, r01, r02,
r10, r11, r12,
r20, r21, r22 = roblox:GetComponents()
local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = roblox:GetComponents()
return {
position = {x, y, z},
position = { x, y, z },
orientation = {
{r00, r01, r02},
{r10, r11, r12},
{r20, r21, r22},
{ r00, r01, r02 },
{ r10, r11, r12 },
{ r20, r21, r22 },
},
}
end,
@ -140,7 +144,7 @@ types = {
fromPod = unpackDecoder(Color3.new),
toPod = function(roblox)
return {roblox.r, roblox.g, roblox.b}
return { roblox.r, roblox.g, roblox.b }
end,
},
@ -161,10 +165,7 @@ types = {
local keypoints = {}
for index, keypoint in ipairs(pod.keypoints) do
keypoints[index] = ColorSequenceKeypoint.new(
keypoint.time,
types.Color3.fromPod(keypoint.color)
)
keypoints[index] = ColorSequenceKeypoint.new(keypoint.time, types.Color3.fromPod(keypoint.color))
end
return ColorSequence.new(keypoints)
@ -279,7 +280,7 @@ types = {
pod[material.Name] = {
math.round(math.clamp(color.R, 0, 1) * 255),
math.round(math.clamp(color.G, 0, 1) * 255),
math.round(math.clamp(color.B, 0, 1) * 255)
math.round(math.clamp(color.B, 0, 1) * 255),
}
end
return pod
@ -290,7 +291,7 @@ types = {
fromPod = unpackDecoder(NumberRange.new),
toPod = function(roblox)
return {roblox.Min, roblox.Max}
return { roblox.Min, roblox.Max }
end,
},
@ -299,11 +300,7 @@ types = {
local keypoints = {}
for index, keypoint in ipairs(pod.keypoints) do
keypoints[index] = NumberSequenceKeypoint.new(
keypoint.time,
keypoint.value,
keypoint.envelope
)
keypoints[index] = NumberSequenceKeypoint.new(keypoint.time, keypoint.value, keypoint.envelope)
end
return NumberSequence.new(keypoints)
@ -358,10 +355,7 @@ types = {
Ray = {
fromPod = function(pod)
return Ray.new(
types.Vector3.fromPod(pod.origin),
types.Vector3.fromPod(pod.direction)
)
return Ray.new(types.Vector3.fromPod(pod.origin), types.Vector3.fromPod(pod.direction))
end,
toPod = function(roblox)
@ -374,10 +368,7 @@ types = {
Rect = {
fromPod = function(pod)
return Rect.new(
types.Vector2.fromPod(pod[1]),
types.Vector2.fromPod(pod[2])
)
return Rect.new(types.Vector2.fromPod(pod[1]), types.Vector2.fromPod(pod[2]))
end,
toPod = function(roblox)
@ -389,31 +380,28 @@ types = {
},
Ref = {
fromPod = function(_pod)
fromPod = function(_)
error("Ref cannot be decoded on its own")
end,
toPod = function(_roblox)
toPod = function(_)
error("Ref can not be encoded on its own")
end,
},
Region3 = {
fromPod = function(pod)
fromPod = function(_)
error("Region3 is not implemented")
end,
toPod = function(roblox)
toPod = function(_)
error("Region3 is not implemented")
end,
},
Region3int16 = {
fromPod = function(pod)
return Region3int16.new(
types.Vector3int16.fromPod(pod[1]),
types.Vector3int16.fromPod(pod[2])
)
return Region3int16.new(types.Vector3int16.fromPod(pod[1]), types.Vector3int16.fromPod(pod[2]))
end,
toPod = function(roblox)
@ -425,11 +413,11 @@ types = {
},
SharedString = {
fromPod = function(pod)
fromPod = function(_pod)
error("SharedString is not supported")
end,
toPod = function(roblox)
toPod = function(_roblox)
error("SharedString is not supported")
end,
},
@ -443,16 +431,13 @@ types = {
fromPod = unpackDecoder(UDim.new),
toPod = function(roblox)
return {roblox.Scale, roblox.Offset}
return { roblox.Scale, roblox.Offset }
end,
},
UDim2 = {
fromPod = function(pod)
return UDim2.new(
types.UDim.fromPod(pod[1]),
types.UDim.fromPod(pod[2])
)
return UDim2.new(types.UDim.fromPod(pod[1]), types.UDim.fromPod(pod[2]))
end,
toPod = function(roblox)
@ -483,7 +468,7 @@ types = {
fromPod = unpackDecoder(Vector2int16.new),
toPod = function(roblox)
return {roblox.X, roblox.Y}
return { roblox.X, roblox.Y }
end,
},
@ -503,7 +488,7 @@ types = {
fromPod = unpackDecoder(Vector3int16.new),
toPod = function(roblox)
return {roblox.X, roblox.Y, roblox.Z}
return { roblox.X, roblox.Y, roblox.Z }
end,
},
}

View file

@ -1,67 +0,0 @@
return function()
local HttpService = game:GetService("HttpService")
local EncodedValue = require(script.Parent.EncodedValue)
local allValues = require(script.Parent.allValues)
local function deepEq(a, b)
if typeof(a) ~= typeof(b) then
return false
end
local ty = typeof(a)
if ty == "table" then
local visited = {}
for key, valueA in a do
visited[key] = true
if not deepEq(valueA, b[key]) then
return false
end
end
for key, valueB in b do
if visited[key] then
continue
end
if not deepEq(valueB, a[key]) then
return false
end
end
return true
else
return a == b
end
end
local extraAssertions = {
CFrame = function(value)
expect(value).to.equal(CFrame.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
end,
}
for testName, testEntry in allValues do
it("round trip " .. testName, function()
local decodeOk, decoded = EncodedValue.decode(testEntry.value)
assert(decodeOk, decoded)
if extraAssertions[testName] ~= nil then
extraAssertions[testName](decoded)
end
local encodeOk, encoded = EncodedValue.encode(decoded, testEntry.ty)
assert(encodeOk, encoded)
if not deepEq(encoded, testEntry.value) then
local expected = HttpService:JSONEncode(testEntry.value)
local actual = HttpService:JSONEncode(encoded)
error(`Round-trip results did not match.\nExpected:\n{expected}\nActual:\n{actual}`)
end
end)
end
end

View file

@ -25,4 +25,4 @@ function Error:__tostring()
return ("Error(%s: %s)"):format(self.kind, tostring(self.extra))
end
return Error
return Error

View file

@ -136,4 +136,4 @@ end
return {
decode = decodeBase64,
encode = encodeBase64,
}
}

View file

@ -1,29 +0,0 @@
return function()
local base64 = require(script.Parent.base64)
it("should encode and decode", function()
local function try(str, expected)
local encoded = base64.encode(str)
expect(encoded).to.equal(expected)
expect(base64.decode(encoded)).to.equal(str)
end
try("Man", "TWFu")
try("Ma", "TWE=")
try("M", "TQ==")
try("ManM", "TWFuTQ==")
try(
[[Man is distinguished, not only by his reason, but by this ]]..
[[singular passion from other animals, which is a lust of the ]]..
[[mind, that by a perseverance of delight in the continued and ]]..
[[indefatigable generation of knowledge, exceeds the short ]]..
[[vehemence of any carnal pleasure.]],
[[TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sI]]..
[[GJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYW]]..
[[xzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJ]]..
[[zZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRl]]..
[[ZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZ]]..
[[SBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=]]
)
end)
end

View file

@ -1,4 +1,5 @@
local CollectionService = game:GetService("CollectionService")
local ScriptEditorService = game:GetService("ScriptEditorService")
--- A list of `Enum.Material` values that are used for Terrain.MaterialColors
local TERRAIN_MATERIAL_COLORS = {
@ -36,9 +37,24 @@ return {
end,
write = function(instance, _, value)
local existing = instance:GetAttributes()
local didAllWritesSucceed = true
for key, attr in pairs(value) do
instance:SetAttribute(key, attr)
for attributeName, attributeValue in pairs(value) do
local isNameValid =
-- For our SetAttribute to succeed, the attribute name must be
-- less than or equal to 100 characters...
#attributeName <= 100
-- ...must only contain alphanumeric characters, periods, hyphens,
-- underscores, or forward slashes...
and attributeName:match("[^%w%.%-_/]") == nil
-- ... and must not use the RBX prefix, which is reserved by Roblox.
and attributeName:sub(1, 3) ~= "RBX"
if isNameValid then
instance:SetAttribute(attributeName, attributeValue)
else
didAllWritesSucceed = false
end
end
for key in pairs(existing) do
@ -47,7 +63,7 @@ return {
end
end
return true
return didAllWritesSucceed
end,
},
Tags = {
@ -77,10 +93,10 @@ return {
},
LocalizationTable = {
Contents = {
read = function(instance, key)
read = function(instance, _)
return true, instance:GetContents()
end,
write = function(instance, key, value)
write = function(instance, _, value)
instance:SetContents(value)
return true
end,
@ -116,4 +132,34 @@ return {
end,
},
},
Script = {
Source = {
read = function(instance: Script)
return true, ScriptEditorService:GetEditorSource(instance)
end,
write = function(instance: Script, _, value: string)
task.spawn(function()
ScriptEditorService:UpdateSourceAsync(instance, function()
return value
end)
end)
return true
end,
},
},
ModuleScript = {
Source = {
read = function(instance: ModuleScript)
return true, ScriptEditorService:GetEditorSource(instance)
end,
write = function(instance: ModuleScript, _, value: string)
task.spawn(function()
ScriptEditorService:UpdateSourceAsync(instance, function()
return value
end)
end)
return true
end,
},
},
}

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,8 @@ local function findCanonicalPropertyDescriptor(className, propertyName)
return PropertyDescriptor.fromRaw(
currentClass.Properties[aliasData.AliasFor],
currentClassName,
aliasData.AliasFor)
aliasData.AliasFor
)
end
return nil

View file

@ -1,7 +0,0 @@
return function()
local RbxDom = require(script.Parent)
it("should load", function()
expect(RbxDom).to.be.ok()
end)
end

View file

@ -2,11 +2,24 @@ local strict = require(script.Parent.strict)
local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
local Version = script.Parent.Parent.Version
local major, minor, patch, metadata = Version.Value:match("^(%d+)%.(%d+)%.(%d+)(.*)$")
local realVersion = { major, minor, patch, metadata }
for i = 1, 3 do
local num = tonumber(realVersion[i])
if num then
realVersion[i] = num
else
error(("invalid version `%s` (field %d)"):format(realVersion[i], i))
end
end
return strict("Config", {
isDevBuild = isDevBuild,
codename = "Epiphany",
version = { 7, 3, 0 },
expectedServerVersionString = "7.2 or newer",
version = realVersion,
expectedServerVersionString = ("%d.%d or newer"):format(realVersion[1], realVersion[2]),
protocolVersion = 4,
defaultHost = "localhost",
defaultPort = "34872",

View file

@ -233,7 +233,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders)
addProp(
prop,
if currentSuccess then currentValue else "[Error]",
if incomingSuccess then incomingValue else next(incoming)
if incomingSuccess then incomingValue else select(2, next(incoming))
)
end
@ -359,7 +359,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders)
if success then
table.insert(changeList, { prop, "N/A", incomingValue })
else
table.insert(changeList, { prop, "N/A", next(incoming) })
table.insert(changeList, { prop, "N/A", select(2, next(incoming)) })
end
end

View file

@ -282,7 +282,7 @@ function ServeSession:__initialSync(serverInfo)
local unappliedPatch = self.__reconciler:applyPatch(catchUpPatch)
if not PatchSet.isEmpty(unappliedPatch) then
Log.warn(
Log.debug(
"Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch)
)
@ -309,7 +309,7 @@ function ServeSession:__mainSyncLoop()
local unappliedPatch = self.__reconciler:applyPatch(message)
if not PatchSet.isEmpty(unappliedPatch) then
Log.warn(
Log.debug(
"Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch)
)