Add Int64 conversion functions to EncodedValue.lua and enhance script middleware selection in project.rs

- Implemented int64FromPod and int64ToPod functions for converting between Lua numbers and msgpack Int64 representation, addressing precision concerns for large integers.
- Updated the middleware_for_script function to determine the correct script directory based on the RunContext property, improving middleware selection for Script instances.
- Adjusted syncback logic to utilize the new middleware selection for Scripts, ensuring accurate handling of different script types during project synchronization.
This commit is contained in:
Joe 2026-02-01 20:51:13 -06:00
parent 04cf9ac91d
commit 76e4bc4f25
2 changed files with 78 additions and 7 deletions

View file

@ -4,6 +4,36 @@ local function identity(...)
return ...
end
-- Convert msgpack Int64 representation to a Lua number
-- Note: Lua numbers are doubles, so precision may be lost for values > 2^53
local function int64FromPod(pod)
-- If it's already a number, return it
if type(pod) == "number" then
return pod
end
-- If it's a msgpack Int64 table, convert to number
if type(pod) == "table" and pod.mostSignificantPart ~= nil and pod.leastSignificantPart ~= nil then
local most = pod.mostSignificantPart
local least = pod.leastSignificantPart
local value = most * 0x100000000 + least
-- Handle signed integers (negative values have MSB set)
if most >= 0x80000000 then
value = value - 0x10000000000000000
end
return value
end
return pod
end
-- Convert a Lua number to msgpack Int64 if needed, otherwise return as-is
-- For values that fit in Lua's safe integer range, returning plain number works
-- because msgpack will encode it appropriately
local function int64ToPod(roblox)
-- Just return the number - msgpack will handle encoding
-- For values outside safe range, this might lose precision
return roblox
end
local function unpackDecoder(f)
return function(value)
return f(unpack(value))
@ -302,8 +332,8 @@ types = {
},
Int64 = {
fromPod = identity,
toPod = identity,
fromPod = int64FromPod,
toPod = int64ToPod,
},
MaterialColors = {

View file

@ -55,6 +55,44 @@ fn can_transition_class(old_class: &str, new_class: &str) -> bool {
false
}
/// Determines the correct script-dir middleware for a Script based on its RunContext property.
///
/// Script class has a RunContext property that determines the correct middleware:
/// - Server → ServerScriptDir (init.server.luau)
/// - Client → ClientScriptDir (init.client.luau)
/// - Plugin → PluginScriptDir (init.plugin.luau)
/// - Legacy → LegacyScriptDir (init.legacy.luau)
///
/// If RunContext is not set or unrecognized, defaults to LegacyScriptDir.
fn middleware_for_script(inst: &Instance) -> Middleware {
let run_context_enums = rbx_reflection_database::get()
.ok()
.and_then(|db| db.enums.get("RunContext"))
.map(|e| &e.items);
let run_context_value = inst.properties.get(&ustr("RunContext")).and_then(|v| match v {
Variant::Enum(e) => Some(e.to_u32()),
_ => None,
});
if let (Some(enums), Some(value)) = (run_context_enums, run_context_value) {
for (name, &enum_value) in enums {
if enum_value == value {
return match name.as_ref() {
"Server" => Middleware::ServerScriptDir,
"Client" => Middleware::ClientScriptDir,
"Plugin" => Middleware::PluginScriptDir,
"Legacy" => Middleware::LegacyScriptDir,
_ => Middleware::LegacyScriptDir,
};
}
}
}
// Default to LegacyScriptDir if no RunContext or unrecognized
Middleware::LegacyScriptDir
}
pub fn snapshot_project(
context: &InstanceContext,
vfs: &Vfs,
@ -424,10 +462,10 @@ pub fn syncback_project<'sync>(
// determine the middleware from the new instance and let
// syncback create the necessary files/directories.
if !snapshot.data.is_incremental() {
// Determine middleware based on new instance class
// Determine middleware based on new instance class (and RunContext for Scripts)
let inferred_middleware = match new_inst.class.as_str() {
"ModuleScript" => Middleware::ModuleScriptDir,
"Script" => Middleware::ServerScriptDir,
"Script" => middleware_for_script(new_inst),
"LocalScript" => Middleware::LocalScriptDir,
"Folder" => Middleware::Dir,
// For other classes, default to Dir
@ -459,7 +497,7 @@ pub fn syncback_project<'sync>(
if middleware == Middleware::Dir {
let script_middleware = match new_inst.class.as_str() {
"ModuleScript" => Some(Middleware::ModuleScriptDir),
"Script" => Some(Middleware::ServerScriptDir),
"Script" => Some(middleware_for_script(new_inst)),
"LocalScript" => Some(Middleware::LocalScriptDir),
_ => None,
};
@ -482,6 +520,8 @@ pub fn syncback_project<'sync>(
| Middleware::ServerScriptDir
| Middleware::ClientScriptDir
| Middleware::LocalScriptDir
| Middleware::PluginScriptDir
| Middleware::LegacyScriptDir
);
if is_script_dir_middleware && new_inst.class == "Folder" {
log::debug!(
@ -493,13 +533,14 @@ pub fn syncback_project<'sync>(
}
// Case 3: Filesystem has a script-dir but new instance is a DIFFERENT script type.
// e.g., ModuleScriptDir (init.luau) but new instance is Script → use ServerScriptDir.
// e.g., ModuleScriptDir (init.luau) but new instance is Script → use correct script-dir.
// Without this, changing a ModuleScript to Script in Studio would silently keep
// the old init.luau file, causing the script to be read as ModuleScript on next build.
// For Scripts, we also check RunContext to pick the correct middleware (Server/Client/Plugin/Legacy).
if is_script_dir_middleware {
let correct_middleware = match new_inst.class.as_str() {
"ModuleScript" => Some(Middleware::ModuleScriptDir),
"Script" => Some(Middleware::ServerScriptDir),
"Script" => Some(middleware_for_script(new_inst)),
"LocalScript" => Some(Middleware::LocalScriptDir),
_ => None,
};