forked from rojo-rbx/rojo
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2495ed57f | ||
|
|
6ad763fc01 | ||
|
|
c856a3e361 | ||
|
|
aa5f0cc335 | ||
|
|
b067335bbf | ||
|
|
7d24a14004 | ||
|
|
910be640e9 | ||
|
|
3137753afa | ||
|
|
000ff351a5 | ||
|
|
533c8ddaf7 | ||
|
|
f777d1b6c6 | ||
|
|
8b17d3b7d9 | ||
|
|
6fbe1daf8e | ||
|
|
3bd191414b | ||
|
|
fd2cb3495b | ||
|
|
e9d33bdc02 | ||
|
|
c0f4b31ab3 | ||
|
|
78de30dcf2 |
19
CHANGES.md
19
CHANGES.md
@@ -1,7 +1,22 @@
|
|||||||
# Rojo Change Log
|
# Rojo Change Log
|
||||||
|
|
||||||
## Current Master
|
## Current master
|
||||||
*No changes*
|
* *No changes*
|
||||||
|
|
||||||
|
## 0.4.8 (May 26, 2018)
|
||||||
|
* Hotfix to prevent errors from being thrown when objects managed by Rojo are deleted
|
||||||
|
|
||||||
|
## 0.4.7 (May 25, 2018)
|
||||||
|
* Added icons to the Rojo plugin, made by [@Vorlias](https://github.com/Vorlias)! ([#70](https://github.com/LPGhatguy/rojo/pull/70))
|
||||||
|
* Server will now issue a warning if no partitions are specified in `rojo serve` ([#40](https://github.com/LPGhatguy/rojo/issues/40))
|
||||||
|
|
||||||
|
## 0.4.6 (May 21, 2018)
|
||||||
|
* Rojo handles being restarted by Roblox Studio more gracefully ([#67](https://github.com/LPGhatguy/rojo/issues/67))
|
||||||
|
* Folders should no longer get collapsed when syncing occurs.
|
||||||
|
* **Significant** robustness improvements with regards to caching.
|
||||||
|
* **This should catch all existing script duplication bugs.**
|
||||||
|
* If there are any bugs with script duplication or caching in the future, restarting the Rojo server process will fix them for that session.
|
||||||
|
* Fixed message in plugin not being prefixed with `Rojo: `.
|
||||||
|
|
||||||
## 0.4.5 (May 1, 2018)
|
## 0.4.5 (May 1, 2018)
|
||||||
* Rojo messages are now prefixed with `Rojo: ` to make them stand out in the output more.
|
* Rojo messages are now prefixed with `Rojo: ` to make them stand out in the output more.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<a href="https://travis-ci.org/LPGhatguy/rojo">
|
<a href="https://travis-ci.org/LPGhatguy/rojo">
|
||||||
<img src="https://api.travis-ci.org/LPGhatguy/rojo.svg?branch=master" alt="Travis-CI Build Status" />
|
<img src="https://api.travis-ci.org/LPGhatguy/rojo.svg?branch=master" alt="Travis-CI Build Status" />
|
||||||
</a>
|
</a>
|
||||||
<img src="https://img.shields.io/badge/latest_version-0.4.5-brightgreen.svg" alt="Current server version" />
|
<img src="https://img.shields.io/badge/latest_version-0.4.8-brightgreen.svg" alt="Current server version" />
|
||||||
<a href="https://lpghatguy.github.io/rojo">
|
<a href="https://lpghatguy.github.io/rojo">
|
||||||
<img src="https://img.shields.io/badge/documentation-website-brightgreen.svg" alt="Rojo Documentation" />
|
<img src="https://img.shields.io/badge/documentation-website-brightgreen.svg" alt="Rojo Documentation" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
BIN
assets/rojo-polling-icon.png
Normal file
BIN
assets/rojo-polling-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 375 B |
BIN
assets/rojo-sync-in.png
Normal file
BIN
assets/rojo-sync-in.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 382 B |
BIN
assets/rojo-test-icon.png
Normal file
BIN
assets/rojo-test-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 430 B |
@@ -1,7 +1,12 @@
|
|||||||
return {
|
return {
|
||||||
pollingRate = 0.2,
|
pollingRate = 0.2,
|
||||||
version = {0, 4, 5},
|
version = {0, 4, 8},
|
||||||
expectedServerVersionString = "0.4.x",
|
expectedServerVersionString = "0.4.x",
|
||||||
protocolVersion = 1,
|
protocolVersion = 1,
|
||||||
|
icons = {
|
||||||
|
syncIn = "rbxassetid://1820320573",
|
||||||
|
togglePolling = "rbxassetid://1820320064",
|
||||||
|
testConnection = "rbxassetid://1820320989",
|
||||||
|
},
|
||||||
dev = false,
|
dev = false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ local function main()
|
|||||||
|
|
||||||
local toolbar = plugin:CreateToolbar("Rojo Plugin " .. displayedVersion)
|
local toolbar = plugin:CreateToolbar("Rojo Plugin " .. displayedVersion)
|
||||||
|
|
||||||
toolbar:CreateButton("Test Connection", "Connect to Rojo Server", "")
|
toolbar:CreateButton("Test Connection", "Connect to Rojo Server", Config.icons.testConnection)
|
||||||
.Click:Connect(function()
|
.Click:Connect(function()
|
||||||
checkUpgrade()
|
checkUpgrade()
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ local function main()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
toolbar:CreateButton("Sync In", "Sync into Roblox Studio", "")
|
toolbar:CreateButton("Sync In", "Sync into Roblox Studio", Config.icons.syncIn)
|
||||||
.Click:Connect(function()
|
.Click:Connect(function()
|
||||||
checkUpgrade()
|
checkUpgrade()
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ local function main()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
toolbar:CreateButton("Toggle Polling", "Poll server for changes", "")
|
toolbar:CreateButton("Toggle Polling", "Poll server for changes", Config.icons.togglePolling)
|
||||||
.Click:Connect(function()
|
.Click:Connect(function()
|
||||||
checkUpgrade()
|
checkUpgrade()
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
local CoreGui = game:GetService("CoreGui")
|
||||||
|
|
||||||
local Promise = require(script.Parent.Parent.modules.Promise)
|
local Promise = require(script.Parent.Parent.modules.Promise)
|
||||||
|
|
||||||
local Config = require(script.Parent.Config)
|
local Config = require(script.Parent.Config)
|
||||||
local Http = require(script.Parent.Http)
|
local Http = require(script.Parent.Http)
|
||||||
local Api = require(script.Parent.Api)
|
local Api = require(script.Parent.Api)
|
||||||
local Reconciler = require(script.Parent.Reconciler)
|
local Reconciler = require(script.Parent.Reconciler)
|
||||||
|
local Version = require(script.Parent.Version)
|
||||||
|
|
||||||
local function collectMatch(source, pattern)
|
local function collectMatch(source, pattern)
|
||||||
local result = {}
|
local result = {}
|
||||||
@@ -35,9 +38,23 @@ function Plugin.new()
|
|||||||
setmetatable(self, Plugin)
|
setmetatable(self, Plugin)
|
||||||
|
|
||||||
do
|
do
|
||||||
|
local uiName = ("Rojo %s UI"):format(Version.display(Config.version))
|
||||||
|
|
||||||
|
if Config.dev then
|
||||||
|
uiName = "Rojo Dev UI"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If there's an existing Rojo UI, like from a Roblox plugin upgrade
|
||||||
|
-- that wasn't Rojo, make sure we clean it up.
|
||||||
|
local existingUi = CoreGui:FindFirstChild(uiName)
|
||||||
|
|
||||||
|
if existingUi ~= nil then
|
||||||
|
existingUi:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
local screenGui = Instance.new("ScreenGui")
|
local screenGui = Instance.new("ScreenGui")
|
||||||
screenGui.Name = "Rojo UI"
|
screenGui.Name = uiName
|
||||||
screenGui.Parent = game.CoreGui
|
screenGui.Parent = CoreGui
|
||||||
screenGui.DisplayOrder = -1
|
screenGui.DisplayOrder = -1
|
||||||
screenGui.Enabled = false
|
screenGui.Enabled = false
|
||||||
|
|
||||||
@@ -54,6 +71,18 @@ function Plugin.new()
|
|||||||
label.Parent = screenGui
|
label.Parent = screenGui
|
||||||
|
|
||||||
self._label = screenGui
|
self._label = screenGui
|
||||||
|
|
||||||
|
-- If our UI was destroyed, we assume it was from another instance of
|
||||||
|
-- the Rojo plugin coming online.
|
||||||
|
--
|
||||||
|
-- Roblox doesn't notify plugins when they get unloaded, so this is the
|
||||||
|
-- best trigger we have right now unless we create a dedicated event
|
||||||
|
-- object.
|
||||||
|
screenGui.AncestryChanged:Connect(function(_, parent)
|
||||||
|
if parent == nil then
|
||||||
|
self:restart()
|
||||||
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -66,7 +95,11 @@ end
|
|||||||
function Plugin:restart()
|
function Plugin:restart()
|
||||||
warn("Rojo: The server has changed since the last request, reloading plugin...")
|
warn("Rojo: The server has changed since the last request, reloading plugin...")
|
||||||
|
|
||||||
self._reconciler:clear()
|
self:stopPolling()
|
||||||
|
|
||||||
|
self._reconciler:destruct()
|
||||||
|
self._reconciler = Reconciler.new()
|
||||||
|
|
||||||
self._api = nil
|
self._api = nil
|
||||||
self._polling = false
|
self._polling = false
|
||||||
self._syncInProgress = false
|
self._syncInProgress = false
|
||||||
@@ -122,7 +155,7 @@ function Plugin:stopPolling()
|
|||||||
return Promise.resolve(false)
|
return Promise.resolve(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Rojo Stopped polling server for changes.")
|
print("Rojo: Stopped polling server for changes.")
|
||||||
|
|
||||||
self._polling = false
|
self._polling = false
|
||||||
self._label.Enabled = false
|
self._label.Enabled = false
|
||||||
|
|||||||
@@ -1,15 +1,32 @@
|
|||||||
local RouteMap = require(script.Parent.RouteMap)
|
local RouteMap = require(script.Parent.RouteMap)
|
||||||
|
|
||||||
local function classEqual(rbx, className)
|
local function classEqual(a, b)
|
||||||
if className == "*" then
|
if a == "*" or b == "*" then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
return rbx.ClassName == className
|
return a == b
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function applyProperties(target, properties)
|
||||||
|
for key, property in pairs(properties) do
|
||||||
|
-- TODO: Transform property value based on property.Type
|
||||||
|
-- Right now, we assume that 'value' is primitive!
|
||||||
|
target[key] = property.Value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Attempt to parent `rbx` to `parent`, doing nothing if:
|
||||||
|
* parent is already `parent`
|
||||||
|
* Changing parent threw an error
|
||||||
|
]]
|
||||||
local function reparent(rbx, parent)
|
local function reparent(rbx, parent)
|
||||||
if rbx then
|
if rbx then
|
||||||
|
if rbx.Parent == parent then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- It's possible that 'rbx' is a service or some other object that we
|
-- It's possible that 'rbx' is a service or some other object that we
|
||||||
-- can't change the parent of. That's the only reason why Parent would
|
-- can't change the parent of. That's the only reason why Parent would
|
||||||
-- fail except for rbx being previously destroyed!
|
-- fail except for rbx being previously destroyed!
|
||||||
@@ -38,7 +55,7 @@ local function findNextChildPair(primaryChildren, secondaryChildren, visited)
|
|||||||
visited[primaryChild] = true
|
visited[primaryChild] = true
|
||||||
|
|
||||||
for _, secondaryChild in ipairs(secondaryChildren) do
|
for _, secondaryChild in ipairs(secondaryChildren) do
|
||||||
if primaryChild.ClassName == secondaryChild.ClassName and primaryChild.Name == secondaryChild.Name then
|
if classEqual(primaryChild.ClassName, secondaryChild.ClassName) and primaryChild.Name == secondaryChild.Name then
|
||||||
visited[secondaryChild] = true
|
visited[secondaryChild] = true
|
||||||
|
|
||||||
return primaryChild, secondaryChild
|
return primaryChild, secondaryChild
|
||||||
@@ -110,12 +127,11 @@ function Reconciler:_reify(item)
|
|||||||
local rbx = Instance.new(className)
|
local rbx = Instance.new(className)
|
||||||
rbx.Name = item.Name
|
rbx.Name = item.Name
|
||||||
|
|
||||||
for key, property in pairs(item.Properties) do
|
applyProperties(rbx, item.Properties)
|
||||||
-- TODO: Check for compound types, like Vector3!
|
|
||||||
rbx[key] = property.Value
|
|
||||||
end
|
|
||||||
|
|
||||||
self:_reconcileChildren(rbx, item)
|
for _, child in ipairs(item.Children) do
|
||||||
|
reparent(self:_reify(child), rbx)
|
||||||
|
end
|
||||||
|
|
||||||
if item.Route then
|
if item.Route then
|
||||||
self._routeMap:insert(item.Route, rbx)
|
self._routeMap:insert(item.Route, rbx)
|
||||||
@@ -125,10 +141,10 @@ function Reconciler:_reify(item)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Clears any state that the Reconciler has, effectively restarting it.
|
Clears any state that the Reconciler has, stopping it completely.
|
||||||
]]
|
]]
|
||||||
function Reconciler:clear()
|
function Reconciler:destruct()
|
||||||
self._routeMap:clear()
|
self._routeMap:destruct()
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -139,6 +155,7 @@ function Reconciler:reconcile(rbx, item)
|
|||||||
-- Item was deleted
|
-- Item was deleted
|
||||||
if not item then
|
if not item then
|
||||||
if rbx then
|
if rbx then
|
||||||
|
self._routeMap:removeByRbx(rbx)
|
||||||
rbx:Destroy()
|
rbx:Destroy()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -151,27 +168,16 @@ function Reconciler:reconcile(rbx, item)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Item changed type!
|
-- Item changed type!
|
||||||
if not classEqual(rbx, item.ClassName) then
|
if not classEqual(rbx.ClassName, item.ClassName) then
|
||||||
|
self._routeMap:removeByRbx(rbx)
|
||||||
rbx:Destroy()
|
rbx:Destroy()
|
||||||
|
|
||||||
rbx = self:_reify(item)
|
return self:_reify(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply all properties, Roblox will de-duplicate changes
|
applyProperties(rbx, item.Properties)
|
||||||
for key, property in pairs(item.Properties) do
|
|
||||||
-- TODO: Transform property value based on property.Type
|
|
||||||
-- Right now, we assume that 'value' is primitive!
|
|
||||||
|
|
||||||
rbx[key] = property.Value
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Use a dumb algorithm for reconciling children
|
|
||||||
self:_reconcileChildren(rbx, item)
|
self:_reconcileChildren(rbx, item)
|
||||||
|
|
||||||
if item.Route then
|
|
||||||
self._routeMap:insert(item.Route, rbx)
|
|
||||||
end
|
|
||||||
|
|
||||||
return rbx
|
return rbx
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ end
|
|||||||
function RouteMap:insert(route, rbx)
|
function RouteMap:insert(route, rbx)
|
||||||
local hashed = hashRoute(route)
|
local hashed = hashRoute(route)
|
||||||
|
|
||||||
|
-- Make sure that each route and instance are only present in RouteMap once.
|
||||||
|
self:removeByRoute(route)
|
||||||
|
self:removeByRbx(rbx)
|
||||||
|
|
||||||
self._map[hashed] = rbx
|
self._map[hashed] = rbx
|
||||||
self._reverseMap[rbx] = hashed
|
self._reverseMap[rbx] = hashed
|
||||||
self._connectionsByRbx[rbx] = rbx.AncestryChanged:Connect(function(_, parent)
|
self._connectionsByRbx[rbx] = rbx.AncestryChanged:Connect(function(_, parent)
|
||||||
@@ -42,24 +46,36 @@ function RouteMap:removeByRoute(route)
|
|||||||
local hashedRoute = hashRoute(route)
|
local hashedRoute = hashRoute(route)
|
||||||
local rbx = self._map[hashedRoute]
|
local rbx = self._map[hashedRoute]
|
||||||
|
|
||||||
if rbx then
|
if rbx ~= nil then
|
||||||
self._map[hashedRoute] = nil
|
self:_removeInternal(rbx, hashedRoute)
|
||||||
self._reverseMap[rbx] = nil
|
|
||||||
self._connectionsByRbx[rbx] = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RouteMap:removeByRbx(rbx)
|
function RouteMap:removeByRbx(rbx)
|
||||||
local hashedRoute = self._reverseMap[rbx]
|
local hashedRoute = self._reverseMap[rbx]
|
||||||
|
|
||||||
if hashedRoute then
|
if hashedRoute ~= nil then
|
||||||
self._map[hashedRoute] = nil
|
self:_removeInternal(rbx, hashedRoute)
|
||||||
self._reverseMap[rbx] = nil
|
|
||||||
self._connectionsByRbx[rbx] = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RouteMap:removeRbxDescendants(parentRbx)
|
--[[
|
||||||
|
Correcly removes the given Roblox Instance/Route pair from the RouteMap.
|
||||||
|
]]
|
||||||
|
function RouteMap:_removeInternal(rbx, hashedRoute)
|
||||||
|
self._map[hashedRoute] = nil
|
||||||
|
self._reverseMap[rbx] = nil
|
||||||
|
self._connectionsByRbx[rbx]:Disconnect()
|
||||||
|
self._connectionsByRbx[rbx] = nil
|
||||||
|
|
||||||
|
self:removeRbxDescendants(rbx)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Ensure that there are no descendants of the given Roblox Instance still
|
||||||
|
present in the map, guaranteeing that it has been cleaned out.
|
||||||
|
]]
|
||||||
|
function RouteMap:_removeRbxDescendants(parentRbx)
|
||||||
for rbx in pairs(self._reverseMap) do
|
for rbx in pairs(self._reverseMap) do
|
||||||
if rbx:IsDescendantOf(parentRbx) then
|
if rbx:IsDescendantOf(parentRbx) then
|
||||||
self:removeByRbx(rbx)
|
self:removeByRbx(rbx)
|
||||||
@@ -67,7 +83,11 @@ function RouteMap:removeRbxDescendants(parentRbx)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RouteMap:clear()
|
--[[
|
||||||
|
Remove all items from the map and disconnect all connections, cleaning up
|
||||||
|
the RouteMap.
|
||||||
|
]]
|
||||||
|
function RouteMap:destruct()
|
||||||
self._map = {}
|
self._map = {}
|
||||||
self._reverseMap = {}
|
self._reverseMap = {}
|
||||||
|
|
||||||
|
|||||||
2
server/Cargo.lock
generated
2
server/Cargo.lock
generated
@@ -636,7 +636,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "0.4.5"
|
version = "0.4.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "0.4.5"
|
version = "0.4.8"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
description = "A tool to create robust Roblox projects"
|
description = "A tool to create robust Roblox projects"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ pub fn serve(project_path: &PathBuf, verbose: bool, port: Option<u64>) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if project.partitions.len() == 0 {
|
||||||
|
println!("");
|
||||||
|
println!("This project has no partitions and will not do anything when served!");
|
||||||
|
println!("This is usually a mistake -- edit rojo.json!");
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref PLUGIN_CHAIN: PluginChain = PluginChain::new(vec![
|
static ref PLUGIN_CHAIN: PluginChain = PluginChain::new(vec![
|
||||||
Box::new(ScriptPlugin::new()),
|
Box::new(ScriptPlugin::new()),
|
||||||
|
|||||||
1
test-project/src/a/b.lua
Normal file
1
test-project/src/a/b.lua
Normal file
@@ -0,0 +1 @@
|
|||||||
|
print("HEY!")
|
||||||
Reference in New Issue
Block a user