forked from rojo-rbx/rojo
Compare commits
26 Commits
v7.4.0-rc1
...
v7.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0a96e3811 | ||
|
|
9d0d76f0a5 | ||
|
|
c7173ac832 | ||
|
|
b12ce47e7e | ||
|
|
269272983b | ||
|
|
6adc5eb9fb | ||
|
|
fd8bc8ae3f | ||
|
|
3369b0d429 | ||
|
|
097d39e8ce | ||
|
|
11fa08e6d6 | ||
|
|
96987af71d | ||
|
|
23327cb3ef | ||
|
|
b43b45be8f | ||
|
|
41994ec82e | ||
|
|
cd14ea7c62 | ||
|
|
9f13bca6b8 | ||
|
|
f4252c3e97 | ||
|
|
6598867d3d | ||
|
|
f39e040a0d | ||
|
|
a3d140269b | ||
|
|
feac29ea40 | ||
|
|
834c8cdbca | ||
|
|
d441fbdf91 | ||
|
|
e897f524dc | ||
|
|
1caf9446d8 | ||
|
|
bfd2c885db |
@@ -23,4 +23,7 @@ insert_final_newline = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.lua]
|
||||
indent_style = tab
|
||||
|
||||
[*.luau]
|
||||
indent_style = tab
|
||||
39
.github/workflows/ci.yml
vendored
39
.github/workflows/ci.yml
vendored
@@ -12,11 +12,11 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
rust_version: [stable, 1.70.0]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -26,10 +26,13 @@ jobs:
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust_version }}
|
||||
toolchain: stable
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.3.0
|
||||
with:
|
||||
@@ -41,6 +44,33 @@ jobs:
|
||||
- name: Test
|
||||
run: cargo test --locked --verbose
|
||||
|
||||
msrv:
|
||||
name: Check MSRV
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.70.0
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.3.0
|
||||
with:
|
||||
version: 'v0.2.7'
|
||||
|
||||
- name: Build
|
||||
run: cargo build --locked --verbose
|
||||
|
||||
lint:
|
||||
name: Rustfmt, Clippy, & Stylua
|
||||
runs-on: ubuntu-latest
|
||||
@@ -57,6 +87,9 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Setup Aftman
|
||||
uses: ok-nick/setup-aftman@v0.3.0
|
||||
with:
|
||||
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -2,6 +2,37 @@
|
||||
|
||||
## Unreleased Changes
|
||||
|
||||
## [7.4.0] - January 16, 2024
|
||||
* Improved the visualization for array properties like Tags ([#829])
|
||||
* Significantly improved performance of `rojo serve`, `rojo build --watch`, and `rojo sourcemap --watch` on macOS. ([#830])
|
||||
* Changed *.lua files that init command generates to *.luau ([#831])
|
||||
* Does not remind users to sync if the sync lock is claimed already ([#833])
|
||||
|
||||
[#829]: https://github.com/rojo-rbx/rojo/pull/829
|
||||
[#830]: https://github.com/rojo-rbx/rojo/pull/830
|
||||
[#831]: https://github.com/rojo-rbx/rojo/pull/831
|
||||
[#833]: https://github.com/rojo-rbx/rojo/pull/833
|
||||
|
||||
## [7.4.0-rc3] - October 25, 2023
|
||||
* Changed `sourcemap --watch` to only generate the sourcemap when it's necessary ([#800])
|
||||
* Switched script source property getter and setter to `ScriptEditorService` methods ([#801])
|
||||
|
||||
This ensures that the script editor reflects any changes Rojo makes to a script while it is open in the script editor.
|
||||
|
||||
* Fixed issues when handling `SecurityCapabilities` values ([#803], [#807])
|
||||
* Fixed Rojo plugin erroring out when attempting to sync attributes with invalid names ([#809])
|
||||
|
||||
[#800]: https://github.com/rojo-rbx/rojo/pull/800
|
||||
[#801]: https://github.com/rojo-rbx/rojo/pull/801
|
||||
[#803]: https://github.com/rojo-rbx/rojo/pull/803
|
||||
[#807]: https://github.com/rojo-rbx/rojo/pull/807
|
||||
[#809]: https://github.com/rojo-rbx/rojo/pull/809
|
||||
|
||||
## [7.4.0-rc2] - October 3, 2023
|
||||
* Fixed bug with parsing version for plugin validation ([#797])
|
||||
|
||||
[#797]: https://github.com/rojo-rbx/rojo/pull/797
|
||||
|
||||
## [7.4.0-rc1] - October 3, 2023
|
||||
### Additions
|
||||
#### Project format
|
||||
@@ -118,7 +149,6 @@
|
||||
* Add buttons for navigation on the Connected page ([#722])
|
||||
|
||||
### Fixes
|
||||
* Significantly improved performance of `rojo serve` and `rojo build` on macOS. [#783]
|
||||
* Significantly improved performance of `rojo sourcemap` ([#668])
|
||||
* Fixed the diff visualizer of connected sessions. ([#674])
|
||||
* Fixed disconnected session activity. ([#675])
|
||||
@@ -152,7 +182,6 @@
|
||||
[#770]: https://github.com/rojo-rbx/rojo/pull/770
|
||||
[#771]: https://github.com/rojo-rbx/rojo/pull/771
|
||||
[#774]: https://github.com/rojo-rbx/rojo/pull/774
|
||||
[#783]: https://github.com/rojo-rbx/rojo/pull/783
|
||||
[rbx-dom#299]: https://github.com/rojo-rbx/rbx-dom/pull/299
|
||||
[rbx-dom#296]: https://github.com/rojo-rbx/rbx-dom/pull/296
|
||||
|
||||
|
||||
31
Cargo.lock
generated
31
Cargo.lock
generated
@@ -1586,9 +1586,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_binary"
|
||||
version = "0.7.2"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10942950a57c939e540a2f977ba55e9140007d7e96c532d455502c290fdf710d"
|
||||
checksum = "6314dd6bf5c21d0598cdb53cf5d241aa643ba41da8b8abf7402b4a35096f03f6"
|
||||
dependencies = [
|
||||
"log",
|
||||
"lz4",
|
||||
@@ -1601,9 +1601,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_dom_weak"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843a2e0e1446623625943f7228d9d4b5cf3883017e3964733600682506864b34"
|
||||
checksum = "9b67b56bac99849c2e3c57547b036927f71c57cf7f4d900d04e3e4ee774ec316"
|
||||
dependencies = [
|
||||
"rbx_types",
|
||||
"serde",
|
||||
@@ -1611,9 +1611,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_reflection"
|
||||
version = "4.4.0"
|
||||
version = "4.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41e762dfca3217d2d37da631de2fa0d1616edaa61a0a2633263d5d3305baf8c3"
|
||||
checksum = "0d41509c991b53a7276a746a795eae2b9204f398164920f61976995b47fe1722"
|
||||
dependencies = [
|
||||
"rbx_types",
|
||||
"serde",
|
||||
@@ -1622,9 +1622,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_reflection_database"
|
||||
version = "0.2.9+roblox-596"
|
||||
version = "0.2.10+roblox-607"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b18f088a2b4aa66324ec97b5b6ffacb53188aef19f3497d95d6a1d1dbb28e66"
|
||||
checksum = "12e20c06fa41f7aadc79005c8354f592b2c2f4d0c61e1080ed5718dafc30aea0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"rbx_reflection",
|
||||
@@ -1634,9 +1634,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_types"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a991523e3ad5f43a4d121cb4a1e5bc23f7826bb4a1db5aa51e94f1073150ec"
|
||||
checksum = "7ca23bfd469d067d81ef14f65fe09aeddc25abcf576a889d1a7664fe021cf18c"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bitflags 1.3.2",
|
||||
@@ -1649,9 +1649,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_xml"
|
||||
version = "0.13.2"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc87343301303ff0510903fb7eb3dbd1c75bdb6ab780fea6091bdc3f58b5829f"
|
||||
checksum = "f8c03f95500961c32340791d1fabd4587f6873bdbff077ecca6ae32db7960dea"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"log",
|
||||
@@ -1831,7 +1831,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rojo"
|
||||
version = "7.4.0-rc1"
|
||||
version = "7.4.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"backtrace",
|
||||
@@ -1852,7 +1852,6 @@ dependencies = [
|
||||
"log",
|
||||
"maplit",
|
||||
"memofs",
|
||||
"notify",
|
||||
"num_cpus",
|
||||
"opener",
|
||||
"paste",
|
||||
@@ -1908,9 +1907,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.15"
|
||||
version = "0.38.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531"
|
||||
checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rojo"
|
||||
version = "7.4.0-rc1"
|
||||
version = "7.4.0"
|
||||
rust-version = "1.70.0"
|
||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||
description = "Enables professional-grade development tools for Roblox developers"
|
||||
@@ -49,11 +49,11 @@ memofs = { version = "0.2.0", path = "crates/memofs" }
|
||||
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||
|
||||
rbx_binary = "0.7.2"
|
||||
rbx_dom_weak = "2.6.0"
|
||||
rbx_reflection = "4.4.0"
|
||||
rbx_reflection_database = "0.2.8"
|
||||
rbx_xml = "0.13.2"
|
||||
rbx_binary = "0.7.4"
|
||||
rbx_dom_weak = "2.7.0"
|
||||
rbx_reflection = "4.5.0"
|
||||
rbx_reflection_database = "0.2.10"
|
||||
rbx_xml = "0.13.3"
|
||||
|
||||
anyhow = "1.0.44"
|
||||
backtrace = "0.3.61"
|
||||
@@ -69,7 +69,6 @@ hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
|
||||
jod-thread = "0.1.2"
|
||||
log = "0.4.14"
|
||||
maplit = "1.0.2"
|
||||
notify = "4.0.17"
|
||||
num_cpus = "1.15.0"
|
||||
opener = "0.5.0"
|
||||
rayon = "1.7.0"
|
||||
|
||||
10
build.rs
10
build.rs
@@ -46,14 +46,10 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
||||
let plugin_version =
|
||||
Version::parse(fs::read_to_string(&plugin_root.join("Version.txt"))?.trim())?;
|
||||
Version::parse(fs::read_to_string(plugin_root.join("Version.txt"))?.trim())?;
|
||||
|
||||
assert!(
|
||||
our_version.major == plugin_version.major,
|
||||
"plugin version does not match Cargo version"
|
||||
);
|
||||
assert!(
|
||||
our_version.minor == plugin_version.minor,
|
||||
assert_eq!(
|
||||
our_version, plugin_version,
|
||||
"plugin version does not match Cargo version"
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# memofs Changelog
|
||||
|
||||
## Unreleased Changes
|
||||
* Changed the `StdBackend` file watcher to use `PollWatcher` on macOS.
|
||||
* Changed `StdBackend` file watching component to use minimal recursive watches. [#830]
|
||||
|
||||
[#830]: https://github.com/rojo-rbx/rojo/pull/830
|
||||
|
||||
## 0.2.0 (2021-08-23)
|
||||
* Updated to `crossbeam-channel` 0.5.1.
|
||||
|
||||
@@ -1,38 +1,24 @@
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::{collections::HashSet, io};
|
||||
|
||||
use crossbeam_channel::Receiver;
|
||||
use notify::{DebouncedEvent, RecursiveMode, Watcher};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use notify::PollWatcher;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use notify::{watcher, RecommendedWatcher};
|
||||
use notify::{watcher, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
|
||||
use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent};
|
||||
|
||||
/// `VfsBackend` that uses `std::fs` and the `notify` crate.
|
||||
pub struct StdBackend {
|
||||
// We use PollWatcher on macos because using the KQueue watcher
|
||||
// can cause some gnarly performance problems.
|
||||
#[cfg(target_os = "macos")]
|
||||
watcher: PollWatcher,
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
watcher: RecommendedWatcher,
|
||||
|
||||
watcher_receiver: Receiver<VfsEvent>,
|
||||
watches: HashSet<PathBuf>,
|
||||
}
|
||||
|
||||
impl StdBackend {
|
||||
pub fn new() -> StdBackend {
|
||||
let (notify_tx, notify_rx) = mpsc::channel();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let watcher = PollWatcher::new(notify_tx, Duration::from_millis(50)).unwrap();
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let watcher = watcher(notify_tx, Duration::from_millis(50)).unwrap();
|
||||
|
||||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
@@ -63,6 +49,7 @@ impl StdBackend {
|
||||
Self {
|
||||
watcher,
|
||||
watcher_receiver: rx,
|
||||
watches: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,12 +99,22 @@ impl VfsBackend for StdBackend {
|
||||
}
|
||||
|
||||
fn watch(&mut self, path: &Path) -> io::Result<()> {
|
||||
self.watcher
|
||||
.watch(path, RecursiveMode::NonRecursive)
|
||||
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
|
||||
if self.watches.contains(path)
|
||||
|| path
|
||||
.ancestors()
|
||||
.any(|ancestor| self.watches.contains(ancestor))
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
self.watches.insert(path.to_path_buf());
|
||||
self.watcher
|
||||
.watch(path, RecursiveMode::Recursive)
|
||||
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
|
||||
}
|
||||
}
|
||||
|
||||
fn unwatch(&mut self, path: &Path) -> io::Result<()> {
|
||||
self.watches.remove(path);
|
||||
self.watcher
|
||||
.unwatch(path)
|
||||
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.4.0-rc1
|
||||
7.4.0
|
||||
@@ -412,15 +412,6 @@ types = {
|
||||
end,
|
||||
},
|
||||
|
||||
SecurityCapabilities = {
|
||||
fromPod = function(_pod)
|
||||
error("SecurityCapabilities is not implemented")
|
||||
end,
|
||||
toPod = function(_roblox)
|
||||
error("SecurityCapabilities is not implemented")
|
||||
end,
|
||||
},
|
||||
|
||||
SharedString = {
|
||||
fromPod = function(_pod)
|
||||
error("SharedString is not supported")
|
||||
|
||||
@@ -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 = {
|
||||
@@ -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
@@ -53,8 +53,23 @@ local function DisplayValue(props)
|
||||
elseif next(props.value) == nil then
|
||||
-- If it's empty, show empty braces
|
||||
textRepresentation = "{}"
|
||||
elseif next(props.value) == 1 then
|
||||
-- We don't need to support mixed tables, so checking the first key is enough
|
||||
-- to determine if it's a simple array
|
||||
local out, i = table.create(#props.value), 0
|
||||
for k, v in props.value do
|
||||
i += 1
|
||||
|
||||
-- Wrap strings in quotes
|
||||
if type(v) == "string" then
|
||||
v = '"' .. v .. '"'
|
||||
end
|
||||
|
||||
out[i] = tostring(v)
|
||||
end
|
||||
textRepresentation = "{ " .. table.concat(out, ", ") .. " }"
|
||||
else
|
||||
-- If it has children, list them out
|
||||
-- Otherwise, show the table contents as a dictionary
|
||||
local out, i = {}, 0
|
||||
for k, v in pairs(props.value) do
|
||||
i += 1
|
||||
|
||||
@@ -136,6 +136,7 @@ function App:init()
|
||||
and self.serveSession == nil
|
||||
and Settings:get("syncReminder")
|
||||
and self:getLastSyncTimestamp()
|
||||
and (self:isSyncLockAvailable())
|
||||
then
|
||||
self:addNotification("You've previously synced this place. Would you like to reconnect?", 300, {
|
||||
Connect = {
|
||||
@@ -283,12 +284,39 @@ function App:getHostAndPort()
|
||||
return host, port
|
||||
end
|
||||
|
||||
function App:isSyncLockAvailable()
|
||||
if #Players:GetPlayers() == 0 then
|
||||
-- Team Create is not active, so no one can be holding the lock
|
||||
return true
|
||||
end
|
||||
|
||||
local lock = ServerStorage:FindFirstChild("__Rojo_SessionLock")
|
||||
if not lock then
|
||||
-- No lock is made yet, so it is available
|
||||
return true
|
||||
end
|
||||
|
||||
if lock.Value and lock.Value ~= Players.LocalPlayer and lock.Value.Parent then
|
||||
-- Someone else is holding the lock
|
||||
return false, lock.Value
|
||||
end
|
||||
|
||||
-- The lock exists, but is not claimed
|
||||
return true
|
||||
end
|
||||
|
||||
function App:claimSyncLock()
|
||||
if #Players:GetPlayers() == 0 then
|
||||
Log.trace("Skipping sync lock because this isn't in Team Create")
|
||||
return true
|
||||
end
|
||||
|
||||
local isAvailable, priorOwner = self:isSyncLockAvailable()
|
||||
if not isAvailable then
|
||||
Log.trace("Skipping sync lock because it is already claimed")
|
||||
return false, priorOwner
|
||||
end
|
||||
|
||||
local lock = ServerStorage:FindFirstChild("__Rojo_SessionLock")
|
||||
if not lock then
|
||||
lock = Instance.new("ObjectValue")
|
||||
@@ -300,11 +328,6 @@ function App:claimSyncLock()
|
||||
return true
|
||||
end
|
||||
|
||||
if lock.Value and lock.Value ~= Players.LocalPlayer and lock.Value.Parent then
|
||||
Log.trace("Found existing sync lock owned by {}", lock.Value)
|
||||
return false, lock.Value
|
||||
end
|
||||
|
||||
lock.Value = Players.LocalPlayer
|
||||
Log.trace("Claimed existing sync lock")
|
||||
return true
|
||||
|
||||
@@ -3,8 +3,9 @@ local strict = require(script.Parent.strict)
|
||||
local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
|
||||
|
||||
local Version = script.Parent.Parent.Version
|
||||
local realVersion = Version.Value:split(".")
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ function gatherAssetUrlsRecursive(currentTable, currentUrls)
|
||||
if typeof(value) == "string" then
|
||||
table.insert(currentUrls, value)
|
||||
elseif typeof(value) == "table" then
|
||||
gatherAssetUrlsRecursive(value)
|
||||
gatherAssetUrlsRecursive(value, currentUrls)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ expression: contents
|
||||
<Properties>
|
||||
<string name="Name">server_init</string>
|
||||
<token name="RunContext">0</token>
|
||||
<string name="Source">return "From folder/init.server.lua"</string>
|
||||
<string name="Source">return "From folder/init.server.luau"</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
return "From folder/init.server.lua"
|
||||
@@ -0,0 +1 @@
|
||||
return "From folder/init.server.luau"
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
@@ -126,14 +125,41 @@ impl JobThreadContext {
|
||||
// For a given VFS event, we might have many changes to different parts
|
||||
// of the tree. Calculate and apply all of these changes.
|
||||
let applied_patches = match event {
|
||||
VfsEvent::Write(path) => {
|
||||
if path.is_dir() {
|
||||
return;
|
||||
VfsEvent::Create(path) | VfsEvent::Remove(path) | VfsEvent::Write(path) => {
|
||||
let mut tree = self.tree.lock().unwrap();
|
||||
let mut applied_patches = Vec::new();
|
||||
|
||||
// Find the nearest ancestor to this path that has
|
||||
// associated instances in the tree. This helps make sure
|
||||
// that we handle additions correctly, especially if we
|
||||
// receive events for descendants of a large tree being
|
||||
// created all at once.
|
||||
let mut current_path = path.as_path();
|
||||
let affected_ids = loop {
|
||||
let ids = tree.get_ids_at_path(¤t_path);
|
||||
|
||||
log::trace!("Path {} affects IDs {:?}", current_path.display(), ids);
|
||||
|
||||
if !ids.is_empty() {
|
||||
break ids.to_vec();
|
||||
}
|
||||
|
||||
log::trace!("Trying parent path...");
|
||||
match current_path.parent() {
|
||||
Some(parent) => current_path = parent,
|
||||
None => break Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
for id in affected_ids {
|
||||
if let Some(patch) = compute_and_apply_changes(&mut tree, &self.vfs, id) {
|
||||
if !patch.is_empty() {
|
||||
applied_patches.push(patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
on_vfs_event(path, &self.tree, &self.vfs)
|
||||
}
|
||||
VfsEvent::Create(path) | VfsEvent::Remove(path) => {
|
||||
on_vfs_event(path, &self.tree, &self.vfs)
|
||||
|
||||
applied_patches
|
||||
}
|
||||
_ => {
|
||||
log::warn!("Unhandled VFS event: {:?}", event);
|
||||
@@ -236,45 +262,6 @@ impl JobThreadContext {
|
||||
}
|
||||
}
|
||||
|
||||
// Find the nearest ancestor to this path that has
|
||||
// associated instances in the tree. This helps make sure
|
||||
// that we handle additions correctly, especially if we
|
||||
// receive events for descendants of a large tree being
|
||||
// created all at once.
|
||||
fn on_vfs_event(
|
||||
path: PathBuf,
|
||||
tree: &Arc<Mutex<RojoTree>>,
|
||||
vfs: &Arc<Vfs>,
|
||||
) -> Vec<AppliedPatchSet> {
|
||||
let mut tree = tree.lock().unwrap();
|
||||
let mut applied_patches = Vec::new();
|
||||
|
||||
let mut current_path = path.as_path();
|
||||
let affected_ids = loop {
|
||||
let ids = tree.get_ids_at_path(current_path);
|
||||
|
||||
log::trace!("Path {} affects IDs {:?}", current_path.display(), ids);
|
||||
|
||||
if !ids.is_empty() {
|
||||
break ids.to_vec();
|
||||
}
|
||||
|
||||
log::trace!("Trying parent path...");
|
||||
match current_path.parent() {
|
||||
Some(parent) => current_path = parent,
|
||||
None => break Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
for id in affected_ids {
|
||||
if let Some(patch) = compute_and_apply_changes(&mut tree, vfs, id) {
|
||||
if !patch.is_empty() {
|
||||
applied_patches.push(patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
applied_patches
|
||||
}
|
||||
fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: Ref) -> Option<AppliedPatchSet> {
|
||||
let metadata = tree
|
||||
.get_metadata(id)
|
||||
|
||||
@@ -146,7 +146,7 @@ impl OutputKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn xml_encode_config() -> rbx_xml::EncodeOptions {
|
||||
fn xml_encode_config() -> rbx_xml::EncodeOptions<'static> {
|
||||
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use super::resolve_path;
|
||||
static MODEL_PROJECT: &str =
|
||||
include_str!("../../assets/default-model-project/default.project.json");
|
||||
static MODEL_README: &str = include_str!("../../assets/default-model-project/README.md");
|
||||
static MODEL_INIT: &str = include_str!("../../assets/default-model-project/src-init.lua");
|
||||
static MODEL_INIT: &str = include_str!("../../assets/default-model-project/src-init.luau");
|
||||
static MODEL_GIT_IGNORE: &str = include_str!("../../assets/default-model-project/gitignore.txt");
|
||||
|
||||
static PLACE_PROJECT: &str =
|
||||
@@ -116,17 +116,17 @@ fn init_place(base_path: &Path, project_params: ProjectParams) -> anyhow::Result
|
||||
fs::create_dir_all(src.join(&src_client))?;
|
||||
|
||||
write_if_not_exists(
|
||||
&src_shared.join("Hello.lua"),
|
||||
&src_shared.join("Hello.luau"),
|
||||
"return function()\n\tprint(\"Hello, world!\")\nend",
|
||||
)?;
|
||||
|
||||
write_if_not_exists(
|
||||
&src_server.join("init.server.lua"),
|
||||
&src_server.join("init.server.luau"),
|
||||
"print(\"Hello world, from server!\")",
|
||||
)?;
|
||||
|
||||
write_if_not_exists(
|
||||
&src_client.join("init.client.lua"),
|
||||
&src_client.join("init.client.luau"),
|
||||
"print(\"Hello world, from client!\")",
|
||||
)?;
|
||||
|
||||
@@ -149,7 +149,7 @@ fn init_model(base_path: &Path, project_params: ProjectParams) -> anyhow::Result
|
||||
fs::create_dir_all(&src)?;
|
||||
|
||||
let init = project_params.render_template(MODEL_INIT);
|
||||
write_if_not_exists(&src.join("init.lua"), &init)?;
|
||||
write_if_not_exists(&src.join("init.luau"), &init)?;
|
||||
|
||||
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
||||
try_git_init(base_path, &git_ignore)?;
|
||||
@@ -170,7 +170,7 @@ fn init_plugin(base_path: &Path, project_params: ProjectParams) -> anyhow::Resul
|
||||
fs::create_dir_all(&src)?;
|
||||
|
||||
write_if_not_exists(
|
||||
&src.join("init.server.lua"),
|
||||
&src.join("init.server.luau"),
|
||||
"print(\"Hello world, from plugin!\")\n",
|
||||
)?;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use tokio::runtime::Runtime;
|
||||
|
||||
use crate::{
|
||||
serve_session::ServeSession,
|
||||
snapshot::{InstanceWithMeta, RojoTree},
|
||||
snapshot::{AppliedPatchSet, InstanceWithMeta, RojoTree},
|
||||
};
|
||||
|
||||
use super::resolve_path;
|
||||
@@ -90,10 +90,12 @@ impl SourcemapCommand {
|
||||
|
||||
loop {
|
||||
let receiver = session.message_queue().subscribe(cursor);
|
||||
let (new_cursor, _patch_set) = rt.block_on(receiver).unwrap();
|
||||
let (new_cursor, patch_set) = rt.block_on(receiver).unwrap();
|
||||
cursor = new_cursor;
|
||||
|
||||
write_sourcemap(&session, self.output.as_deref(), filter)?;
|
||||
if patch_set_affects_sourcemap(&session, &patch_set, filter) {
|
||||
write_sourcemap(&session, self.output.as_deref(), filter)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +118,43 @@ fn filter_non_scripts(instance: &InstanceWithMeta) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
fn patch_set_affects_sourcemap(
|
||||
session: &ServeSession,
|
||||
patch_set: &[AppliedPatchSet],
|
||||
filter: fn(&InstanceWithMeta) -> bool,
|
||||
) -> bool {
|
||||
let tree = session.tree();
|
||||
|
||||
// A sourcemap has probably changed when:
|
||||
patch_set.par_iter().any(|set| {
|
||||
// 1. An instance was removed, in which case it will no
|
||||
// longer exist in the tree and we cant check the filter
|
||||
!set.removed.is_empty()
|
||||
// 2. A newly added instance passes the filter
|
||||
|| set.added.iter().any(|referent| {
|
||||
let instance = tree
|
||||
.get_instance(*referent)
|
||||
.expect("instance did not exist when updating sourcemap");
|
||||
filter(&instance)
|
||||
})
|
||||
// 3. An existing instance has its class name, name,
|
||||
// or file paths changed, and passes the filter
|
||||
|| set.updated.iter().any(|updated| {
|
||||
let changed = updated.changed_class_name.is_some()
|
||||
|| updated.changed_name.is_some()
|
||||
|| updated.changed_metadata.is_some();
|
||||
if changed {
|
||||
let instance = tree
|
||||
.get_instance(updated.id)
|
||||
.expect("instance did not exist when updating sourcemap");
|
||||
filter(&instance)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn recurse_create_node<'a>(
|
||||
tree: &'a RojoTree,
|
||||
referent: Ref,
|
||||
|
||||
@@ -24,7 +24,7 @@ impl<K: Hash + Eq, V: Eq> MultiMap<K, V> {
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
self.inner.get(k.borrow()).map(Vec::as_slice).unwrap_or(&[])
|
||||
self.inner.get(k).map(Vec::as_slice).unwrap_or(&[])
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, k: K, v: V) {
|
||||
|
||||
@@ -8,9 +8,7 @@ use std::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
glob::Glob, resolution::UnresolvedValue, snapshot_middleware::emit_legacy_scripts_default,
|
||||
};
|
||||
use crate::{glob::Glob, resolution::UnresolvedValue};
|
||||
|
||||
static PROJECT_FILENAME: &str = "default.project.json";
|
||||
|
||||
@@ -75,12 +73,10 @@ pub struct Project {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub serve_address: Option<IpAddr>,
|
||||
|
||||
/// Determines if rojo should emit scripts with the appropriate `RunContext` for `*.client.lua` and `*.server.lua` files in the project.
|
||||
/// Or, if rojo should keep the legacy behavior of emitting LocalScripts and Scripts with legacy Runcontext
|
||||
#[serde(
|
||||
default = "emit_legacy_scripts_default",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
/// Determines if Rojo should emit scripts with the appropriate `RunContext`
|
||||
/// for `*.client.lua` and `*.server.lua` files in the project instead of
|
||||
/// using `Script` and `LocalScript` Instances.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub emit_legacy_scripts: Option<bool>,
|
||||
|
||||
/// A list of globs, relative to the folder the project file is in, that
|
||||
|
||||
@@ -159,7 +159,7 @@ impl ApiService {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
json_ok(&WriteResponse { session_id })
|
||||
json_ok(WriteResponse { session_id })
|
||||
}
|
||||
|
||||
async fn handle_api_read(&self, request: Request<Body>) -> Response<Body> {
|
||||
@@ -271,7 +271,7 @@ impl ApiService {
|
||||
},
|
||||
};
|
||||
|
||||
json_ok(&OpenResponse {
|
||||
json_ok(OpenResponse {
|
||||
session_id: self.serve_session.session_id(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"$path": "src"
|
||||
},
|
||||
"plugins": [
|
||||
"test-plugin.lua"
|
||||
"test-plugin.luau"
|
||||
]
|
||||
}
|
||||
@@ -66,7 +66,11 @@ impl TestServeSession {
|
||||
|
||||
let source_path = Path::new(SERVE_TESTS_PATH).join(name);
|
||||
let dir = tempdir().expect("Couldn't create temporary directory");
|
||||
let project_path = dir.path().join(name);
|
||||
let project_path = dir
|
||||
.path()
|
||||
.canonicalize()
|
||||
.expect("Couldn't canonicalize temporary directory path")
|
||||
.join(name);
|
||||
|
||||
let source_is_file = fs::metadata(&source_path).unwrap().is_file();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user