diff --git a/Sync/Plugin/Plugin.lua b/Sync/Plugin/Plugin.lua index 4d8a1e9..b9b4581 100644 --- a/Sync/Plugin/Plugin.lua +++ b/Sync/Plugin/Plugin.lua @@ -78,7 +78,7 @@ end local idCount = 0 local nCount = 0 -local function notify(text) +local function notify(text, willUpdate) nCount = nCount + 1 idCount = idCount + 1 local id = idCount @@ -86,18 +86,23 @@ local function notify(text) local position = Value(UDim2.new(0, -WIDTH, 0, 60 * nCount - 50)) local transparency = Value(0) local textValue = Value(text) - local textChanged = Observer(textValue) + local textChanged local arrowRotation = Value(0) - local done = Value(false) + local done = Value(not willUpdate) local background = Value(Color3.new()) local backgroundSpring = Spring(background, 4) local start = tick() - local disconn = textChanged:onChange(function() - if tick() - start > 0.5 then -- don't change color if it's just appearing - backgroundSpring:setPosition(Color3.new(0.4, 0.4, 0.4)) - end - end) + local disconn = function() end + + if willUpdate then + textChanged = Observer(textValue) + disconn = textChanged:onChange(function() + if tick() - start > 0.5 then -- don't change color if it's just appearing + backgroundSpring:setPosition(Color3.new(0.4, 0.4, 0.4)) + end + end) + end local t = New "Frame" { Name = "Notification", @@ -173,11 +178,70 @@ local function notify(text) t:Destroy() end) - return { - text = textValue, - arrowRotation = arrowRotation, - done = done, - } + if willUpdate then + return { + text = textValue, + arrowRotation = arrowRotation, + done = done, + } + end + return +end + +local function makeScript(s) -- because no continue + local path = s.path -- { "ServerScriptService", "script" } + local content = s.content + local type = s.type + + local obj = game + local ok2 = pcall(function() + for i = 1, #path - 1 do + obj = obj:FindFirstChild(path[i]) + if not obj then + local folder = Instance.new "Model" + folder.Name = path[i] + folder.Parent = obj + obj = folder + end + end + end) + if not ok2 then + notify( + "Failed to sync " + .. table.concat(path, ".") + .. "! Is the path correct?" + ) + return + end + + local name = path[#path] + local existingObj = obj:FindFirstChild(name) + if existingObj then + if existingObj:IsA "Script" then + existingObj.Source = content + else + notify( + "Object already exists at path " + .. table.concat(path, ".") + .. "! Please remove it and try again." + ) + return + end + else + local createScript + + if type == "server" then + createScript = Instance.new "Script" + elseif type == "client" then + createScript = Instance.new "LocalScript" + else + return + end + + createScript.Name = name + createScript.Source = content + createScript.Parent = obj + end end local debounce @@ -189,7 +253,7 @@ buttons[1].Click:connect(function() debounce = true initiate() - local n = notify "Syncing..." + local n = notify("Syncing...", true) Spawn(function() while not peek(n.done) do @@ -206,13 +270,34 @@ buttons[1].Click:connect(function() ) end) - if ok then - n.text:set("Synced: " .. res) - else - n.text:set "Failed to sync! Is Mercury Sync Server running?" + local function finish() + n.done:set(true) + wait(0.05) + debounce = false end - n.done:set(true) - debounce = false + if not ok then + n.text:set "Failed to sync! Is Mercury Sync Server running?" + finish() + return + end + + n.text:set "Decoding..." + local json = HttpService:JSONDecode(res) -- { files } + + if not json.files or json.files == "null" then + n.text:set "No files to sync!" + finish() + return + end + n.text:set "Applying..." + + for _, v in pairs(json.files) do -- { path, content, type } + makeScript(v) + end + + n.text:set "Successfully synchronised!" + + finish() end) end) diff --git a/Sync/Server/compile.go b/Sync/Server/compile.go new file mode 100644 index 0000000..868546c --- /dev/null +++ b/Sync/Server/compile.go @@ -0,0 +1,21 @@ +package main + +import ( + "os" + "os/exec" +) + +func CompileLuau(sourcePath string) (string, error) { + path, err := exec.LookPath("./tools/darklua") + if err != nil { + return "", err + } + + cmd := exec.Command(path, "process", sourcePath, "./temp.lua") + cmd.Run() + + // Return the compiled file + file, _ := os.ReadFile("./temp.lua") + + return string(file), nil +} diff --git a/Sync/Server/main.go b/Sync/Server/main.go index 258c3aa..b8bcc7f 100644 --- a/Sync/Server/main.go +++ b/Sync/Server/main.go @@ -30,7 +30,8 @@ func main() { } gin.SetMode(gin.ReleaseMode) - r := gin.Default() + r := gin.New() + r.Use(gin.Recovery()) r.SetTrustedProxies([]string{"127.0.0.1"}) r.GET("/", func(cx *gin.Context) { @@ -41,13 +42,16 @@ func main() { // Create struct for JSON response type File struct { - Path string - Content string + Path []string `json:"path"` + Content string `json:"content"` + Type string `json:"type"` } var Response struct { - Files []File + Files []File `json:"files"` + // Message string `json:"message"` } + usedFilenames := make(map[string]bool) // Read files recursively and send them to the client fmt.Println(target) filepath.Walk(target, func(path string, info os.FileInfo, err error) error { @@ -61,47 +65,80 @@ func main() { } var filetype string + var scripttype string switch strings.ToLower(filepath.Ext(path)) { case ".lua": filetype = "lua" case ".luau": filetype = "luau" + case ".moon": + filetype = "moon" + case ".yue": + filetype = "yue" default: return nil } - // Trim target directory and extension from path + // Trim target directory and extension from path, and remove suffix if it's a server/client script formatPath := strings.TrimPrefix(path, target+string(os.PathSeparator)) formatPath = strings.TrimSuffix(formatPath, "."+filetype) + if strings.Contains(formatPath, ".") { + scripttype = strings.Split(formatPath, ".")[1] + if scripttype == "server" || scripttype == "client" { + formatPath = strings.Split(formatPath, ".")[0] + } + } + if scripttype == "" { + // scripttype = "module" + fmt.Println(c.InRed("Unknown script type: ") + c.InUnderline(c.InPurple(formatPath)) + c.InRed("!")) + fmt.Println(c.InYellow("If you were trying to sync a ModuleScript, these are not supported by Mercury Sync. Please transpose them manually.")) + } + formatPath = strings.ReplaceAll(formatPath, string(os.PathSeparator), ".") - fmt.Println(c.InGreen("Sending ")+c.InUnderline(c.InPurple(formatPath))+c.InGreen("...")) - - file, err := os.Open(path) - if err != nil { - fmt.Println(c.InRed("Error while reading file:"), err.Error()) + if usedFilenames[formatPath] { + fmt.Println(c.InRed("Duplicate filename: ") + c.InUnderline(c.InPurple(formatPath)) + c.InRed("! Skipping...")) return nil } + usedFilenames[formatPath] = true - // Parse file contents var content string - buf := make([]byte, 1024) - for { - n, _ := file.Read(buf) - if n == 0 { - break + + switch filetype { + case "luau": + fmt.Println(c.InBlue("Compiling ") + c.InUnderline(c.InPurple(formatPath)) + c.InBlue("...")) + content, err = CompileLuau(path) + if err != nil { + fmt.Println(c.InRed("Error while compiling Luau file:"), err) + if strings.Contains(err.Error(), "file does not exist") || + strings.Contains(err.Error(), "no such file or directory") { + + fmt.Println(c.InYellow("Please place a copy of darklua (name \"darklua\" or \"darklua.exe\") in the tools folder.")) + } + return nil } - content += string(buf[:n]) + default: + file, err := os.ReadFile(path) + if err != nil { + fmt.Println(c.InRed("Error while reading file:"), err) + return nil + } + content = string(file) } + fmt.Println(c.InGreen("Sending ") + c.InUnderline(c.InPurple(formatPath)) + c.InGreen("...")) + Response.Files = append(Response.Files, File{ - Path: formatPath, - Content: content, + Path: strings.Split(formatPath, "."), + Content: strings.ReplaceAll(content, "\r\n", "\n"), + Type: scripttype, }) return nil }) + os.Remove("./temp.lua") + cx.JSON(200, Response) }) diff --git a/aftman.toml b/aftman.toml index 87ff3cd..a1737b0 100644 --- a/aftman.toml +++ b/aftman.toml @@ -3,6 +3,6 @@ # To add a new tool, add an entry to this table. [tools] -rojo = "rojo-rbx/rojo@7.2.1" -selene = "Kampfkarren/selene@0.24.0" -stylua = "johnnymorganz/stylua@0.15.3" +selene = "Kampfkarren/selene@0.25.0" +stylua = "johnnymorganz/stylua@0.18.2" +darklua = "seaofvoices/darklua@0.10.3"