mirror of
https://github.com/luau/UniversalSynSaveInstance.git
synced 2026-02-04 06:33:10 +02:00
Huge Update
This commit is contained in:
86
README.md
86
README.md
@@ -10,16 +10,16 @@ local synsaveinstance = loadstring(game:HttpGet(Params.RepoURL .. Params.SSI ..
|
||||
|
||||
# Syn Save Instance
|
||||
|
||||
Or shortly SSI, a project aimed at resurrecting saveinstance function from [2019 Synapse X leaked source code](https://github.com/Acrillis/SynapseX).<br />
|
||||
Or shortly SSI, a project aimed at resurrecting saveinstance function from [Synapse X Source 2019].<br />
|
||||
Reason: Many Executors fail miserably at providing good user experience when it comes to tinkering with saving instances
|
||||
|
||||
# TO-DOs:
|
||||
|
||||
- [ ] Add `continue` where needed
|
||||
- [ ] Add Documentation similar to [KRNL](https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D) or [Synapse X](https://docs.synapse.to/reference/misc.html?highlight=saveins#save-instance) / [Synapse X Old](https://synapsexdocs.github.io/custom-lua-functions/misc-functions/#save-instance)
|
||||
- [ ] Add fallback function for appendfile (whether through storing current xml as string or with use of readfile)
|
||||
- [x] Add `continue` where needed
|
||||
- [ ] Add Documentation similar to [KRNL Docs] or [Synapse X Docs] / [Synapse X Docs Old]
|
||||
- [x] ~~Add fallback function for appendfile (whether through storing current xml as string or with use of readfile)~~ Removed Appendfile entirely
|
||||
- [x] Add getproperties as fallback for specialinfo
|
||||
- [ ] Add Redirects to some special (in a bad way 😡) values, more info @ ~~[PropertyPatches](https://github.com/MaximumADHD/Roblox-File-Format/blob/main/Plugins/GenerateApiDump/PropertyPatches.lua#L72)~~ [PropertyPatches](https://github.com/rojo-rbx/rbx-dom/tree/master/patches)+[Bonus](https://github.com/rojo-rbx/rbx-dom/blob/9716af360307eb5da5f97d54d84d694b2bc06acf/rbx_dom_lua/src/customProperties.lua), otherwise they will fallback to default when file is opened
|
||||
- [ ] Add Redirects to some special (in a bad way 😡) values, more info @ ~~[PropertyPatches v1]~~ [PropertyPatches v2]+[PropertyPatches v3], otherwise they will fallback to default when file is opened
|
||||
- Not all though, test each & see if it carries over or not (when file is opened)..
|
||||
- All current redirects: [Here](https://github.com/luau/SynSaveInstance/blob/main/TODO/PropertyPatches)
|
||||
- [ ] Add more Fixes for Errors that **_can_** pop up during opening process
|
||||
@@ -30,70 +30,110 @@ Reason: Many Executors fail miserably at providing good user experience when it
|
||||
- [x] Add --!native tag just in case
|
||||
- [ ] Find default values of binarystring properties (MaximumADHD might have a clue)
|
||||
- LOOK INTO Instance:IsPropertyModified & Instance:ResetPropertyToDefault
|
||||
- [ ] ~~Auto-Detect DataTypes/ValueType Categories of Properties (CFrame, UDim2 so on)~~ Full API Dump Solves this ?
|
||||
- [x] ~~Auto-Detect DataTypes/ValueType Categories of Properties (CFrame, UDim2 so on)~~ Full API Dump Solves this ?
|
||||
- [x] Bring said DataType serializer into an outside function
|
||||
- [ ] ~~Bypass NotCreatable by hardcoding links/references/indexes to said Classes
|
||||
- [ ] ~~Bypass NotCreatable by hardcoding links/references/indexes to said Classes~~ Should be Solved by IsPropertyModified
|
||||
- Example: Terrain class can be indexed by doing workspace.Terrain but is NotCreatable
|
||||
- [x] Check if table.concat is actually the fastest way as compared to other alternatives (IT'S NOT)
|
||||
- [ ] Do ~~clean-up in inheritor &~~ (API Dumps solve this, illogical) automatically assume the top-most class that owns the property, while also cleaning up said property from classes that inherit from it
|
||||
- [x] Do ~~clean-up in inheritor &~~ (API Dumps solve this, illogical) automatically assume the top-most class that owns the property, while also cleaning up said property from classes that inherit from it
|
||||
- This will be only be needed if we try to implement our own scanning for hidden properties in which case a lot of duplicates might arise that need to be tracked down to instance they all inherit from & cleaned up respectively
|
||||
- [x] Fix indexes being mixed up after table.remove shifting
|
||||
- [x] Hidden properties
|
||||
- [x] ~~Scan for them~~ Full API Dump Solves this
|
||||
- [ ] ~~Scan game & map instances in format {ClassName = {Instance1, Instance2} }, if none found then attempt to create proper Replica for them~~ Full API Dump Solves this
|
||||
- [x] ~~Scan game & map instances in format {ClassName = {Instance1, Instance2} }, if none found then attempt to create proper Replica for them~~ Full API Dump Solves this
|
||||
* This will help with getting many ValueTypes accurately, especially BinaryStrings vs strings
|
||||
- [] ~~Inherit them properly & do the clean-up~~ Full API Dump Solves this
|
||||
- [x] ~~Inherit them properly & do the clean-up~~ Full API Dump Solves this
|
||||
- [x] ~~Tell whether ValueType is string or BinaryString~~ Full API Dump Solves this
|
||||
- [ ] Look into adding support for Binary Format Output (rbxl/rbxm)
|
||||
- .RBXL files are similar to .RBXLX files but are saved in Binary format, which helps reduce the file size.
|
||||
- ! Check out string.pack & string.unpack for more information !
|
||||
- ! Check out string.pack/.unpack & bit32 library for more information !
|
||||
- [ ] Support for Model files:
|
||||
- [x] rbxmx (xml)
|
||||
- [ ] rbxm (binary)
|
||||
- [ ] ~~Possibly convert to non-Name tables & use instance references instead (Perhaps make a config Bool Toggle for this, false by default), ex. DecompileIgnore = {game.CoreGui}~~ Add too much complexity for now
|
||||
- This will allow for more flexibility of saveinstancing
|
||||
- [x] ~~Remove Useless tables & functions of specialinfo~~ Repurposed
|
||||
- [ ] Replace all operators with compound operators if possible, make sure to add other LuaU syntax too (matters for performance!)
|
||||
- [x] Implement [Luau Syntax] (matters for performance!):
|
||||
|
||||
- [x] Compound Operators
|
||||
- [x] Avoid using `next`, `ipairs` & `pairs`
|
||||
- [ ] Type-checking (😩🙀)
|
||||
- [ ] `local maxValue = if a > b then a else b` expressions
|
||||
- [ ] print(`Bob has {count} apple(s)!`) expressions
|
||||
- [] Floor division
|
||||
|
||||
- [ ] Speed things up as much as possible
|
||||
- Requires benchmarks
|
||||
- Requires looking at other scripts of ours that are aimed at speed & performance
|
||||
- [x] Support for NotScriptable Properties
|
||||
- Requires gethiddenproperty support
|
||||
- [ ] Support for as many [KRNL-like saveinstance options](https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D):
|
||||
- [ ] Support for as many [KRNL-like saveinstance Options]:
|
||||
- Change mode to invalid mode like "custom" if you only want to save ExtraInstances
|
||||
* [x] Decompile (! This takes priority over OPTIONS.noscripts if set !)
|
||||
* [x] DecompileIgnore
|
||||
* [x] DecompileTimeout (! This takes priority over OPTIONS.timeout if set !)
|
||||
* [x] ExtraInstances
|
||||
* [x] FilePath
|
||||
* [x] IgnoreDefaultProps
|
||||
* [x] IsolateStarterPlayer
|
||||
* [x] NilInstances
|
||||
* [x] Object (for .rbxmx files)
|
||||
* [x] RemovePlayerCharacters
|
||||
* [x] SavePlayers
|
||||
* [x] ShowStatus
|
||||
* [ ] IsolatePlayerGui
|
||||
- [ ] Support for as many Executors as possible (🤢🤮)
|
||||
- [x] ~~Use getspecialinfo fallback function carefully as it's hardcoded~~ Useless because there's no way to tell if the Property Values of those instances are default or not
|
||||
- LOOK INTO Instance:IsPropertyModified & Instance:ResetPropertyToDefault
|
||||
- [ ] Isolators must clear
|
||||
- [x] Isolators must clear
|
||||
- [x] Store all functions outside that are used during saveinstancing for sake of performance
|
||||
- [ ] Remove buffersize, savebuffer & so on for sake of performance by concatenating <Item> strings to total string then writing it to file (no extra steps like table.concat)
|
||||
- [x] ~~Remove buffersize, savebuffer & so on for sake of performance by concatenating <Item> strings to total string then writing it to file (no extra steps like table.concat)~~ table.concat proved faster in the case of huge amount of concatenations
|
||||
- Test table.concat vs string ..= with a full buffer (this benchmark differs per usecase)
|
||||
- [ ] Make sure BinaryStrings are compared to Defaults properly (aka in same format)
|
||||
- [ ] Add Option to restart saveinstance from the point that it crashed on
|
||||
- [ ] Check out DataType exceptions [Here](https://github.com/rojo-rbx/rbx-dom/blob/master/rbx_reflector/src/cli/generate.rs#L260)
|
||||
- [ ] Check out [DataType Exceptions]
|
||||
- [x] Add README Similar to current Synapse
|
||||
- [ ] Ignore all properties of instances that aren't Local or Module Scripts except Name if mode is set to "scripts"
|
||||
- [ ] Maybe modes should do more than just affecting the list of instances to save, like changing IgnoreDefaultProperties to false if mode is "full" for example
|
||||
- [x] Add Support for SharedStrings [Docs](https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md#sharedstring)
|
||||
- Fun fact: SharedStrings can also be used for ValueTypes that aren't `SharedString`, this behavior is not documented anywhere but makes sense (Could create issues though, due to _potential_ ValueType mix-up). By replacing `<BinaryString name="Tags">Base64EncodedValue</BinaryString>` with `<SharedString name="Tags">UniqueIdentifierForSharedString</SharedString>`and putting `<SharedString md5="UniqueIdentifierForSharedString">Base64EncodedValue</SharedString>` into SharedStrings container you can achieve this amazing behaviour. This should be only enabled using an optional setting ()<br />Only known to work with (probably because both are base64 encoded):
|
||||
- [x] Add Support for [SharedStrings]
|
||||
- Fun fact: SharedStrings can also be used for ValueTypes that aren't `SharedString`, this behavior is not documented anywhere but makes sense (Could create issues though, due to _potential_ ValueType mix-up). By replacing `<BinaryString name="Tags">Base64EncodedValue</BinaryString>` with `<SharedString name="Tags">UniqueIdentifierForSharedString</SharedString>` & putting `<SharedString md5="UniqueIdentifierForSharedString">Base64EncodedValue</SharedString>` into SharedStrings container you can achieve this amazing behaviour. This should be only enabled using an optional setting<br />Only known to work with (probably because both are base64 encoded):
|
||||
* BinaryString
|
||||
- [ ] Add Lua & Luau versions instead of merged
|
||||
- [x] Add Lua & Luau versions instead of merged (WARNING: LUAU WILL ALWAYS BE MORE UPDATED THAN LUA VERSION - IDC & CBA SIMPLY, lua version exists just for sake of old & bad executors, ask devs of your executors to support luau as its latest & greatest)
|
||||
- [ ] Add Support for multiple Instances to be saved as a model
|
||||
- [ ] Do something about devs renaming Services therefore bypassing Ignore lists (CoreGui/CorePackages are not affected)
|
||||
- LOOK INTO Instance:IsPropertyModified & Instance:ResetPropertyToDefault
|
||||
- [ ] Fix Player's Characters not being visible (must Refresh MeshId)
|
||||
- "https://assetdelivery.roblox.com/v1/asset/?id=" Could cause issues too (needs testing)
|
||||
- Perhaps add a possible FIX script to README
|
||||
- [ ] Be able to exclude / blacklist any mentions of certain string in other strings
|
||||
- Example: You wish to blacklist your player's name from appearing in any property value
|
||||
- Default options like IsolateSomething might also use / influence this
|
||||
- [ ] Force disable ParticleEmitters in case something like IgnorePropertiesOfNotScriptsOnScriptsMode is enabled (they stack in one place and create huge lag)
|
||||
|
||||
# Acknowledgments
|
||||
|
||||
This document is based largely on the efforts of [@Anaminus](https://github.com/Anaminus) & [@Dekkonot](https://github.com/Dekkonot), authors of the [Rbxlx Format Specifications](https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md). Additional
|
||||
This document is based largely on the efforts of [@Anaminus] & [@Dekkonot], authors of the [Roblox Format Specifications]. Additional
|
||||
resources include:
|
||||
|
||||
- [Syngp Synapse X 2019 Source code](https://github.com/Acrillis/SynapseX) for base saveinstance code (extended by [@mblouka](https://github.com/mblouka) & [@Acrillis](https://github.com/Acrillis))
|
||||
- [Roblox File Format](https://github.com/MaximumADHD/Roblox-File-Format) for a list of redirects of old/deprecated xml properties that still use the old tag values
|
||||
- [Roblox Client Tracker](https://github.com/MaximumADHD/Roblox-Client-Tracker) for an extended & close to full JSON Api Dump (with hidden properties & default values)
|
||||
- [Syngp Synapse X Source code 2019][Synapse X Source 2019] for base saveinstance code (extended by [@mblouka] & [@Acrillis])
|
||||
- [Roblox File Format] for a list of redirects of old/deprecated xml properties that still use the old tag values
|
||||
- [Roblox Client Tracker] for an extended & close to full JSON Api Dump (with hidden properties & default values)
|
||||
|
||||
[@Acrillis]: https://github.com/Acrillis
|
||||
[@Anaminus]: https://github.com/Anaminus
|
||||
[@Dekkonot]: https://github.com/Dekkonot
|
||||
[@mblouka]: https://github.com/mblouka
|
||||
[DataType Exceptions]: https://github.com/rojo-rbx/rbx-dom/blob/master/rbx_reflector/src/cli/generate.rs#L260
|
||||
[KRNL Docs]: https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D
|
||||
[KRNL-like saveinstance Options]: https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D
|
||||
[Luau Syntax]: https://luau-lang.org/syntax
|
||||
[Roblox Client Tracker]: https://github.com/MaximumADHD/Roblox-Client-Tracker
|
||||
[Roblox File Format]: https://github.com/MaximumADHD/Roblox-File-Format
|
||||
[Roblox Format Specifications]: https://github.com/RobloxAPI/spec/
|
||||
[SharedStrings]: https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md#sharedstring
|
||||
[Synapse X Docs Old]: https://synapsexdocs.github.io/custom-lua-functions/misc-functions/#save-instance
|
||||
[Synapse X Docs]: https://docs.synapse.to/reference/misc.html?highlight=saveins#save-instance
|
||||
[Synapse X Source 2019]: https://github.com/Acrillis/SynapseX
|
||||
[PropertyPatches v1]: https://github.com/MaximumADHD/Roblox-File-Format/blob/main/Plugins/GenerateApiDump/PropertyPatches.lua#L72
|
||||
[PropertyPatches v2]: https://github.com/rojo-rbx/rbx-dom/tree/master/patches
|
||||
[PropertyPatches v3]: https://github.com/rojo-rbx/rbx-dom/blob/9716af360307eb5da5f97d54d84d694b2bc06acf/rbx_dom_lua/src/customProperties.lua
|
||||
|
||||
1411
saveinstance.lua
Normal file
1411
saveinstance.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,9 @@ local finder, globalcontainer = loadstring(game:HttpGet(Params.RepoURL .. Params
|
||||
|
||||
finder({
|
||||
-- readbinarystring = '(...):find("bin",nil,true)', -- ! Could match some unwanted stuff
|
||||
decompile = '(...):find("decomp",nil,true) or (...):find("assembl",nil,true)',
|
||||
decompile = '((...):find("decomp",nil,true) and (...):sub(#...) ~= "s") or (...):find("assembl",nil,true)',
|
||||
gethiddenproperty = '(...):find("get",nil,true) and (...):find("h",nil,true) and (...):find("prop",nil,true) and (...):sub(#...) ~= "s"',
|
||||
sethiddenproperty = '(...):find("set",nil,true) and (...):find("h",nil,true) and (...):find("prop",nil,true) and (...):sub(#...) ~= "s"',
|
||||
gethui = '(...):find("get",nil,true) and (...):find("h",nil,true) and (...):find("ui",nil,true)',
|
||||
getnilinstances = '(...):find("nil",nil,true)', -- ! Could match some unwanted stuff
|
||||
getproperties = '(...):find("get",nil,true) and (...):find("prop",nil,true) and (...):sub(#...) == "s"',
|
||||
@@ -20,6 +21,7 @@ finder({
|
||||
|
||||
local decompile = globalcontainer.decompile
|
||||
local gethiddenproperty = globalcontainer.gethiddenproperty
|
||||
local sethiddenproperty = globalcontainer.sethiddenproperty
|
||||
local getproperties = globalcontainer.getproperties
|
||||
local writefile = globalcontainer.writefile
|
||||
|
||||
@@ -105,10 +107,11 @@ Descriptors = {
|
||||
__BINARYSTRING = Base64_Encode,
|
||||
__BIT = function(...) -- * Credits to Friend (you know yourself)
|
||||
local Value = 0
|
||||
|
||||
for Index, Bit in { ... } do
|
||||
local PackedArgs = { ... }
|
||||
for Index = 1, #PackedArgs do
|
||||
local Bit = PackedArgs[Index]
|
||||
if Bit then
|
||||
Value += 2 ^ (Index - 1)
|
||||
Value = Value + 2 ^ (Index - 1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -196,8 +199,8 @@ Descriptors = {
|
||||
|
||||
local Converted = ""
|
||||
local Keypoints = raw.Keypoints
|
||||
for _index_2 = 1, #Keypoints do
|
||||
local v = Keypoints[_index_2]
|
||||
for Index = 1, #Keypoints do
|
||||
local v = Keypoints[Index]
|
||||
local Value = v.Value
|
||||
Converted ..= v.Time .. " " .. Value.R .. " " .. Value.G .. " " .. Value.B .. " 0 " -- * " 0" is Envelope: Has the range 0 - 1. Currently unused by Roblox.
|
||||
end
|
||||
@@ -233,8 +236,8 @@ Descriptors = {
|
||||
--The value is the text content, formatted as a space-separated list of floating point numbers.
|
||||
local Converted = ""
|
||||
local Keypoints = raw.Keypoints
|
||||
for _index_2 = 1, #Keypoints do
|
||||
local v = Keypoints[_index_2]
|
||||
for Index = 1, #Keypoints do
|
||||
local v = Keypoints[Index]
|
||||
Converted ..= v.Time .. " " .. v.Value .. " " .. v.Envelope .. " "
|
||||
end
|
||||
|
||||
@@ -395,8 +398,11 @@ if getproperties then
|
||||
globalcontainer.getspecialinfo = function(instance)
|
||||
local specialinfo = getproperties(instance)
|
||||
|
||||
for Property, Value in getreal(instance) do
|
||||
specialinfo[Property] = Value
|
||||
local ok, result = pcall(getreal, instance) -- * Some executors only allow certain Classes for this method (like UnionOperation, MeshPart, Terrain), for example Electron
|
||||
if ok then
|
||||
for Property, Value in result do
|
||||
specialinfo[Property] = Value
|
||||
end
|
||||
end
|
||||
|
||||
return specialinfo
|
||||
@@ -411,10 +417,24 @@ local getspecialinfo = globalcontainer.getspecialinfo
|
||||
local function getsafeproperty(instance, PropertyName)
|
||||
return instance[PropertyName]
|
||||
end
|
||||
local function setsafeproperty(instance, PropertyName, Value)
|
||||
instance[PropertyName] = Value
|
||||
end
|
||||
|
||||
local function IsPropertyModified(instance, ProperyName)
|
||||
return instance:IsPropertyModified(ProperyName)
|
||||
end
|
||||
local function ResetPropertyToDefault(instance, ProperyName)
|
||||
instance:ResetPropertyToDefault(ProperyName)
|
||||
end
|
||||
|
||||
local function SetProperty(instance, PropertyName, Value)
|
||||
local ok = pcall(setsafeproperty, instance, PropertyName, Value)
|
||||
if not ok then
|
||||
ok = pcall(sethiddenproperty, instance, PropertyName, Value)
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function ReadProperty(Property, instance, PropertyName, specialProperties, Special)
|
||||
local raw
|
||||
@@ -482,11 +502,29 @@ end
|
||||
|
||||
local ldeccache = {}
|
||||
|
||||
local function ArrayToDictionary(Table)
|
||||
local function ArrayToDictionary(Table, HybridMode)
|
||||
local tmp = table.create(#Table)
|
||||
|
||||
for _, Key in Table do
|
||||
tmp[Key] = true
|
||||
if HybridMode == "table" then
|
||||
for Some1, Some2 in Table do
|
||||
if type(Some1) == "number" then
|
||||
tmp[Some2] = true
|
||||
else
|
||||
tmp[Some1] = ArrayToDictionary(Some2, "table") -- Some1 is Class, Some2 is Name
|
||||
end
|
||||
end
|
||||
elseif HybridMode == "bool" then
|
||||
for Some1, Some2 in Table do
|
||||
if type(Some1) == "number" then
|
||||
tmp[Some2] = true
|
||||
else
|
||||
tmp[Some1] = Some2
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, Key in Table do
|
||||
tmp[Key] = true
|
||||
end
|
||||
end
|
||||
|
||||
return tmp
|
||||
@@ -656,7 +694,7 @@ local referents = setmetatable({
|
||||
})
|
||||
|
||||
local function ReturnItem(ClassName, instance)
|
||||
return '<Item class="' .. ClassName .. '" referent="' .. referents[instance] .. '"><Properties>'
|
||||
return '<Item class="' .. ClassName .. '" referent="' .. referents[instance] .. '"><Properties>' -- TODO: Ideally this shouldn't return <Properties> as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is ENABLED
|
||||
end
|
||||
local function ReturnProperty(Tag, PropertyName, Value)
|
||||
return "<" .. Tag .. ' name="' .. PropertyName .. '">' .. Value .. "</" .. Tag .. ">"
|
||||
@@ -721,6 +759,7 @@ StatusText.TextStrokeTransparency = 0.7
|
||||
StatusText.TextXAlignment = Enum.TextXAlignment.Right
|
||||
StatusText.TextYAlignment = Enum.TextYAlignment.Top
|
||||
|
||||
local ScriptsClasses = ArrayToDictionary({ "LocalScript", "ModuleScript", Script = false }, "bool")
|
||||
local function synsaveinstance(CustomOptions)
|
||||
table.clear(SharedStrings)
|
||||
|
||||
@@ -735,31 +774,47 @@ local function synsaveinstance(CustomOptions)
|
||||
-- decomptype = "new", -- * Deprecated
|
||||
timeout = 30,
|
||||
--* New:
|
||||
__DEBUG_MODE = false,
|
||||
__DEBUG_MODE = false, -- Recommended to enable if you wish to help us improve our products and find bugs / issues with it!
|
||||
DecompileIgnore = { -- * Clean these up (merged Old Syn and New Syn)
|
||||
"Chat",
|
||||
"TextChatService",
|
||||
},
|
||||
}, -- Descendants of ClassNames specified here will be saved but not decompiled
|
||||
--[[ Explanation of structure for DecompileIgnore
|
||||
{
|
||||
"Chat", - This affects any descendants of instance with "Chat" ClassName
|
||||
Players = {"MyPlayerName"} - - This affects any descendants of instance with "Players" .Class AND "MyPlayerName" .Name ONLY
|
||||
}
|
||||
]]
|
||||
InstancesBlacklist = { "CoreGui", "CorePackages" },
|
||||
|
||||
--[[ Explanation of structure for InstancesBlacklist
|
||||
{
|
||||
"CoreGui", - This affects any descendants of instance with "Chat" ClassName
|
||||
Players = {"MyPlayerName"} - - This affects any descendants of instance with "Players" .Class AND "MyPlayerName" .Name ONLY
|
||||
}
|
||||
]]
|
||||
ExtraInstances = {},
|
||||
NilInstances = true,
|
||||
ShowStatus = true,
|
||||
Instance = false, -- If provided, saves as .rbxmx (Model file) instead -- ! MUST BE AN INSTANCE REFERENCE like game.Workspace for example; "optimized" mode is NOT supported with this option
|
||||
-- Binary = false, -- true in actualy syn newer versions
|
||||
FilePath = false, -- does not need to contain a file extension, only the name of the file.
|
||||
Object = false, -- If provided, saves as .rbxmx (Model file) instead; If Object is game, it will be saved as a .RBXL file -- ! MUST BE AN INSTANCE REFERENCE like game.Workspace for example; "optimized" mode is NOT supported with this option
|
||||
-- Binary = false, -- true in syn newer versions (false in our case because no binary support yet)
|
||||
-- Decompile = not OPTIONS.noscripts, -- ! This takes priority over OPTIONS.noscripts if set
|
||||
-- DecompileTimeout = OPTIONS.timeout, -- ! This takes priority over OPTIONS.timeout if set
|
||||
IgnoreDefaultProperties = true,
|
||||
IgnoreSpecialProperties = false, -- true will disable Terrain too
|
||||
IgnoreNotArchivable = true,
|
||||
-- IsolateStarterPlayer = true,
|
||||
IsolateLocalPlayer = true, -- Saves Children of LocalPlayer as separate folder and prevents any instance with .Name identical to LocalPlayer.Name from saving
|
||||
IgnorePropertiesOfNotScriptsOnScriptsMode = false, -- Ignores property of every instance that is not a script in "scripts" mode
|
||||
IgnoreSpecialProperties = false, -- true will disable Terrain & some other things
|
||||
-- IsolatePlayerGui = false,
|
||||
IsolateStarterPlayer = true, --If enabled, StarterPlayer will be cleared and the saved starter player will be placed into folders.
|
||||
IsolateLocalPlayer = false, -- Saves Children of LocalPlayer as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving
|
||||
IsolateLocalPlayerCharacter = false, -- Saves Children of LocalPlayer.Character as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving
|
||||
-- MaxThreads = 3
|
||||
-- RemovePlayerCharacters = true,
|
||||
RemovePlayerCharacters = true, -- If enabled, player characters will not be saved.
|
||||
SavePlayers = false,
|
||||
SaveCacheInterval = 0x1600,
|
||||
SaveCacheInterval = 0x1600, -- The less the more often it saves, but that would mean less performance due to constantly saving
|
||||
ReadMe = true,
|
||||
-- ! Risky
|
||||
-- AllowResettingProperties = false, -- Enables Resetting of properties for sake of checking their default value (Useful for cases when Instance is NotCreatable like services yet we need to get the default value ) then sets the property back to the original value, which might get detected by some games
|
||||
AllowResettingProperties = false, -- Enables Resetting of properties for sake of checking their default value (Useful for cases when Instance is NotCreatable like services yet we need to get the default value ) then sets the property back to the original value, which might get detected by some games --! WARNING: Sometimes Properties might not be able to be set to the original value due to circumstances
|
||||
SharedStringOverwrite = false, -- ! if the process is not finished aka crashed then none of the affected values will be available; SharedStrings can also be used for ValueTypes that aren't `SharedString`, this behavior is not documented anywhere but makes sense (Could create issues though, due to _potential_ ValueType mix-up, only works on certain types which are all base64 encoded so far); Reason: Allows for potential smaller file size (can also be bigger in some cases)
|
||||
}
|
||||
|
||||
@@ -777,6 +832,10 @@ local function synsaveinstance(CustomOptions)
|
||||
if DecompileTimeout ~= nil then
|
||||
OPTIONS.timeout = DecompileTimeout
|
||||
end
|
||||
local IgnoreDefaultProps = CustomOptions.IgnoreDefaultProps
|
||||
if IgnoreDefaultProps ~= nil then
|
||||
OPTIONS.IgnoreDefaultProperties = IgnoreDefaultProps
|
||||
end
|
||||
else
|
||||
CustomOptions = {}
|
||||
end
|
||||
@@ -809,7 +868,13 @@ local function synsaveinstance(CustomOptions)
|
||||
return "-- Decompiling is NOT supported on your executor"
|
||||
end
|
||||
end
|
||||
local ToSaveInstance = OPTIONS.Instance
|
||||
local ToSaveInstance = OPTIONS.Object
|
||||
|
||||
if ToSaveInstance == game then
|
||||
ToSaveInstance = nil
|
||||
end
|
||||
local FilePath = OPTIONS.FilePath
|
||||
local IgnorePropertiesOfNotScriptsOnScriptsMode = OPTIONS.IgnorePropertiesOfNotScriptsOnScriptsMode
|
||||
local placename
|
||||
|
||||
local ToSaveList
|
||||
@@ -822,6 +887,12 @@ local function synsaveinstance(CustomOptions)
|
||||
if mode == "optimized" then -- ! NOT supported with .rbxmx
|
||||
mode = "full"
|
||||
end
|
||||
if not CustomOptions.IsolateLocalPlayerCharacter then
|
||||
OPTIONS.IsolateLocalPlayerCharacter = false
|
||||
end
|
||||
if not CustomOptions.IsolateStarterPlayer then
|
||||
OPTIONS.IsolateStarterPlayer = false
|
||||
end
|
||||
if not CustomOptions.IsolateLocalPlayer then
|
||||
OPTIONS.IsolateLocalPlayer = false
|
||||
end
|
||||
@@ -829,9 +900,13 @@ local function synsaveinstance(CustomOptions)
|
||||
OPTIONS.NilInstances = false
|
||||
end
|
||||
|
||||
placename = "model" .. PlaceId .. "_" .. ToSaveInstance:GetDebugId(0) .. ".rbxmx" -- * GetDebugId is only unique per instance within same game session, after rejoining it might be different
|
||||
placename = (FilePath or "model" .. PlaceId .. "_" .. ToSaveInstance:GetDebugId(0)) .. ".rbxmx" -- * GetDebugId is only unique per instance within same game session, after rejoining it might be different
|
||||
else
|
||||
placename = "place" .. PlaceId .. ".rbxlx"
|
||||
placename = (FilePath or "place" .. PlaceId) .. ".rbxlx"
|
||||
end
|
||||
|
||||
if mode ~= "scripts" then
|
||||
IgnorePropertiesOfNotScriptsOnScriptsMode = nil
|
||||
end
|
||||
|
||||
local TempRoot = ToSaveInstance or game
|
||||
@@ -869,9 +944,8 @@ local function synsaveinstance(CustomOptions)
|
||||
local cach = {}
|
||||
for _index_0 = 1, #Hierarchy do
|
||||
local instance = Hierarchy[_index_0]
|
||||
local ClassName = instance.ClassName
|
||||
|
||||
if ClassName == "LocalScript" or ClassName == "ModuleScript" then
|
||||
if ScriptsClasses[instance.ClassName] then
|
||||
local Parent = instance.Parent
|
||||
while Parent and Parent ~= TempRoot do
|
||||
instance = instance.Parent
|
||||
@@ -890,33 +964,37 @@ local function synsaveinstance(CustomOptions)
|
||||
end
|
||||
|
||||
local DecompileIgnore, InstancesBlacklist =
|
||||
ArrayToDictionary(OPTIONS.DecompileIgnore), ArrayToDictionary(OPTIONS.InstancesBlacklist)
|
||||
ArrayToDictionary(OPTIONS.DecompileIgnore, "table"), ArrayToDictionary(OPTIONS.InstancesBlacklist, "table")
|
||||
|
||||
local IgnoreSpecialProperties = OPTIONS.IgnoreSpecialProperties
|
||||
local IgnoreNotArchivable = OPTIONS.IgnoreNotArchivable
|
||||
local __DEBUG_MODE = OPTIONS.__DEBUG_MODE
|
||||
local AllowResettingProperties = OPTIONS.AllowResettingProperties
|
||||
local IgnoreDefaultProperties = OPTIONS.IgnoreDefaultProperties
|
||||
local IgnoreNotArchivable = OPTIONS.IgnoreNotArchivable
|
||||
local IgnoreSpecialProperties = OPTIONS.IgnoreSpecialProperties
|
||||
local IsolateLocalPlayer = OPTIONS.IsolateLocalPlayer
|
||||
|
||||
local IsolateLocalPlayerCharacter = OPTIONS.IsolateLocalPlayerCharacter
|
||||
local IsolateStarterPlayer = OPTIONS.IsolateStarterPlayer
|
||||
local SaveCacheInterval = OPTIONS.SaveCacheInterval
|
||||
local SharedStringOverwrite = OPTIONS.SharedStringOverwrite
|
||||
local __DEBUG_MODE = OPTIONS.__DEBUG_MODE
|
||||
|
||||
local function getsizeformat()
|
||||
local size
|
||||
for i, BinaryPrefix in
|
||||
{
|
||||
"B",
|
||||
"KB",
|
||||
"MB",
|
||||
"GB",
|
||||
"TB",
|
||||
}
|
||||
do
|
||||
local Size
|
||||
local BinaryPrefixes = {
|
||||
"B",
|
||||
"KB",
|
||||
"MB",
|
||||
"GB",
|
||||
"TB",
|
||||
}
|
||||
for Index = 1, #BinaryPrefixes do
|
||||
local buffersize = #total
|
||||
if buffersize < 0x400 ^ i then
|
||||
size = math.floor(buffersize / (0x400 ^ (i - 1)) * 10) / 10 .. " " .. BinaryPrefix
|
||||
if buffersize < 0x400 ^ Index then
|
||||
Size = math.floor(buffersize / (0x400 ^ (Index - 1)) * 10) / 10 .. " " .. BinaryPrefixes[Index]
|
||||
break
|
||||
end
|
||||
end
|
||||
return size
|
||||
return Size
|
||||
end
|
||||
|
||||
local function savecache()
|
||||
@@ -937,165 +1015,190 @@ local function synsaveinstance(CustomOptions)
|
||||
savecache()
|
||||
end
|
||||
for _index_0 = 1, #Hierarchy do
|
||||
repeat
|
||||
local instance = Hierarchy[_index_0]
|
||||
local instance = Hierarchy[_index_0]
|
||||
|
||||
local ClassName = instance.ClassName
|
||||
local InstanceName = instance.Name
|
||||
local ClassName = instance.ClassName
|
||||
local InstanceName = instance.Name
|
||||
local Blacklisted = InstancesBlacklist[ClassName]
|
||||
if
|
||||
instance.RobloxLocked
|
||||
or not ClassList[ClassName]
|
||||
or IgnoreNotArchivable and not instance.Archivable
|
||||
or Blacklisted and (Blacklisted == true or Blacklisted[InstanceName])
|
||||
then
|
||||
continue
|
||||
end
|
||||
|
||||
if
|
||||
instance.RobloxLocked
|
||||
or InstancesBlacklist[InstanceName]
|
||||
or not ClassList[ClassName]
|
||||
or IgnoreNotArchivable and not instance.Archivable
|
||||
then
|
||||
break
|
||||
end
|
||||
if not DecompileIgnoring then
|
||||
local DecompileIgnored = DecompileIgnore[ClassName]
|
||||
DecompileIgnoring = DecompileIgnored
|
||||
and (DecompileIgnored == true or DecompileIgnored[InstanceName])
|
||||
and instance
|
||||
end
|
||||
|
||||
if not DecompileIgnoring then
|
||||
DecompileIgnoring = DecompileIgnore[InstanceName] and instance
|
||||
end
|
||||
|
||||
local Properties = inheritedproperties[ClassName]
|
||||
savebuffer[#savebuffer + 1] = ReturnItem(ClassName, instance)
|
||||
local Properties = inheritedproperties[ClassName]
|
||||
savebuffer[#savebuffer + 1] = ReturnItem(ClassName, instance) -- TODO: Ideally this shouldn't return <Properties> as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is ENABLED
|
||||
local Ignored
|
||||
if IgnorePropertiesOfNotScriptsOnScriptsMode and ScriptsClasses[ClassName] == nil then
|
||||
Ignored = true
|
||||
end
|
||||
if not Ignored then
|
||||
local specialProperties, Replica
|
||||
for _index_1 = 1, #Properties do
|
||||
local Property = Properties[_index_1]
|
||||
local PropertyName = Property.Name
|
||||
|
||||
repeat
|
||||
local Special = Property.Special
|
||||
if IgnoreSpecialProperties and Special then
|
||||
break
|
||||
end
|
||||
local raw
|
||||
raw, specialProperties =
|
||||
ReadProperty(Property, instance, PropertyName, specialProperties, Special)
|
||||
if raw == "__BREAK" then
|
||||
break
|
||||
end
|
||||
local ValueType = Property.ValueType
|
||||
if SharedStringOverwrite and ValueType == "BinaryString" then -- TODO: Convert this to table if more types are added
|
||||
ValueType = "SharedString"
|
||||
local Special = Property.Special
|
||||
if IgnoreSpecialProperties and Special then
|
||||
continue
|
||||
end
|
||||
local raw
|
||||
raw, specialProperties = ReadProperty(Property, instance, PropertyName, specialProperties, Special)
|
||||
if raw == "__BREAK" then
|
||||
continue
|
||||
end
|
||||
local ValueType = Property.ValueType
|
||||
if SharedStringOverwrite and ValueType == "BinaryString" then -- TODO: Convert this to table if more types are added
|
||||
ValueType = "SharedString"
|
||||
end
|
||||
|
||||
local Category = Property.Category
|
||||
|
||||
if IgnoreDefaultProperties and PropertyName ~= "Source" then -- ? Source is special, might need to be changed to check for LuaSourceContainer IsA instead
|
||||
local ok, IsModified = pcall(IsPropertyModified, instance, PropertyName) -- ? Not yet enabled lol (580)
|
||||
if ok and not IsModified then
|
||||
continue
|
||||
end
|
||||
|
||||
local Category = Property.Category
|
||||
local Default = Property.Default
|
||||
|
||||
if IgnoreDefaultProperties and PropertyName ~= "Source" then -- ? Source is special, might need to be changed to check for LuaSourceContainer IsA instead
|
||||
local ok, IsModified = pcall(IsPropertyModified, instance, PropertyName) -- ? Not yet enabled lol (580)
|
||||
if ok and not IsModified then
|
||||
break
|
||||
if OverwriteDefaults[Default] then
|
||||
local ClassTags = ClassList[ClassName].Tags
|
||||
|
||||
local NotCreatable = ClassTags and ClassTags.NotCreatable
|
||||
|
||||
local Reset
|
||||
|
||||
if NotCreatable then -- TODO: This whole block should only run if Replica doesn't exist yet, except ResetPropertyToDefault because it's needed for just about every property of NotCreatable objects (in order to check default if undefined in API Dump)
|
||||
if AllowResettingProperties then
|
||||
Reset = pcall(ResetPropertyToDefault, instance, PropertyName)
|
||||
if Reset and not Replica then
|
||||
Replica = instance
|
||||
end
|
||||
end
|
||||
elseif not Replica then
|
||||
Replica = classreplicas[ClassName]
|
||||
end
|
||||
|
||||
local Default = Property.Default
|
||||
if Replica and not (NotCreatable and not Reset) then
|
||||
Default = ReadProperty(Property, Replica, PropertyName, specialProperties, Special)
|
||||
-- * Improve this along with specialProperties (merge or maybe store the method to Property.Special), get this property at any cost
|
||||
|
||||
if OverwriteDefaults[Default] then
|
||||
local ClassTags = ClassList[ClassName].Tags
|
||||
if not (ClassTags and ClassTags.NotCreatable) then
|
||||
Replica = classreplicas[ClassName]
|
||||
|
||||
Default = ReadProperty(Property, Replica, PropertyName, specialProperties, Special)
|
||||
-- * Improve this along with specialProperties (merge or maybe store the method to Property.Special), get this property at any cost
|
||||
|
||||
Default = ApiFormatify(Default, Category, ValueType)
|
||||
Property.Default = Default
|
||||
-- if Property.Special then
|
||||
-- end
|
||||
if Reset and not SetProperty(Replica, PropertyName, raw) and __DEBUG_MODE then -- It has been reset
|
||||
warn(
|
||||
"FAILED TO SET BACK TO ORIGINAL VALUE (OPEN A GITHUB ISSUE): ",
|
||||
ValueType,
|
||||
ClassName,
|
||||
PropertyName
|
||||
)
|
||||
end
|
||||
elseif Default == "default" and ValueType == "PhysicalProperties" then
|
||||
Default = "nil"
|
||||
|
||||
Default = ApiFormatify(Default, Category, ValueType)
|
||||
Property.Default = Default
|
||||
-- if Property.Special then
|
||||
-- end
|
||||
end
|
||||
|
||||
if ApiFormatify(raw, Category, ValueType, Default) == Default then -- ! PhysicalProperties, Font, CFrame, BrickColor (and Enum to some extent) aren't being defaulted properly in the api dump, meaning an issue must be created.. (They're not being tostringed or fail to do so)
|
||||
-- print("Default not serializing", PropertyName)
|
||||
|
||||
break
|
||||
end
|
||||
elseif Default == "default" and ValueType == "PhysicalProperties" then
|
||||
Default = "nil"
|
||||
Property.Default = Default
|
||||
end
|
||||
|
||||
local tag, value
|
||||
if Category == "Class" then
|
||||
tag = "Ref"
|
||||
if raw then
|
||||
value = referents[raw]
|
||||
else
|
||||
value = "null"
|
||||
end
|
||||
elseif Category == "Enum" then -- ! We do this order (Enums before Descriptors) specifically because Font Enum might get a Font Descriptor despite having Enum Category, unlike Font DataType which that Descriptor is meant for
|
||||
value, tag = Descriptors.__ENUM(raw)
|
||||
if ApiFormatify(raw, Category, ValueType, Default) == Default then -- ! PhysicalProperties, Font, CFrame, BrickColor (and Enum to some extent) aren't being defaulted properly in the api dump, meaning an issue must be created.. (They're not being tostringed or fail to do so)
|
||||
-- print("Default not serializing", PropertyName)
|
||||
|
||||
continue
|
||||
end
|
||||
end
|
||||
|
||||
local tag, value
|
||||
if Category == "Class" then
|
||||
tag = "Ref"
|
||||
if raw then
|
||||
value = referents[raw]
|
||||
else
|
||||
local Descriptor = Descriptors[ValueType]
|
||||
value = "null"
|
||||
end
|
||||
elseif Category == "Enum" then -- ! We do this order (Enums before Descriptors) specifically because Font Enum might get a Font Descriptor despite having Enum Category, unlike Font DataType which that Descriptor is meant for
|
||||
value, tag = Descriptors.__ENUM(raw)
|
||||
else
|
||||
local Descriptor = Descriptors[ValueType]
|
||||
|
||||
if Descriptor then
|
||||
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
|
||||
elseif "BinaryString" == ValueType then -- TODO: Try fitting this inside Descriptors
|
||||
tag = ValueType
|
||||
value = Descriptors.__BINARYSTRING(raw)
|
||||
if Descriptor then
|
||||
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
|
||||
elseif "BinaryString" == ValueType then -- TODO: Try fitting this inside Descriptors
|
||||
tag = ValueType
|
||||
value = Descriptors.__BINARYSTRING(raw)
|
||||
|
||||
if
|
||||
PropertyName == "SmoothGrid"
|
||||
or PropertyName == "MaterialColors"
|
||||
or PropertyName == "PhysicsGrid"
|
||||
then
|
||||
value = Descriptors.__CDATA(value)
|
||||
end
|
||||
elseif "ProtectedString" == ValueType then -- TODO: Try fitting this inside Descriptors
|
||||
tag = ValueType
|
||||
if
|
||||
PropertyName == "SmoothGrid"
|
||||
or PropertyName == "MaterialColors"
|
||||
or PropertyName == "PhysicsGrid"
|
||||
then
|
||||
value = Descriptors.__CDATA(value)
|
||||
end
|
||||
elseif "ProtectedString" == ValueType then -- TODO: Try fitting this inside Descriptors
|
||||
tag = ValueType
|
||||
|
||||
if PropertyName == "Source" then
|
||||
if ClassName == "Script" then
|
||||
value = "-- Server scripts can NOT be decompiled"
|
||||
if PropertyName == "Source" then
|
||||
if ScriptsClasses[ClassName] == false then
|
||||
value = "-- Server scripts can NOT be decompiled" --TODO: Could be not just server scrippts in the future
|
||||
else
|
||||
if DecompileIgnoring then
|
||||
value = "-- Ignored"
|
||||
else
|
||||
if DecompileIgnoring then
|
||||
value = "-- Ignored"
|
||||
else
|
||||
value = ldecompile(instance)
|
||||
end
|
||||
value = ldecompile(instance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
value = Descriptors.__PROTECTEDSTRING(value)
|
||||
else
|
||||
--OptionalCoordinateFrame and so on, we make it dynamic
|
||||
local startIDX, endIDX = Find(ValueType, "Optional")
|
||||
if startIDX == 1 then
|
||||
-- Extract the string after "Optional"
|
||||
value = Descriptors.__PROTECTEDSTRING(value)
|
||||
else
|
||||
--OptionalCoordinateFrame and so on, we make it dynamic
|
||||
local startIDX, endIDX = Find(ValueType, "Optional")
|
||||
if startIDX == 1 then
|
||||
-- Extract the string after "Optional"
|
||||
|
||||
Descriptor = Descriptors[ValueType:sub(endIDX + 1)]
|
||||
Descriptor = Descriptors[ValueType:sub(endIDX + 1)]
|
||||
|
||||
if Descriptor then
|
||||
if raw ~= nil then
|
||||
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
|
||||
else
|
||||
value, tag = "", ValueType
|
||||
end
|
||||
if Descriptor then
|
||||
if raw ~= nil then
|
||||
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
|
||||
else
|
||||
value, tag = "", ValueType
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if tag then
|
||||
savebuffer[#savebuffer + 1] = ReturnProperty(tag, PropertyName, value)
|
||||
elseif __DEBUG_MODE then
|
||||
warn("UNSUPPORTED TYPE (OPEN A GITHUB ISSUE): ", ValueType, ClassName, PropertyName)
|
||||
end
|
||||
|
||||
until true
|
||||
end
|
||||
savebuffer[#savebuffer + 1] = "</Properties>"
|
||||
local Children = Afterwards or instance:GetChildren()
|
||||
if #Children ~= 0 then
|
||||
savehierarchy(Children)
|
||||
if tag then
|
||||
savebuffer[#savebuffer + 1] = ReturnProperty(tag, PropertyName, value)
|
||||
elseif __DEBUG_MODE then
|
||||
warn("UNSUPPORTED TYPE (OPEN A GITHUB ISSUE): ", ValueType, ClassName, PropertyName)
|
||||
end
|
||||
end
|
||||
end
|
||||
savebuffer[#savebuffer + 1] = "</Properties>"
|
||||
local Children = Afterwards or instance:GetChildren()
|
||||
if #Children ~= 0 then
|
||||
savehierarchy(Children)
|
||||
end
|
||||
|
||||
if DecompileIgnoring and DecompileIgnoring == instance then
|
||||
DecompileIgnoring = nil
|
||||
end
|
||||
if DecompileIgnoring and DecompileIgnoring == instance then
|
||||
DecompileIgnoring = nil
|
||||
end
|
||||
|
||||
savebuffer[#savebuffer + 1] = "</Item>"
|
||||
|
||||
until true
|
||||
savebuffer[#savebuffer + 1] = "</Item>"
|
||||
end
|
||||
end
|
||||
local function saveextra(Name, Hierarchy, CustomClassName, Source)
|
||||
@@ -1169,19 +1272,30 @@ local function synsaveinstance(CustomOptions)
|
||||
savehierarchy(ToSaveList)
|
||||
end
|
||||
|
||||
if OPTIONS.IsolateLocalPlayer then
|
||||
if IsolateLocalPlayer or IsolateLocalPlayerCharacter then
|
||||
local Players = service.Players
|
||||
local LocalPlayer = Players.LocalPlayer
|
||||
local LocalPlayer_Children = LocalPlayer:GetChildren()
|
||||
saveextra("LocalPlayer", LocalPlayer_Children)
|
||||
if IsolateLocalPlayer then
|
||||
saveextra("LocalPlayer", LocalPlayer:GetChildren())
|
||||
end
|
||||
if IsolateLocalPlayerCharacter then
|
||||
local LocalPlayerCharacter = LocalPlayer.Character
|
||||
if LocalPlayerCharacter then
|
||||
saveextra("LocalPlayer Character", LocalPlayerCharacter:GetChildren())
|
||||
end
|
||||
end
|
||||
end
|
||||
if IsolateStarterPlayer then
|
||||
saveextra("StarterPlayer", service.StarterPlayer:GetChildren())
|
||||
end
|
||||
|
||||
if nilinstances then
|
||||
saveextra("Nil Instances", nilinstances)
|
||||
end
|
||||
saveextra("README", nil, "Script", "--[[\n" .. [[
|
||||
if OPTIONS.ReadMe then
|
||||
saveextra("README", nil, "Script", "--[[\n" .. [[
|
||||
Thank you for using SynSaveInstance Revival.
|
||||
We recommended to save the game right away to take advantage of the binary format (if you didn't save in binary).
|
||||
We recommended to save the game right away to take advantage of the binary format (if you didn't save in binary) AND to preserve values of certain properties if you used IgnoreDefaultProperties setting (as they might change in the future).
|
||||
If your player cannot spawn into the game, please move the scripts in StarterPlayer elsewhere. (This is done by default)
|
||||
If the chat system does not work, please use the explorer and delete everything inside the Chat service.
|
||||
Or run `game:GetService("Chat"):ClearAllChildren()`
|
||||
@@ -1202,7 +1316,7 @@ local function synsaveinstance(CustomOptions)
|
||||
|
||||
This file was generated with the following settings:
|
||||
]] .. service.HttpService:JSONEncode(OPTIONS) .. "\n]]")
|
||||
|
||||
end
|
||||
local tmp = { "<SharedStrings>" }
|
||||
for Identifier, Value in SharedStrings do
|
||||
tmp[#tmp + 1] = '<SharedString md5="' .. Identifier .. '">' .. Value .. "</SharedString>"
|
||||
@@ -1225,15 +1339,46 @@ local function synsaveinstance(CustomOptions)
|
||||
StatusTextClone = StatusText:Clone()
|
||||
StatusTextClone.Parent = StatusGui
|
||||
end
|
||||
if not OPTIONS.SavePlayers then
|
||||
InstancesBlacklist.Players = true
|
||||
if not OPTIONS.SavePlayers and not InstancesBlacklist.Players then
|
||||
InstancesBlacklist.Players = {}
|
||||
end
|
||||
if OPTIONS.IsolateLocalPlayer then
|
||||
if OPTIONS.RemovePlayerCharacters then
|
||||
local Players = service.Players
|
||||
|
||||
local T = InstancesBlacklist.Model
|
||||
if T ~= true then
|
||||
if not T then
|
||||
T = {}
|
||||
end
|
||||
for _, Player in Players:GetPlayers() do
|
||||
T[Player.Name] = true
|
||||
end
|
||||
InstancesBlacklist.Model = T
|
||||
end
|
||||
end
|
||||
if IsolateStarterPlayer then
|
||||
InstancesBlacklist.StarterPlayer = true
|
||||
end
|
||||
if IsolateLocalPlayer or IsolateLocalPlayerCharacter then
|
||||
local Players = service.Players
|
||||
local LocalPlayer = Players.LocalPlayer
|
||||
local LocalPlayerName = LocalPlayer.Name
|
||||
if IsolateLocalPlayer and InstancesBlacklist.Player ~= true then
|
||||
if not InstancesBlacklist.Player then
|
||||
InstancesBlacklist.Player = {}
|
||||
end
|
||||
|
||||
InstancesBlacklist[LocalPlayer.Name] = true
|
||||
InstancesBlacklist.Player[LocalPlayerName] = true
|
||||
end
|
||||
if IsolateLocalPlayerCharacter and InstancesBlacklist.Model ~= true then
|
||||
if not InstancesBlacklist.Model then
|
||||
InstancesBlacklist.Model = {}
|
||||
end
|
||||
|
||||
InstancesBlacklist.Model[LocalPlayerName] = true
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local elapse_t = os.clock()
|
||||
local ok, err = pcall(savegame)
|
||||
@@ -1246,7 +1391,7 @@ local function synsaveinstance(CustomOptions)
|
||||
StatusTextClone.Text = string.format("Saved! Time %.2f seconds; Size %s", elapse_t, getsizeformat())
|
||||
task.wait(Log10 * 2 + 3)
|
||||
else
|
||||
StatusTextClone.Text = "Failed!\nCheck F9 console for more info"
|
||||
StatusTextClone.Text = "Failed! Check F9 console for more info"
|
||||
warn("Error encountered while saving")
|
||||
warn("Information about error:")
|
||||
warn(err)
|
||||
|
||||
Reference in New Issue
Block a user