roport 1.2.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/roport.js
CHANGED
package/package.json
CHANGED
|
@@ -602,7 +602,7 @@ local function pullChanges()
|
|
|
602
602
|
if data.changes and #data.changes > 0 then
|
|
603
603
|
print("Roport: Pulling " .. #data.changes .. " files...")
|
|
604
604
|
for _, change in ipairs(data.changes) do
|
|
605
|
-
applyUpdate(change.filePath, change.content)
|
|
605
|
+
applyUpdate(change.filePath, change.content, change.absolutePath)
|
|
606
606
|
end
|
|
607
607
|
showToast("Updated " .. #data.changes .. " files", "success")
|
|
608
608
|
else
|
|
@@ -746,7 +746,7 @@ end
|
|
|
746
746
|
-- Two-Way Sync Logic
|
|
747
747
|
local function findInstanceByPath(path)
|
|
748
748
|
-- Remove extension
|
|
749
|
-
local cleanPath = path:gsub("%.server%.luau$", ""):gsub("%.client%.luau$", ""):gsub("%.luau$", ""):gsub("/init%.meta%.json$", "")
|
|
749
|
+
local cleanPath = path:gsub("%.server%.luau$", ""):gsub("%.client%.luau$", ""):gsub("%.luau$", ""):gsub("%.server%.lua$", ""):gsub("%.client%.lua$", ""):gsub("%.lua$", ""):gsub("/init%.meta%.json$", "")
|
|
750
750
|
|
|
751
751
|
local parts = cleanPath:split("/")
|
|
752
752
|
if parts[1] ~= "src" then return nil end
|
|
@@ -779,7 +779,7 @@ local function findInstanceByPath(path)
|
|
|
779
779
|
end
|
|
780
780
|
|
|
781
781
|
local function createInstanceByPath(path, content)
|
|
782
|
-
local cleanPath = path:gsub("%.server%.luau$", ""):gsub("%.client%.luau$", ""):gsub("%.luau$", ""):gsub("/init%.meta%.json$", "")
|
|
782
|
+
local cleanPath = path:gsub("%.server%.luau$", ""):gsub("%.client%.luau$", ""):gsub("%.luau$", ""):gsub("%.server%.lua$", ""):gsub("%.client%.lua$", ""):gsub("%.lua$", ""):gsub("/init%.meta%.json$", "")
|
|
783
783
|
local parts = cleanPath:split("/")
|
|
784
784
|
if parts[1] ~= "src" then return nil end
|
|
785
785
|
|
|
@@ -807,9 +807,9 @@ local function createInstanceByPath(path, content)
|
|
|
807
807
|
local className = "Folder"
|
|
808
808
|
|
|
809
809
|
if isLast then
|
|
810
|
-
if path:match("%.server%.luau$") then className = "Script"
|
|
811
|
-
elseif path:match("%.client%.luau$") then className = "LocalScript"
|
|
812
|
-
elseif path:match("%.luau$") then className = "ModuleScript"
|
|
810
|
+
if path:match("%.server%.luau$") or path:match("%.server%.lua$") then className = "Script"
|
|
811
|
+
elseif path:match("%.client%.luau$") or path:match("%.client%.lua$") then className = "LocalScript"
|
|
812
|
+
elseif path:match("%.luau$") or path:match("%.lua$") then className = "ModuleScript"
|
|
813
813
|
elseif path:match("%.json$") then
|
|
814
814
|
-- Try to peek className from content
|
|
815
815
|
local success, data = pcall(function() return HttpService:JSONDecode(content) end)
|
|
@@ -834,7 +834,47 @@ local function createInstanceByPath(path, content)
|
|
|
834
834
|
return current
|
|
835
835
|
end
|
|
836
836
|
|
|
837
|
-
local function applyUpdate(filePath, content)
|
|
837
|
+
local function applyUpdate(filePath, content, absolutePath)
|
|
838
|
+
-- Handle RBXMX
|
|
839
|
+
if filePath:match("%.rbxmx$") and absolutePath then
|
|
840
|
+
local success, model = pcall(function()
|
|
841
|
+
return game:GetService("InsertService"):LoadLocalAsset(absolutePath)
|
|
842
|
+
end)
|
|
843
|
+
|
|
844
|
+
if success and model then
|
|
845
|
+
local parentPath = filePath:match("(.+)/[^/]+$")
|
|
846
|
+
local parentInst = findInstanceByPath(parentPath or "")
|
|
847
|
+
|
|
848
|
+
if not parentInst and parentPath then
|
|
849
|
+
parentInst = createInstanceByPath(parentPath, "")
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
if parentInst then
|
|
853
|
+
local children = model:GetChildren()
|
|
854
|
+
if #children > 0 then
|
|
855
|
+
local newObj = children[1]
|
|
856
|
+
local name = filePath:match("([^/]+)%.rbxmx$")
|
|
857
|
+
local existing = parentInst:FindFirstChild(name)
|
|
858
|
+
|
|
859
|
+
if existing then
|
|
860
|
+
newObj.Name = existing.Name
|
|
861
|
+
newObj.Parent = existing.Parent
|
|
862
|
+
existing:Destroy()
|
|
863
|
+
else
|
|
864
|
+
newObj.Name = name
|
|
865
|
+
newObj.Parent = parentInst
|
|
866
|
+
end
|
|
867
|
+
print("Roport: Imported " .. newObj.Name .. " from " .. filePath)
|
|
868
|
+
end
|
|
869
|
+
else
|
|
870
|
+
warn("Roport: Could not find parent for " .. filePath)
|
|
871
|
+
end
|
|
872
|
+
else
|
|
873
|
+
warn("Roport: Failed to load RBXMX: " .. tostring(model))
|
|
874
|
+
end
|
|
875
|
+
return
|
|
876
|
+
end
|
|
877
|
+
|
|
838
878
|
local inst = findInstanceByPath(filePath)
|
|
839
879
|
if not inst then
|
|
840
880
|
inst = createInstanceByPath(filePath, content)
|
|
@@ -894,7 +934,7 @@ task.spawn(function()
|
|
|
894
934
|
if data.changes and #data.changes > 0 then
|
|
895
935
|
print("Roport: Received " .. #data.changes .. " updates from VS Code")
|
|
896
936
|
for _, change in ipairs(data.changes) do
|
|
897
|
-
applyUpdate(change.filePath, change.content)
|
|
937
|
+
applyUpdate(change.filePath, change.content, change.absolutePath)
|
|
898
938
|
end
|
|
899
939
|
end
|
|
900
940
|
else
|
package/src/server.js
CHANGED
|
@@ -195,9 +195,9 @@ function startServer(port) {
|
|
|
195
195
|
const stat = await fs.stat(fullPath);
|
|
196
196
|
if (stat.isFile()) {
|
|
197
197
|
const content = await fs.readFile(fullPath, 'utf8');
|
|
198
|
-
response.changes.push({ filePath, content });
|
|
198
|
+
response.changes.push({ filePath, absolutePath: fullPath, content });
|
|
199
199
|
} else if (stat.isDirectory()) {
|
|
200
|
-
response.changes.push({ filePath, isDirectory: true });
|
|
200
|
+
response.changes.push({ filePath, absolutePath: fullPath, isDirectory: true });
|
|
201
201
|
}
|
|
202
202
|
} else {
|
|
203
203
|
// File Does Not Exist -> Deletion
|
|
@@ -12,7 +12,7 @@ local RunService = game:GetService("RunService")
|
|
|
12
12
|
local TweenService = game:GetService("TweenService")
|
|
13
13
|
local ScriptEditorService = game:GetService("ScriptEditorService")
|
|
14
14
|
|
|
15
|
-
print("Roport Plugin v1.3.
|
|
15
|
+
print("Roport Plugin v1.3.2 Loaded (Project Config Support)")
|
|
16
16
|
|
|
17
17
|
-- Prevent running as a normal script
|
|
18
18
|
if not plugin then return end
|
|
@@ -331,7 +331,7 @@ statusText.Parent = statusFrame
|
|
|
331
331
|
|
|
332
332
|
-- Helper Functions (Global)
|
|
333
333
|
local function sanitize(name)
|
|
334
|
-
return name:gsub("[\\/:*?\"<>|]", "_")
|
|
334
|
+
return (name:gsub("[\\/:*?\"<>|]", "_"))
|
|
335
335
|
end
|
|
336
336
|
|
|
337
337
|
local function resolveRobloxService(robloxPathArray)
|
|
@@ -361,7 +361,7 @@ local function getPath(inst)
|
|
|
361
361
|
local relative = {}
|
|
362
362
|
local current = inst
|
|
363
363
|
while current ~= root do
|
|
364
|
-
table.insert(relative, 1, sanitize(current.Name))
|
|
364
|
+
table.insert(relative, 1, (sanitize(current.Name)))
|
|
365
365
|
current = current.Parent
|
|
366
366
|
end
|
|
367
367
|
|
|
@@ -685,7 +685,7 @@ local function pullChanges()
|
|
|
685
685
|
if data.changes and #data.changes > 0 then
|
|
686
686
|
print("Roport: Pulling " .. #data.changes .. " files...")
|
|
687
687
|
for _, change in ipairs(data.changes) do
|
|
688
|
-
applyUpdate(change.filePath, change.content)
|
|
688
|
+
applyUpdate(change.filePath, change.content, change.absolutePath)
|
|
689
689
|
end
|
|
690
690
|
showToast("Updated " .. #data.changes .. " files", "success")
|
|
691
691
|
else
|
|
@@ -788,6 +788,8 @@ local function createSettingInput(label, default, callback)
|
|
|
788
788
|
end)
|
|
789
789
|
end
|
|
790
790
|
|
|
791
|
+
if not state.settings then state.settings = { port = 3456, syncInterval = 2 } end
|
|
792
|
+
|
|
791
793
|
createSettingInput("Server Port", state.settings.port, function(val)
|
|
792
794
|
local n = tonumber(val)
|
|
793
795
|
if n then
|
|
@@ -958,97 +960,8 @@ local function findInstanceByPath(path)
|
|
|
958
960
|
return nil
|
|
959
961
|
end
|
|
960
962
|
|
|
961
|
-
-- XML Parser for .rbxmx support
|
|
962
|
-
local function parseRbxmx(content)
|
|
963
|
-
local roots = {}
|
|
964
|
-
local stack = {}
|
|
965
|
-
local current = nil
|
|
966
|
-
|
|
967
|
-
-- Helper to decode entities
|
|
968
|
-
local function decode(s)
|
|
969
|
-
return s:gsub("<", "<"):gsub(">", ">"):gsub(""", '"'):gsub("'", "'"):gsub("&", "&")
|
|
970
|
-
end
|
|
971
|
-
|
|
972
|
-
-- 1. Extract Items
|
|
973
|
-
-- We iterate through the string finding <Item ...> and </Item>
|
|
974
|
-
-- This is a simplified parser that assumes well-formed Roblox XML
|
|
975
|
-
|
|
976
|
-
local pos = 1
|
|
977
|
-
while true do
|
|
978
|
-
local s, e, tag, attrs = content:find("<Item%s+([^>]+)>", pos)
|
|
979
|
-
local sEnd, eEnd = content:find("</Item>", pos)
|
|
980
|
-
|
|
981
|
-
if not s and not sEnd then break end
|
|
982
|
-
|
|
983
|
-
if s and (not sEnd or s < sEnd) then
|
|
984
|
-
-- Found start tag
|
|
985
|
-
local class = attrs:match('class="([^"]+)"')
|
|
986
|
-
local inst = Instance.new(class or "Folder")
|
|
987
|
-
|
|
988
|
-
-- Parse Properties immediately following
|
|
989
|
-
local propStart, propEnd = content:find("<Properties>", e)
|
|
990
|
-
if propStart then
|
|
991
|
-
local propContentEnd = content:find("</Properties>", propEnd)
|
|
992
|
-
if propContentEnd then
|
|
993
|
-
local propBlock = content:sub(propEnd + 1, propContentEnd - 1)
|
|
994
|
-
|
|
995
|
-
-- Parse properties
|
|
996
|
-
for pType, pName, pVal in propBlock:gmatch("<(%w+)%s+name=\"([^\"]+)\">([^<]*)</%1>") do
|
|
997
|
-
pcall(function()
|
|
998
|
-
if pType == "string" then inst[pName] = decode(pVal)
|
|
999
|
-
elseif pType == "bool" then inst[pName] = (pVal == "true")
|
|
1000
|
-
elseif pType == "float" or pType == "double" or pType == "int" or pType == "int64" then inst[pName] = tonumber(pVal)
|
|
1001
|
-
elseif pType == "Color3" or pType == "Color3uint8" then
|
|
1002
|
-
-- Complex types usually have nested tags in newer format, or text in older
|
|
1003
|
-
-- This simple parser handles basic types. Complex types need more logic.
|
|
1004
|
-
end
|
|
1005
|
-
end)
|
|
1006
|
-
end
|
|
1007
|
-
|
|
1008
|
-
-- Handle nested tags for complex properties (Vector3, Color3, etc)
|
|
1009
|
-
-- <Vector3 name="Position"><X>0</X><Y>10</Y><Z>0</Z></Vector3>
|
|
1010
|
-
for pType, pName, pInner in propBlock:gmatch("<(%w+)%s+name=\"([^\"]+)\">([%s%S]-)</%1>") do
|
|
1011
|
-
pcall(function()
|
|
1012
|
-
if pType == "Vector3" then
|
|
1013
|
-
local x = tonumber(pInner:match("<X>(.-)</X>"))
|
|
1014
|
-
local y = tonumber(pInner:match("<Y>(.-)</Y>"))
|
|
1015
|
-
local z = tonumber(pInner:match("<Z>(.-)</Z>"))
|
|
1016
|
-
inst[pName] = Vector3.new(x, y, z)
|
|
1017
|
-
elseif pType == "Color3" then
|
|
1018
|
-
local r = tonumber(pInner:match("<R>(.-)</R>"))
|
|
1019
|
-
local g = tonumber(pInner:match("<G>(.-)</G>"))
|
|
1020
|
-
local b = tonumber(pInner:match("<B>(.-)</B>"))
|
|
1021
|
-
inst[pName] = Color3.new(r, g, b)
|
|
1022
|
-
elseif pType == "UDim2" then
|
|
1023
|
-
local xs = tonumber(pInner:match("<XS>(.-)</XS>"))
|
|
1024
|
-
local xo = tonumber(pInner:match("<XO>(.-)</XO>"))
|
|
1025
|
-
local ys = tonumber(pInner:match("<YS>(.-)</YS>"))
|
|
1026
|
-
local yo = tonumber(pInner:match("<YO>(.-)</YO>"))
|
|
1027
|
-
inst[pName] = UDim2.new(xs, xo, ys, yo)
|
|
1028
|
-
end
|
|
1029
|
-
end)
|
|
1030
|
-
end
|
|
1031
|
-
end
|
|
1032
|
-
end
|
|
1033
|
-
|
|
1034
|
-
if current then
|
|
1035
|
-
inst.Parent = current
|
|
1036
|
-
else
|
|
1037
|
-
table.insert(roots, inst)
|
|
1038
|
-
end
|
|
1039
|
-
|
|
1040
|
-
table.insert(stack, current)
|
|
1041
|
-
current = inst
|
|
1042
|
-
pos = e + 1
|
|
1043
|
-
else
|
|
1044
|
-
-- Found end tag
|
|
1045
|
-
current = table.remove(stack)
|
|
1046
|
-
pos = eEnd + 1
|
|
1047
|
-
end
|
|
1048
|
-
end
|
|
1049
|
-
|
|
1050
|
-
return roots
|
|
1051
|
-
end
|
|
963
|
+
-- XML Parser for .rbxmx support (Deprecated/Unused - using InsertService instead)
|
|
964
|
+
-- local function parseRbxmx(content) ... end
|
|
1052
965
|
|
|
1053
966
|
local function createInstanceByPath(path, content)
|
|
1054
967
|
local isInit = path:match("/init%.luau$") or path:match("/init%.server%.luau$") or path:match("/init%.client%.luau$") or path:match("/init%.lua$") or path:match("/init%.server%.lua$") or path:match("/init%.client%.lua$")
|
|
@@ -1133,7 +1046,7 @@ local function createInstanceByPath(path, content)
|
|
|
1133
1046
|
return nil
|
|
1134
1047
|
end
|
|
1135
1048
|
|
|
1136
|
-
local function applyUpdate(filePath, content)
|
|
1049
|
+
local function applyUpdate(filePath, content, absolutePath)
|
|
1137
1050
|
local inst = findInstanceByPath(filePath)
|
|
1138
1051
|
if not inst then
|
|
1139
1052
|
inst = createInstanceByPath(filePath, content)
|
|
@@ -1144,21 +1057,24 @@ local function applyUpdate(filePath, content)
|
|
|
1144
1057
|
inst.Value = content
|
|
1145
1058
|
elseif filePath:match("%.json$") and not filePath:match("%.meta%.json$") and not filePath:match("%.model%.json$") and inst:IsA("ModuleScript") then
|
|
1146
1059
|
inst.Source = "return " .. content
|
|
1147
|
-
elseif filePath:match("%.rbxmx$") then
|
|
1148
|
-
-- Handle Model Update
|
|
1149
|
-
local
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1060
|
+
elseif filePath:match("%.rbxmx$") and absolutePath then
|
|
1061
|
+
-- Handle Model Update via InsertService
|
|
1062
|
+
local success, model = pcall(function()
|
|
1063
|
+
return game:GetService("InsertService"):LoadLocalAsset(absolutePath)
|
|
1064
|
+
end)
|
|
1065
|
+
|
|
1066
|
+
if success and model then
|
|
1067
|
+
local children = model:GetChildren()
|
|
1068
|
+
if #children > 0 then
|
|
1069
|
+
local newObj = children[1]
|
|
1070
|
+
newObj.Name = inst.Name
|
|
1071
|
+
newObj.Parent = inst.Parent
|
|
1072
|
+
inst:Destroy()
|
|
1073
|
+
inst = newObj
|
|
1074
|
+
print("Roport: Imported " .. newObj.Name .. " from " .. filePath)
|
|
1075
|
+
end
|
|
1076
|
+
else
|
|
1077
|
+
warn("Roport: Failed to load RBXMX: " .. tostring(model))
|
|
1162
1078
|
end
|
|
1163
1079
|
elseif filePath:match("%.model%.json$") or filePath:match("%.meta%.json$") then
|
|
1164
1080
|
local success, data = pcall(function() return HttpService:JSONDecode(content) end)
|
|
@@ -1288,7 +1204,7 @@ task.spawn(function()
|
|
|
1288
1204
|
if data.changes and #data.changes > 0 then
|
|
1289
1205
|
print("Roport: Received " .. #data.changes .. " updates from VS Code")
|
|
1290
1206
|
for _, change in ipairs(data.changes) do
|
|
1291
|
-
applyUpdate(change.filePath, change.content)
|
|
1207
|
+
applyUpdate(change.filePath, change.content, change.absolutePath)
|
|
1292
1208
|
end
|
|
1293
1209
|
end
|
|
1294
1210
|
else
|