diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 7dc22b3..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: bug -assignees: phoriah - ---- - -**Are you sure a similar issue hasn't been opened yet?** -Check Open & Closed issues, including pull requests and avoid making a duplicate if so. - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -IMPORTANT: INCLUDE CONSOLE PICTURES ALWAYS (can be opened using F9 or by typing /console in chat, your executor might also have it's own built-in console which you must also show) -If applicable, add screenshots to help explain your problem. - -**Executor (& Emulator if used) Name(s):** -Fluxus v1 - -**Game Link:** -https://www.roblox.com/games/185655149/Welcome-to-Bloxburg - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index be94451..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: "[FEATURE]" -labels: enhancement -assignees: phoriah - ---- - -**Are you sure a similar issue hasn't been opened yet?** -Check Open & Closed issues, including pull requests and avoid making a duplicate if so. - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c13ae24..0000000 --- a/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.vscode -build -docs -moonwave.toml -test.luau \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0ad25db..0000000 --- a/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/README.md b/README.md deleted file mode 100644 index e8be415..0000000 --- a/README.md +++ /dev/null @@ -1,202 +0,0 @@ -

- -

- - -# Loadstring - -```lua -local Params = { - RepoURL = "https://raw.githubusercontent.com/luau/SynSaveInstance/main/", - SSI = "saveinstance", -} -local synsaveinstance = loadstring(game:HttpGet(Params.RepoURL .. Params.SSI .. ".luau", true), Params.SSI)() -local Options = {} -- Documentation here https://luau.github.io/UniversalSynSaveInstance/api/SynSaveInstance -synsaveinstance(Options) -``` - -# Universal Syn Save Instance - -Or shortly USSI, a project aimed at resurrecting saveinstance function from [Synapse X Source 2019] & Other Executor Source leaks :trollface:.
-Reason: Many Executors fail miserably at providing good user experience when it comes to tinkering with saving instances. - -> [!TIP] -> Important part about this saveinstance is that it doesn't modify anything, therefore reduces the amount of detection vectors by a lot.
-> You can also enable the `SafeMode` option to completely bypass any detections and save **ANY** game!

-> If this script is helpful to you, please click `⭐ Star` in the upper right corner of the page to support it, thank you! - -> [!NOTE] -> - You MUST always include the Credit string - `UniversalSynSaveInstance https://discord.gg/wx4ThpAsmw` -> - Do NOT claim you wrote this :accessibility: -> - Do NOT forget to include the [License](https://github.com/luau/SynSaveInstance/blob/main/LICENSE) :finnadie: - -# 💖 Support Us & Our Work - -Buy Me a Coffee at ko-fi.com -[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/M4M1JNH5G "KO-FI") -
-[![ko-fi](https://user-images.githubusercontent.com/95628489/231759262-25661006-b7ca-4967-a79d-2b465cd9575a.png)](https://ko-fi.com/M4M1JNH5G "KO-FI QR-CODE") - -# DISCORD SERVER:
- - **/**
-[Our Official Discord Server!](https://discord.com/invite/wx4ThpAsmw)
- -# TO-DOs - -- [ ] Look into adding support for Binary Format Output (rbxl/rbxm) - - Users can already convert to Binary Format by - 1. Open the File - 2. Click on top left "FILE" text and select "Save to File As" - 3. Make Sure rbxl/rbxm format is selected (not XML!) - 4. Click Save - - .RBXL files are similar to .RBXLX files but are saved in Binary format, which helps reduce the file size. - - ! Check out [Rojo Rbx Dom Binary] & [Roblox Format Specifications Binary] for more documentation about the Binary File Format! - - ! Also see [buffer], [bit32] libraries as well as [pack]/[unpack] from the [string] library for more information on how you can implement something like this! - - ! [Rbx-Binary-Format] -- [x] Add custom decompiler in case executor doesn't have one but has getscriptbytecode (UMF) - - -- [x] Add custom timeout logic for decompiler instead of relying on executor to have one - - Using threads & coroutines. -- [x] Add `continue` where needed -- [ ] Add Documentation similar to [KRNL Docs] or [Synapse X Docs] / [Synapse X Docs Old] -- [ ] Merge SharedStrings and sharedstrings tables -- [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 -- [x] ~~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~~ Relying on CanSave instead - - 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 -- [x] Add Optional tags support -- [ ] Add readbinarystring or readbinarystringpropertyvalue/readbspval/getbspval (elysian) as fallback for gethiddenproperty -- [ ] Add table.clone instead {} in some cases if possible -- [ ] Avoid scanning for default values of properties if those properties won't get serialized anyway (e.g. don't have a Descriptor) -- [x] Add --!native tag just in case -- [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~~ 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) -- [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 - - [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 - - [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 -- [ ] Support for Model files: - - [x] rbxmx (xml) - - [ ] rbxm (binary) -- [x] 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} - - This will allow for more flexibility of saveinstancing -- [x] ~~Remove Useless tables & functions of specialinfo~~ Repurposed -- [x] Implement [Luau Syntax] (important for performance!): - - - [x] Compound Operators - - [x] Avoid using `next`, `ipairs` & `pairs` - - [-] ~~Interpolated strings instead of concat~~ Slower - - [ ] Type-checking (😩🙀) - - [ ] `if-then-else` 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] & [UNC]: - - 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 - - [-] ~~Add Drawing Library support for ShowStatus~~ Can't reliably test if it's working on an executor - * [-] ~~IsolatePlayerGui~~ Use IsolateLocalPlayer instead - * [ ] Callback - * [ ] CopyToClipboard/Clipboard - * [ ] Binary (rbxl/rbxm) -- [ ] 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 -- [x] Isolators must clear -- [-] ~~Store all functions outside that are used during saveinstancing for sake of performance~~ Arguable -- [ ] ~~Remove buffersize, savebuffer & so on for sake of performance by concatenating 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) - - Find default values of BinaryStrings properties (MaximumADHD might have a clue) -- [ ] Add Option to restart saveinstance from the point that it crashed on (perhaps by skipping) -- [ ] Check out [DataType Exceptions] -- [x] Add README Similar to current Synapse -- [x] ~~Ignore all properties of instances that aren't Local or Module Scripts except Name if mode is set to "scripts"~~ IgnorePropertiesOfNotScriptsOnScriptsMode -- [ ] Maybe modes should do more than just determining the list of instances to save, like changing IgnoreDefaultProperties to false if mode is "full" for example -- [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 `Base64EncodedValue` with `UniqueIdentifierForSharedString` & putting `Base64EncodedValue` into SharedStrings container you can achieve this amazing behaviour. This should be only enabled using an optional setting
Only known to work with (probably because both are base64 encoded): - * BinaryString -- [x] Add Lua & Luau versions instead of merged (WARNING: LUAU WILL ALWAYS BE MORE UPDATED THAN LUA VERSION, lua version exists just for the sake of old & bad executors, ask devs of your executors to support luau as its latest & greatest) -- [x] ~~Add Support for multiple Instances to be saved as a model~~ IsModel = true & ExtraInstances -- [x] Do something about devs renaming Services therefore bypassing Ignore lists (CoreGui/CorePackages are not affected) - - LOOK INTO Instance:IsPropertyModified & Instance:ResetPropertyToDefault -- [ ] Custom fallback Decompiler for ModuleScripts using require and then iterating through it, gathering all info about functions using [getupvals/getprotos/getconsts][debug], converting all DataTypes using tostring or Descriptors, and then perhaps converting to JSON. (Make use of op-codes from Dex?) !!! -- [ ] Check out varios Leaked Executors (Especially their Init / Lua scripts) to expand knowledge on the whole subject of saveinstance -- [ ] Fix Player's Characters not being visible (must Refresh MeshId) - - "" 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) -- [ ] Be able to specify which special properties you want saved (to avoid saving all) - -# Acknowledgments -> [!IMPORTANT] -> This document is based largely on the efforts of [@Anaminus] & [@Dekkonot], authors of the [Roblox Format Specifications]. Additional -resources include: -> -> - [Syngp Synapse X Source code 2019][Synapse X Source 2019] for base saveinstance code (extended by [@mblouka] & [@Acrillis]) -> - [Moon/LorekeeperZinnia][@LorekeeperZinnia] for being the original creator of saveinstance that was used in Synapse X, Elysian and many others. As well as being an inspiration for this project. -> - [Rojo Rbx Dom Xml] for being a fallback documentation in case something wasn't clear in the [Roblox Format Specifications] -> - [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) - -\*\*\* View source code of this file for more credits - -[@Acrillis]: https://github.com/Acrillis -[@Anaminus]: https://github.com/Anaminus -[@Dekkonot]: https://github.com/Dekkonot -[@mblouka]: https://github.com/mblouka -[@LorekeeperZinnia]: https://github.com/LorekeeperZinnia -[bit32]: https://create.roblox.com/docs/reference/engine/libraries/bit32 -[buffer]: https://create.roblox.com/docs/reference/engine/libraries/buffer -[pack]: https://create.roblox.com/docs/reference/engine/libraries/string#pack -[unpack]: https://create.roblox.com/docs/reference/engine/libraries/string#unpack -[string]: https://create.roblox.com/docs/reference/engine/libraries/string -[DataType Exceptions]: https://github.com/rojo-rbx/rbx-dom/blob/8ca9250fa5a5ad3756c89e1e111e1aabaf698b27/rbx_reflector/src/cli/generate.rs#L196 -[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 -[Rojo Rbx Dom Xml]: https://github.com/rojo-rbx/rbx-dom/blob/master/docs/xml.md -[Rojo Rbx Dom Binary]: https://github.com/rojo-rbx/rbx-dom/blob/master/docs/binary.md -[Luau Syntax]: https://luau-lang.org/syntax -[Rbx-Binary-Format]: https://github.com/Dekkonot/rbx-binary-format/blob/master/src/writer.lua -[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/ -[Roblox Format Specifications Binary]: https://github.com/RobloxAPI/spec/blob/master/formats/rbxl.md -[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 -[debug]: https://web.archive.org/web/20221021015553/https://docs.synapse.to/reference/debug_lib.html -[Synapse X Docs]: https://web.archive.org/web/20230318113846/https://docs.synapse.to/reference/misc.html -[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/master/rbx_dom_lua/src/customProperties.lua -[UNC]: https://github.com/unified-naming-convention/NamingStandard/commit/613c1956b801ace54ba141dfc60842a16608b54f diff --git a/TODO/PropertyPatches.luau b/TODO/PropertyPatches.luau deleted file mode 100644 index d7a94cf..0000000 --- a/TODO/PropertyPatches.luau +++ /dev/null @@ -1,7 +0,0 @@ --- TODO --- https://github.com/MaximumADHD/Roblox-File-Format/blob/main/Plugins/GenerateApiDump/PropertyPatches.lua --- https://github.com/rojo-rbx/rbx-dom/blob/master/rbx_dom_lua/src/customProperties.lua --- https://github.com/rojo-rbx/rbx-dom/tree/master/patches --- https://github.com/Dekkonot/rbx-instance-serializer/blob/master/src/API.lua#L19 --- Also search Full Dump for "XML", "Internal", "Serialize" --- PropertyPatchesDumper \ No newline at end of file diff --git a/Tools/DataType Dumper/DataType Dumper.py b/Tools/DataType Dumper/DataType Dumper.py deleted file mode 100644 index 9bab591..0000000 --- a/Tools/DataType Dumper/DataType Dumper.py +++ /dev/null @@ -1,71 +0,0 @@ -import requests -import os - - -def array_to_dictionary(table, hybrid_mode=None): - tmp = {} - if hybrid_mode == "adjust": - for key, value in table.items(): - if isinstance(key, int): - tmp[value] = True - elif isinstance(value, dict): - tmp[key] = array_to_dictionary(value, "adjust") - else: - tmp[key] = value - else: - for value in table: - if isinstance(value, str): - tmp[value] = True - return tmp - - -datatypes = [] -datatypes_set = set() # To ensure uniqueness - - -def fetch_api(): - api_dump_url = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - response = requests.get(api_dump_url) - api_classes = response.json()["Classes"] - - global datatypes - global datatypes_set - - for api_class in api_classes: - class_members = api_class["Members"] - - for member in class_members: - if member["MemberType"] == "Property": - ignored = False - - if not ignored: - member_tags = member.get("Tags") - - if member_tags: - member_tags = array_to_dictionary(member_tags) - - serialization = member["Serialization"] - if serialization["CanLoad"] and serialization["CanSave"]: - value_type = member["ValueType"] - value_type_name = value_type["Name"] - value_type_cat = value_type["Category"] - if value_type_cat == "Enum": - value_type_name = "Enum" - if value_type_cat == "Class": - value_type_name = "Class" - if value_type_name not in datatypes_set: - datatypes_set.add(value_type_name) - datatypes.append(value_type_name) - - -try: - fetch_api() - datatypes.sort() - s = "\n".join(datatypes) + "\n" - print(s) - script_dir = os.path.dirname(os.path.realpath(__file__)) - output_file_path = os.path.join(script_dir, "Dump") - with open(output_file_path, "w") as file: - file.write(s) -except Exception as e: - print(f"Error: {e}") diff --git a/Tools/DataType Dumper/Dump b/Tools/DataType Dumper/Dump deleted file mode 100644 index 4bd8131..0000000 --- a/Tools/DataType Dumper/Dump +++ /dev/null @@ -1,35 +0,0 @@ -Axes -BinaryString -BrickColor -CFrame -Class -Color3 -Color3uint8 -ColorSequence -Content -Enum -Faces -Font -NumberRange -NumberSequence -OptionalCoordinateFrame -PhysicalProperties -ProtectedString -QDir -QFont -Ray -Rect -SecurityCapabilities -SharedString -UDim -UDim2 -UniqueId -Vector2 -Vector3 -Vector3int16 -bool -double -float -int -int64 -string diff --git a/Tools/Doesn't Inherit from Class Dumper/Doesn't Inherit from Class Dumper.py b/Tools/Doesn't Inherit from Class Dumper/Doesn't Inherit from Class Dumper.py deleted file mode 100644 index 03cdb27..0000000 --- a/Tools/Doesn't Inherit from Class Dumper/Doesn't Inherit from Class Dumper.py +++ /dev/null @@ -1,73 +0,0 @@ -import requests -import os - - -def array_to_dictionary(table, hybrid_mode=None): - tmp = {} - if hybrid_mode == "adjust": - for key, value in table.items(): - if isinstance(key, int): - tmp[value] = True - elif isinstance(value, dict): - tmp[key] = array_to_dictionary(value, "adjust") - else: - tmp[key] = value - else: - for value in table: - if isinstance(value, str): - tmp[value] = True - return tmp - - -def find_first_table(array): - for item in array: - if isinstance(item, dict): - return item - return None - - -Class = "Instance" - - -def check_superclass_inheritance(class_name, class_list): - current_class = class_list.get(class_name) - while current_class: - if current_class["Name"] == Class: - return True - current_class = class_list.get(current_class["Superclass"]) - return False - - -s = "\n" - - -def fetch_api(): - api_dump_url = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - response = requests.get(api_dump_url) - api_data = response.json() - api_classes = api_data["Classes"] - class_list = {cls["Name"]: cls for cls in api_classes} - - global s - for api_class in api_classes: - class_name = api_class["Name"] - - # Check for superclass inheritance - if not check_superclass_inheritance(class_name, class_list): - s += f"{class_name} does not inherit from {Class}\n" - - prev_len = len(s) - - if len(s) != prev_len: - s += "\n" - - -try: - fetch_api() - print(s) - script_dir = os.path.dirname(os.path.realpath(__file__)) - output_file_path = os.path.join(script_dir, "Dump") - with open(output_file_path, "w") as file: - file.write(s) -except Exception as e: - print(f"Error: {e}") diff --git a/Tools/Doesn't Inherit from Class Dumper/Dump b/Tools/Doesn't Inherit from Class Dumper/Dump deleted file mode 100644 index 8b13789..0000000 --- a/Tools/Doesn't Inherit from Class Dumper/Dump +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Tools/ExpectedClassDumper for NotCreatable/Dump b/Tools/ExpectedClassDumper for NotCreatable/Dump deleted file mode 100644 index c57f921..0000000 --- a/Tools/ExpectedClassDumper for NotCreatable/Dump +++ /dev/null @@ -1,87 +0,0 @@ - -AudioDeviceInput.Player {Player} - -AudioDeviceOutput.Player {Player} - -CoreGui.SelectionImageObject {GuiObject} - -PlayerGui.SelectionImageObject {GuiObject} - -Beam.Attachment0 {Attachment} -Beam.Attachment1 {Attachment} - -RocketPropulsion.Target {BasePart} - -Constraint.Attachment0 {Attachment} -Constraint.Attachment1 {Attachment} - -ControllerManager.ActiveController {ControllerBase} -ControllerManager.ClimbSensor {ControllerSensor} -ControllerManager.GroundSensor {ControllerSensor} -ControllerManager.RootPart {BasePart} - -GuiBase2d.RootLocalizationTable {LocalizationTable} - -GuiObject.NextSelectionDown {GuiObject} -GuiObject.NextSelectionLeft {GuiObject} -GuiObject.NextSelectionRight {GuiObject} -GuiObject.NextSelectionUp {GuiObject} -GuiObject.SelectionImageObject {GuiObject} - -FloorWire.From {BasePart} -FloorWire.To {BasePart} - -PVAdornment.Adornee {PVInstance} - -PartAdornment.Adornee {BasePart} - -SelectionLasso.Humanoid {Humanoid} - -SelectionPartLasso.Part {BasePart} - -GuiService.SelectedObject {GuiObject} - -JointInstance.Part0 {BasePart} -JointInstance.Part1 {BasePart} - -VelocityMotor.Hole {Hole} - -NoCollisionConstraint.Part0 {BasePart} -NoCollisionConstraint.Part1 {BasePart} - -Model.PrimaryPart {BasePart} - -Workspace.CurrentCamera {Camera} - -PathfindingLink.Attachment0 {Attachment} -PathfindingLink.Attachment1 {Attachment} - -Player.Character {Model} -Player.RespawnLocation {SpawnLocation} -Player.Team {Team} - -ProximityPrompt.RootLocalizationTable {LocalizationTable} - -ControllerPartSensor.SensedPart {BasePart} - -Sound.SoundGroup {SoundGroup} - -StyleDerive.StyleSheet {StyleSheet} - -StyleLink.StyleSheet {StyleSheet} - -ChatInputBarConfiguration.TargetTextChannel {TextChannel} - -TextChatMessage.BubbleChatMessageProperties {BubbleChatMessageProperties} -TextChatMessage.TextChannel {TextChannel} -TextChatMessage.TextSource {TextSource} - -Trail.Attachment0 {Attachment} -Trail.Attachment1 {Attachment} - -UIDragDetector.BoundingUI {GuiBase2d} -UIDragDetector.ReferenceUIInstance {GuiObject} - -WeldConstraint.Part0Internal {BasePart} -WeldConstraint.Part1Internal {BasePart} - diff --git a/Tools/ExpectedClassDumper for NotCreatable/Expected Class for NotCreatable Dumper.luau b/Tools/ExpectedClassDumper for NotCreatable/Expected Class for NotCreatable Dumper.luau deleted file mode 100644 index 0bab61b..0000000 --- a/Tools/ExpectedClassDumper for NotCreatable/Expected Class for NotCreatable Dumper.luau +++ /dev/null @@ -1,76 +0,0 @@ --- ! Meant for Studio - -local GlobalSettings, GlobalBasicSettings = settings(), UserSettings() -local service = setmetatable({}, { - __index = function(self, index) - local Service = game:GetService(index) - or GlobalSettings:GetService(index) - or GlobalBasicSettings:GetService(index) - self[index] = Service - return Service - end, -}) - -service.HttpService.HttpEnabled = true - -local function ArrayToDictionary(Table, HybridMode) - local tmp = table.create(#Table) - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - tmp[Key] = true - end - end - - return tmp -end -local s = "\n" -do - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = service.HttpService:GetAsync(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - local ClassName = API_Class.Name - local ClassMembers = API_Class.Members - - for _index_1 = 1, #ClassMembers do - local Member = ClassMembers[_index_1] - local MemberName = Member.Name - local MemberType = Member.MemberType - if MemberType == "Property" then - local Serialization = Member.Serialization - - if Serialization.CanLoad and Serialization.CanSave then - local ValueType = Member.ValueType - if ValueType.Category == "Class" and ValueType.Name ~= "Instance" then - -- warn(ClassName, MemberName, ValueType.Name) - s ..= ClassName .. "." .. MemberName .. " {" .. ValueType.Name .. "}\n" - end - end - end - end - end - end - - local ok, result = pcall(FetchAPI) - - if ok then - print(s) - else - warn(result) - end -end diff --git a/Tools/ExpectedClassDumper for NotCreatable/Expected Class for NotCreatable Dumper.py b/Tools/ExpectedClassDumper for NotCreatable/Expected Class for NotCreatable Dumper.py deleted file mode 100644 index 1cd7424..0000000 --- a/Tools/ExpectedClassDumper for NotCreatable/Expected Class for NotCreatable Dumper.py +++ /dev/null @@ -1,53 +0,0 @@ -import requests, os - - -def array_to_dictionary(table, hybrid_mode=None): - tmp = {} - if hybrid_mode == "adjust": - for key, value in table.items(): - if isinstance(key, int): - tmp[value] = True - elif isinstance(value, dict): - tmp[key] = array_to_dictionary(value, "adjust") - else: - tmp[key] = value - else: - for value in table: - if isinstance(value, str): - tmp[value] = True - return tmp - -s = "\n" - -def fetch_api(): - api_dump_url = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - response = requests.get(api_dump_url) - api_classes = response.json()['Classes'] - - global s - for api_class in api_classes: - class_name = api_class['Name'] - class_members = api_class['Members'] - prevlen = len(s) - for member in class_members: - member_name = member['Name'] - member_type = member['MemberType'] - if member_type == "Property": - serialization = member['Serialization'] - - if serialization['CanLoad'] and serialization['CanSave']: - value_type = member['ValueType'] - if value_type['Category'] == "Class" and value_type['Name'] != "Instance": - s += f"{class_name}.{member_name} {{{value_type['Name']}}}\n" - if prevlen != len(s): - s +="\n" - -try: - fetch_api() - print(s) - script_dir = os.path.dirname(os.path.realpath(__file__)) - output_file_path = os.path.join(script_dir, "Dump") - with open(output_file_path, "w") as file: - file.write(s) -except Exception as e: - print(f"Error: {e}") diff --git a/Tools/Fake NotCreatable Dumper/Dump b/Tools/Fake NotCreatable Dumper/Dump deleted file mode 100644 index 8213794..0000000 --- a/Tools/Fake NotCreatable Dumper/Dump +++ /dev/null @@ -1,34 +0,0 @@ -Varies per executor; Tested on Codex - -Breakpoint {Fake Creatable} - -InternalSyncItem {Fake Creatable} - -ReflectionMetadata {Fake Creatable} - -ReflectionMetadataCallbacks {Fake Creatable} - -ReflectionMetadataClass {Fake Creatable} - -ReflectionMetadataClasses {Fake Creatable} - -ReflectionMetadataEnum {Fake Creatable} - -ReflectionMetadataEnumItem {Fake Creatable} - -ReflectionMetadataEnums {Fake Creatable} - -ReflectionMetadataEvents {Fake Creatable} - -ReflectionMetadataFunctions {Fake Creatable} - -ReflectionMetadataMember {Fake Creatable} - -ReflectionMetadataProperties {Fake Creatable} - -ReflectionMetadataYieldFunctions {Fake Creatable} - -StudioAttachment {Fake Creatable} - -StudioCallout {Fake Creatable} - diff --git a/Tools/Fake NotCreatable Dumper/Fake NotCreatable Dumper.luau b/Tools/Fake NotCreatable Dumper/Fake NotCreatable Dumper.luau deleted file mode 100644 index eb3aaba..0000000 --- a/Tools/Fake NotCreatable Dumper/Fake NotCreatable Dumper.luau +++ /dev/null @@ -1,101 +0,0 @@ --- Dumps all properties that get changed when NotScriptable property is changed. This means they're somehow related to it and/or are in sync with it --- Doesnt get everything! -local service = setmetatable({}, { - __index = function(Self, Name) - local Service = game:GetService(Name) - Self[Name] = Service - return Service - end, -}) -local function ArrayToDictionary(Table, HybridMode) - local tmp = table.create(#Table) - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - tmp[Key] = true - end - end - - return tmp -end -do - -- TODO: More @ https://github.com/Dekkonot/rbx-instance-serializer/blob/23f772f6f78af879a21faa9fea3e6c4f93d1cdee/src/API.lua#L19 - - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = game:HttpGet(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - - local classList = {} - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - - local Class = {} - - local ClassName = API_Class.Name - - local ClassTags = API_Class.Tags - - if ClassTags then - ClassTags = ArrayToDictionary(ClassTags) - end - - -- ClassInfo.Name = ClassName - Class.Tags = ClassTags -- or {} - local Superclass = API_Class.Superclass - Class.Superclass = Superclass - - classList[ClassName] = Class - end - - -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering throguh properties to remove this - - return classList - end - - local api = FetchAPI() - local keys = {} - for classname in api do - table.insert(keys, classname) - end - table.sort(keys) - local s = `Varies per executor; Tested on {identifyexecutor()}\n\n` - for _, ClassName in keys do - local prevsiz = #s - local ClassInfo = api[ClassName] - local Tags = ClassInfo.Tags - local NotCreatable = Tags and Tags.NotCreatable - - local o, r = pcall(Instance.new, ClassName) - - if NotCreatable and o then - s ..= ClassName .. " {Fake NotCreatable}\n" - elseif not (o or NotCreatable) then - s ..= ClassName .. " {Fake Creatable}\n" - print(ClassName, r) - elseif typeof(r) ~= "Instance" then - warn(ClassName, r) - end - - if prevsiz ~= #s then - s ..= "\n" - end - end - warn("Finished") - -- warn(s) - task.wait(0.5) - print(s) - setclipboard(s) - -- writefile("!missing.txt", game:GetService("HttpService"):JSONEncode(t)) -end diff --git a/Tools/Interesting Properties Behavior/Dump b/Tools/Interesting Properties Behavior/Dump deleted file mode 100644 index af1034e..0000000 --- a/Tools/Interesting Properties Behavior/Dump +++ /dev/null @@ -1,604 +0,0 @@ - -Instance.Parent {CanLoad Only} -Instance.archivable {CanLoad Only} -Instance.archivable {Deprecated} - -AdPortal.PortalVersion {CanSave Only} - -AnalyticsService.ApiKey {Deprecated} {CanSave} -AnalyticsService is NotCreatable but AnalyticsService.ApiKey has a default value: - -AnimationTrack.TimePosition {CanLoad Only} - -AnnotationsService is NotCreatable but AnnotationsService.Hovered has a default value: __api_dump_no_string_value__ - -AssetDeliveryProxy is NotCreatable but AssetDeliveryProxy.Interface has a default value: - -AssetPatchSettings is NotCreatable but AssetPatchSettings.ContentId has a default value: - -Attachment.Axis {CanLoad Only} -Attachment.SecondaryAxis {CanLoad Only} -Attachment.WorldAxis {CanLoad Only} -Attachment.WorldCFrame {CanLoad Only} -Attachment.WorldSecondaryAxis {CanLoad Only} - -Bone.Transform {CanLoad Only} - -AudioAnalyzer.PeakLevel {CanSave Only} -AudioAnalyzer.RmsLevel {CanSave Only} - -AudioDeviceInput.IsReady {CanSave Only} - -AudioEqualizer.Editor {CanLoad Only} - -AudioFilter.Editor {CanLoad Only} - -AudioPlayer.Asset {CanLoad Only} -AudioPlayer.IsReady {CanSave Only} -AudioPlayer.TimeLength {CanSave Only} - -AudioSearchParams.AudioSubtype {CanLoad Only} -AudioSearchParams.AudioSubtype {Deprecated} - -AvatarChatService is NotCreatable but AvatarChatService.ClientFeatures has a default value: 0 - -Avatar2DGenerationJob is NotCreatable but Avatar2DGenerationJob.Result has a default value: - -Avatar3DGenerationJob is NotCreatable but Avatar3DGenerationJob.Result has a default value: - -BaseImportData.Id {CanSave Only} - -MaterialImportData.IsPbr {CanSave Only} - -MeshImportData.CageManifold {CanSave Only} -MeshImportData.CageMeshNotIntersected {CanSave Only} -MeshImportData.CageNoOverlappingVertices {CanSave Only} -MeshImportData.CageUVMatched {CanSave Only} -MeshImportData.Dimensions {CanSave Only} -MeshImportData.MeshNoHoleDetected {CanSave Only} -MeshImportData.NoIrrelevantCageModified {CanSave Only} -MeshImportData.NoOuterCageFarExtendedFromMesh {CanSave Only} -MeshImportData.PolygonCount {CanSave Only} - -RootImportData.FileDimensions {CanSave Only} -RootImportData.PolygonCount {CanSave Only} - -CoreGui.Version {CanSave Only} - -PlayerGui is NotCreatable but PlayerGui.CurrentScreenOrientation has a default value: LandscapeLeft - -StarterGui.ResetPlayerGuiOnSpawn {Deprecated} {CanSave} - -BodyAngularVelocity.angularvelocity {CanLoad Only} -BodyAngularVelocity.angularvelocity {Deprecated} -BodyAngularVelocity.maxTorque {CanLoad Only} -BodyAngularVelocity.maxTorque {Deprecated} - -BodyForce.force {CanLoad Only} -BodyForce.force {Deprecated} - -BodyGyro.cframe {CanLoad Only} -BodyGyro.cframe {Deprecated} -BodyGyro.maxTorque {CanLoad Only} -BodyGyro.maxTorque {Deprecated} - -BodyPosition.maxForce {CanLoad Only} -BodyPosition.maxForce {Deprecated} -BodyPosition.position {CanLoad Only} -BodyPosition.position {Deprecated} - -BodyThrust.force {CanLoad Only} -BodyThrust.force {Deprecated} -BodyThrust.location {CanLoad Only} -BodyThrust.location {Deprecated} - -BodyVelocity.maxForce {CanLoad Only} -BodyVelocity.maxForce {Deprecated} -BodyVelocity.velocity {CanLoad Only} -BodyVelocity.velocity {Deprecated} - -Camera.CoordinateFrame {CanLoad Only} -Camera.CoordinateFrame {Deprecated} -Camera.NearPlaneZ {CanSave Only} -Camera.ViewportSize {CanSave Only} -Camera.focus {CanLoad Only} -Camera.focus {Deprecated} - -BodyColors.HeadColor {CanLoad Only} -BodyColors.LeftArmColor {CanLoad Only} -BodyColors.LeftLegColor {CanLoad Only} -BodyColors.RightArmColor {CanLoad Only} -BodyColors.RightLegColor {CanLoad Only} -BodyColors.TorsoColor {CanLoad Only} - -Clothing.Outfit1 {CanLoad Only} -Clothing.Outfit2 {CanLoad Only} - -Chat is NotCreatable but Chat.BubbleChatEnabled has a default value: false - -DragDetector.Axis {CanLoad Only} -DragDetector.SecondaryAxis {CanLoad Only} -DragDetector.WorldAxis {CanLoad Only} -DragDetector.WorldSecondaryAxis {CanLoad Only} - -Collaborator is NotCreatable but Collaborator.CFrame has a default value: __api_dump_no_string_value__ - -CommandInstance.Checked {CanLoad Only} -CommandInstance.DisplayName {CanLoad Only} -CommandInstance.Enabled {CanLoad Only} -CommandInstance.Icon {CanLoad Only} -CommandInstance.StatusTip {CanLoad Only} - -ConnectivityService.NetworkStatus {CanSave Only} -ConnectivityService is NotCreatable but ConnectivityService.NetworkStatus has a default value: Unknown - -HingeConstraint.CurrentAngle {CanSave Only} -HingeConstraint.SoftlockServoUponReachingTarget {Deprecated} {CanSave} - -RodConstraint.CurrentDistance {CanSave Only} - -RopeConstraint.CurrentDistance {CanSave Only} - -SlidingBallConstraint.CurrentPosition {CanSave Only} -SlidingBallConstraint.SoftlockServoUponReachingTarget {Deprecated} {CanSave} - -CylindricalConstraint.CurrentAngle {CanSave Only} -CylindricalConstraint.SoftlockAngularServoUponReachingTarget {Deprecated} {CanSave} -CylindricalConstraint.WorldRotationAxis {CanSave Only} - -SpringConstraint.CurrentLength {CanSave Only} - -TorsionSpringConstraint.CurrentAngle {CanSave Only} -TorsionSpringConstraint.LimitEnabled {Deprecated} {CanSave} - -ContentProvider.BaseUrl {CanSave Only} -ContentProvider.RequestQueueSize {CanSave Only} - -DataStoreInfo.CreatedTime {CanSave Only} -DataStoreInfo.DataStoreName {CanSave Only} -DataStoreInfo.UpdatedTime {CanSave Only} - -DataStoreKey.KeyName {CanSave Only} - -DataStoreKeyInfo.CreatedTime {CanSave Only} -DataStoreKeyInfo.UpdatedTime {CanSave Only} -DataStoreKeyInfo.Version {CanSave Only} - -DataStoreObjectVersionInfo.CreatedTime {CanSave Only} -DataStoreObjectVersionInfo.IsDeleted {CanSave Only} -DataStoreObjectVersionInfo.Version {CanSave Only} - -DataStoreService.LegacyNamingScheme {Deprecated} {CanSave} -DataStoreService is NotCreatable but DataStoreService.AutomaticRetry has a default value: true - -Debris.MaxItems {Deprecated} {CanSave} - -DebugSettings.DataModel {CanSave Only} -DebugSettings.InstanceCount {CanSave Only} -DebugSettings.JobCount {CanSave Only} -DebugSettings.PlayerCount {CanSave Only} -DebugSettings.RobloxVersion {CanSave Only} - -DebuggerBreakpoint is NotCreatable but DebuggerBreakpoint.Condition has a default value: __api_dump_skipped_class__ - -DebuggerManager.DebuggingEnabled {CanSave Only} - -DraggerService is NotCreatable but DraggerService.AlignDraggedObjects has a default value: __api_dump_skipped_class__ - -ExperienceStateCaptureService is NotCreatable but ExperienceStateCaptureService.HiddenSelectionEnabled has a default value: false - -FaceAnimatorService is NotCreatable but FaceAnimatorService.AudioAnimationEnabled has a default value: true - -Decal.Shiny {CanLoad Only} -Decal.Shiny {Deprecated} -Decal.Specular {CanLoad Only} -Decal.Specular {Deprecated} - -FacialAnimationRecordingService is NotCreatable but FacialAnimationRecordingService.BiometricDataConsent has a default value: __api_dump_skipped_class__ - -FacialAnimationStreamingServiceV2 is NotCreatable but FacialAnimationStreamingServiceV2.ServiceState has a default value: 0 - -Fire.Heat {CanLoad Only} -Fire.Size {CanLoad Only} -Fire.size {CanLoad Only} -Fire.size {Deprecated} - -FloatCurve.Length {CanSave Only} - -FunctionalTest.AllowSleep {CanLoad Only} -FunctionalTest.Is30FpsThrottleEnabled {CanLoad Only} -FunctionalTest.PhysicsEnvironmentalThrottle {CanLoad Only} -FunctionalTest.Timeout {CanLoad Only} - -GameSettings.VideoRecording {CanLoad Only} - -GamepadService is NotCreatable but GamepadService.GamepadCursorEnabled has a default value: false - -GuiBase2d.Localize {CanLoad Only} -GuiBase2d.Localize {Deprecated} - -GuiObject.BackgroundColor {CanLoad Only} -GuiObject.BackgroundColor {Deprecated} -GuiObject.BorderColor {CanLoad Only} -GuiObject.BorderColor {Deprecated} -GuiObject.Draggable {Deprecated} {CanSave} -GuiObject.GuiState {CanSave Only} -GuiObject.Transparency {CanLoad Only} - -TextButton.Font {CanLoad Only} -TextButton.FontSize {CanLoad Only} -TextButton.FontSize {Deprecated} -TextButton.LocalizedText {CanSave Only} -TextButton.OpenTypeFeaturesError {CanSave Only} -TextButton.TextColor {CanLoad Only} -TextButton.TextColor {Deprecated} -TextButton.TextWrap {CanLoad Only} -TextButton.TextWrap {Deprecated} - -TextLabel.Font {CanLoad Only} -TextLabel.FontSize {CanLoad Only} -TextLabel.FontSize {Deprecated} -TextLabel.LocalizedText {CanSave Only} -TextLabel.OpenTypeFeaturesError {CanSave Only} -TextLabel.TextColor {CanLoad Only} -TextLabel.TextColor {Deprecated} -TextLabel.TextWrap {CanLoad Only} -TextLabel.TextWrap {Deprecated} - -ScrollingFrame.AbsoluteCanvasSize {CanSave Only} -ScrollingFrame.AbsoluteWindowSize {CanSave Only} - -TextBox.Font {CanLoad Only} -TextBox.FontSize {CanLoad Only} -TextBox.FontSize {Deprecated} -TextBox.OpenTypeFeaturesError {CanSave Only} -TextBox.TextColor {CanLoad Only} -TextBox.TextColor {Deprecated} -TextBox.TextWrap {CanLoad Only} -TextBox.TextWrap {Deprecated} - -ViewportFrame.CurrentCamera {CanLoad Only} - -DockWidgetPluginGui.HostWidgetWasRestored {CanSave Only} -DockWidgetPluginGui is NotCreatable but DockWidgetPluginGui.HostWidgetWasRestored has a default value: false - -ScreenGui.IgnoreGuiInset {CanLoad Only} - -GuiBase3d.Color {CanLoad Only} -GuiBase3d.Color {Deprecated} - -SelectionBox.SurfaceColor {CanLoad Only} -SelectionBox.SurfaceColor {Deprecated} - -SelectionSphere.SurfaceColor {CanLoad Only} -SelectionSphere.SurfaceColor {Deprecated} - -Path2D.SelectedControlPoint {CanLoad Only} -Path2D.SelectedControlPointData {CanLoad Only} - -GuiService.MenuIsOpen {CanSave Only} - -HttpService is NotCreatable but HttpService.HttpEnabled has a default value: false - -Humanoid.CollisionType {Deprecated} {CanSave} -Humanoid.Health {CanLoad Only} -Humanoid.Jump {CanLoad Only} -Humanoid.LeftLeg {CanLoad Only} -Humanoid.LeftLeg {Deprecated} -Humanoid.RightLeg {CanLoad Only} -Humanoid.RightLeg {Deprecated} -Humanoid.RootPart {CanSave Only} -Humanoid.Torso {CanLoad Only} -Humanoid.Torso {Deprecated} -Humanoid.maxHealth {CanLoad Only} -Humanoid.maxHealth {Deprecated} - -IncrementalPatchBuilder is NotCreatable but IncrementalPatchBuilder.AddPathsToBundle has a default value: false - -InputObject is NotCreatable but InputObject.Delta has a default value: 0, 0, 0 - -InsertService.AllowInsertFreeModels {Deprecated} {CanSave} -InsertService is NotCreatable but InsertService.AllowClientInsertModels has a default value: false - -InternalSyncItem.Target {CanLoad Only} - -Motor.CurrentAngle {CanLoad Only} - -Lighting.ClockTime {CanLoad Only} -Lighting.Outlines {Deprecated} {CanSave} -Lighting.ShadowColor {CanLoad Only} -Lighting.ShadowColor {Deprecated} -Lighting is NotCreatable but Lighting.Ambient has a default value: 0.5, 0.5, 0.5 - -LiveScriptingService is NotCreatable but LiveScriptingService.ServerLiveEditingMode has a default value: Uninitialized - -LocalizationService is NotCreatable but LocalizationService.ForcePlayModeGameLocaleId has a default value: - -LocalizationTable.DevelopmentLanguage {CanLoad Only} -LocalizationTable.DevelopmentLanguage {Deprecated} - -LodDataEntity is NotCreatable but LodDataEntity.EntityData has a default value: __api_dump_no_string_value__ - -BaseScript.LinkedSource {Deprecated} {CanSave} - -ModuleScript.LinkedSource {Deprecated} {CanSave} - -MarkerCurve.Length {CanSave Only} - -MaterialService is NotCreatable but MaterialService.AsphaltName has a default value: Asphalt - -MetaBreakpoint.Id {CanSave Only} -MetaBreakpoint.IsLogpoint {CanSave Only} -MetaBreakpoint.Valid {CanSave Only} -MetaBreakpoint is NotCreatable but MetaBreakpoint.Condition has a default value: - -MetaBreakpointContext is NotCreatable but MetaBreakpointContext.ContextDataInternal has a default value: 0 1 2 - -Mouse.Hit {CanSave Only} -Mouse.Origin {CanSave Only} -Mouse.Target {CanSave Only} -Mouse.TargetSurface {CanSave Only} -Mouse.UnitRay {CanSave Only} -Mouse.ViewSizeX {CanSave Only} -Mouse.ViewSizeY {CanSave Only} -Mouse.X {CanSave Only} -Mouse.Y {CanSave Only} - -NotificationService.SelectedTheme {CanLoad Only} - -BasePart.AssemblyAngularVelocity {CanLoad Only} -BasePart.AssemblyLinearVelocity {CanLoad Only} -BasePart.BackParamA {Deprecated} {CanSave} -BasePart.BackParamB {Deprecated} {CanSave} -BasePart.BackSurfaceInput {Deprecated} {CanSave} -BasePart.BottomParamA {Deprecated} {CanSave} -BasePart.BottomParamB {Deprecated} {CanSave} -BasePart.BottomSurfaceInput {Deprecated} {CanSave} -BasePart.BrickColor {CanLoad Only} -BasePart.CollisionGroupId {Deprecated} {CanSave} -BasePart.Color {CanLoad Only} -BasePart.Elasticity {CanLoad Only} -BasePart.Elasticity {Deprecated} -BasePart.Friction {CanLoad Only} -BasePart.Friction {Deprecated} -BasePart.FrontParamA {Deprecated} {CanSave} -BasePart.FrontParamB {Deprecated} {CanSave} -BasePart.FrontSurfaceInput {Deprecated} {CanSave} -BasePart.LeftParamA {Deprecated} {CanSave} -BasePart.LeftParamB {Deprecated} {CanSave} -BasePart.LeftSurfaceInput {Deprecated} {CanSave} -BasePart.RightParamA {Deprecated} {CanSave} -BasePart.RightParamB {Deprecated} {CanSave} -BasePart.RightSurfaceInput {Deprecated} {CanSave} -BasePart.RotVelocity {Deprecated} {CanSave} -BasePart.Rotation {CanLoad Only} -BasePart.Size {CanLoad Only} -BasePart.TopParamA {Deprecated} {CanSave} -BasePart.TopParamB {Deprecated} {CanSave} -BasePart.TopSurfaceInput {Deprecated} {CanSave} -BasePart.Velocity {Deprecated} {CanSave} -BasePart.brickColor {CanLoad Only} -BasePart.brickColor {Deprecated} -BasePart.siz {CanLoad Only} - -FormFactorPart.FormFactor {CanLoad Only} -FormFactorPart.FormFactor {Deprecated} -FormFactorPart.formFactor {CanLoad Only} -FormFactorPart.formFactor {Deprecated} - -Part.Shape {CanLoad Only} -Part.shap {CanLoad Only} - -Terrain.ClusterGrid {CanLoad Only} -Terrain.ClusterGridV2 {CanLoad Only} -Terrain.ClusterGridV3 {CanLoad Only} -Terrain is NotCreatable but Terrain.AcquisitionMethod has a default value: None - -TriangleMeshPart.CollisionFidelity {CanLoad Only} -TriangleMeshPart.FluidFidelity {CanLoad Only} - -MeshPart.MeshID {CanLoad Only} -MeshPart.MeshID {Deprecated} - -PartOperation.TriangleCount {CanSave Only} - -TrussPart.Style {CanLoad Only} - -HopperBin.Command {CanLoad Only} -HopperBin.TextureName {CanLoad Only} - -Workspace.CollisionGroups {CanLoad Only} -Workspace.StreamingPauseMode {CanLoad Only} - -PackageLink.PackageId {CanSave Only} -PackageLink.VersionNumber {CanLoad Only} -PackageLink is NotCreatable but PackageLink.AutoUpdate has a default value: false - -DataStoreKeyPages.Cursor {CanSave Only} - -DataStoreListingPages.Cursor {CanSave Only} - -ParticleEmitter.VelocitySpread {CanLoad Only} -ParticleEmitter.VelocitySpread {Deprecated} - -PatchMapping is NotCreatable but PatchMapping.FlattenTree has a default value: false - -Path.Status {CanSave Only} - -PathfindingService.EmptyCutoff {CanLoad Only} -PathfindingService.EmptyCutoff {Deprecated} -PathfindingService is NotCreatable but PathfindingService.EmptyCutoff has a default value: 0 - -Player.CharacterAppearance {Deprecated} {CanSave} -Player.FollowUserId {CanSave Only} - -PlayerEmulatorService is NotCreatable but PlayerEmulatorService.CustomPoliciesEnabled has a default value: false - -PluginMenu.Icon {CanLoad Only} -PluginMenu.Title {CanLoad Only} - -PluginToolbarButton.ClickableWhenViewportHidden {CanLoad Only} -PluginToolbarButton.Enabled {CanLoad Only} -PluginToolbarButton.Icon {CanLoad Only} - -PolicyService is NotCreatable but PolicyService.IsLuobuServer has a default value: Unknown - -Pose.MaskWeight {CanLoad Only} -Pose.MaskWeight {Deprecated} - -RenderSettings.EnableFRM {CanLoad Only} - -RotationCurve.Length {CanSave Only} - -SafetyService is NotCreatable but SafetyService.IsCaptureModeForReport has a default value: false - -ScreenshotHud.ExperienceNameOverlayEnabled {Deprecated} {CanSave} -ScreenshotHud.OverlayFont {Deprecated} {CanSave} -ScreenshotHud.UsernameOverlayEnabled {Deprecated} {CanSave} - -SyncScriptBuilder.RawBytecode {Deprecated} {CanSave} -SyncScriptBuilder is NotCreatable but SyncScriptBuilder.CompileTarget has a default value: CoreScript - -ScriptContext is NotCreatable but ScriptContext.ScriptsDisabled has a default value: false - -ScriptDebugger is NotCreatable but ScriptDebugger.CoreScriptIdentifier has a default value: - -Selection.SelectionBoxThickness {CanSave Only} -Selection.SelectionLineThickness {CanSave Only} - -AtmosphereSensor.AirDensity {CanSave Only} -AtmosphereSensor.RelativeWindVelocity {CanSave Only} - -ServerScriptService is NotCreatable but ServerScriptService.LoadStringEnabled has a default value: false - -DataModel.PrivateServerId {CanSave Only} -DataModel.PrivateServerOwnerId {CanSave Only} -DataModel.VIPServerId {CanSave Only} -DataModel.VIPServerOwnerId {CanSave Only} - -ServiceVisibilityService is NotCreatable but ServiceVisibilityService.HiddenServices has a default value: - -Smoke.Opacity {CanLoad Only} -Smoke.RiseVelocity {CanLoad Only} -Smoke.Size {CanLoad Only} - -Sound.EmitterSize {Deprecated} {CanSave} -Sound.MinDistance {CanLoad Only} -Sound.MinDistance {Deprecated} -Sound.Pitch {CanLoad Only} -Sound.Pitch {Deprecated} -Sound.xmlRead_MaxDistance_3 {Deprecated} {CanSave} -Sound.xmlRead_MinDistance_3 {CanLoad Only} -Sound.xmlRead_MinDistance_3 {Deprecated} - -ChannelSelectorSoundEffect is NotCreatable but ChannelSelectorSoundEffect.Channel has a default value: 1 - -SoundService is NotCreatable but SoundService.AmbientReverb has a default value: NoReverb - -Sparkles.Color {CanLoad Only} - -StarterPlayer is NotCreatable but StarterPlayer.AllowCustomAnimations has a default value: true - -Studio.UI Theme {CanSave Only} - -StudioData is NotCreatable but StudioData.EnableScriptCollabByDefaultOnLoad has a default value: false - -StudioDeviceEmulatorService is NotCreatable but StudioDeviceEmulatorService.HasMultiTouchStarted has a default value: __api_dump_skipped_class__ - -StudioPublishService is NotCreatable but StudioPublishService.PublishLocked has a default value: false - -StudioService.UseLocalSpace {CanLoad Only} -StudioService is NotCreatable but StudioService.ActiveScript has a default value: __api_dump_skipped_class__ - -TaskScheduler.SchedulerDutyCycle {CanSave Only} -TaskScheduler.SchedulerRate {CanSave Only} -TaskScheduler.ThreadPoolSize {CanSave Only} - -Team.AutoColorCharacters {CanLoad Only} -Team.AutoColorCharacters {Deprecated} -Team.Score {CanLoad Only} -Team.Score {Deprecated} - -TeamCreateData is NotCreatable but TeamCreateData.InitialCameraCFrame has a default value: __api_dump_no_string_value__ - -TeleportAsyncResult.PrivateServerId {CanSave Only} -TeleportAsyncResult.ReservedServerAccessCode {CanSave Only} - -TeleportService.CustomizedTeleportUI {CanLoad Only} -TeleportService.CustomizedTeleportUI {Deprecated} - -TerrainRegion.GridV3 {CanLoad Only} - -TestService.ErrorCount {CanSave Only} -TestService.Is30FpsThrottleEnabled {CanLoad Only} -TestService.IsPhysicsEnvironmentalThrottled {CanLoad Only} -TestService.TestCount {CanSave Only} -TestService.WarnCount {CanSave Only} - -BubbleChatConfiguration is NotCreatable but BubbleChatConfiguration.AdorneeName has a default value: HumanoidRootPart - -ChannelTabsConfiguration is NotCreatable but ChannelTabsConfiguration.AbsolutePosition has a default value: 0, 0 - -ChatInputBarConfiguration is NotCreatable but ChatInputBarConfiguration.AbsolutePosition has a default value: 0, 0 - -ChatWindowConfiguration is NotCreatable but ChatWindowConfiguration.AbsolutePosition has a default value: 0, 0 - -TextChatMessage is NotCreatable but TextChatMessage.BubbleChatMessageProperties has a default value: __api_dump_no_string_value__ - -TextChatService is NotCreatable but TextChatService.ChatTranslationEnabled has a default value: true - -TextSource is NotCreatable but TextSource.CanSend has a default value: true - -Translator.LocaleId {CanSave Only} - -TweenBase.PlaybackState {CanSave Only} - -UIListLayout.HorizontalPadding {CanLoad Only} -UIListLayout.VerticalPadding {CanLoad Only} - -UIPageLayout.CurrentPage {CanSave Only} - -UnvalidatedAssetService is NotCreatable but UnvalidatedAssetService.CachedData has a default value: {"lastSaveTime":0,"lastKnownPublishRequest":0,"users":[]} - -UserGameSettings.IsUsingCameraYInverted {CanSave Only} -UserGameSettings.IsUsingGamepadCameraSensitivity {CanSave Only} -UserGameSettings.MicroProfilerWebServerIP {CanSave Only} -UserGameSettings.MicroProfilerWebServerPort {CanSave Only} - -UserInputService.AccelerometerEnabled {CanSave Only} -UserInputService.GamepadEnabled {CanSave Only} -UserInputService.GyroscopeEnabled {CanSave Only} -UserInputService.KeyboardEnabled {CanSave Only} -UserInputService.LegacyInputEventsEnabled {Deprecated} {CanSave} -UserInputService.MouseDeltaSensitivity {CanLoad Only} -UserInputService.MouseEnabled {CanSave Only} -UserInputService.TouchEnabled {CanSave Only} -UserInputService.VREnabled {CanSave Only} -UserInputService is NotCreatable but UserInputService.AccelerometerEnabled has a default value: __api_dump_skipped_class__ - -VRService is NotCreatable but VRService.AutomaticScaling has a default value: World - -DoubleConstrainedValue.ConstrainedValue {CanLoad Only} -DoubleConstrainedValue.Value {CanLoad Only} - -IntConstrainedValue.ConstrainedValue {CanLoad Only} -IntConstrainedValue.Value {CanLoad Only} - -VersionControlService is NotCreatable but VersionControlService.ScriptCollabEnabled has a default value: false - -VideoCaptureService is NotCreatable but VideoCaptureService.Active has a default value: false - -VideoDeviceInput.IsReady {CanSave Only} - -VoiceChatInternal is NotCreatable but VoiceChatInternal.VoiceChatState has a default value: Idle - -VoiceChatService is NotCreatable but VoiceChatService.EnableDefaultVoice has a default value: true - -WeldConstraint.Active {CanSave Only} -WeldConstraint.CFrame1 {CanLoad Only} -WeldConstraint.Enabled {CanLoad Only} -WeldConstraint.Part0 {CanLoad Only} -WeldConstraint.Part1 {CanLoad Only} - -Wire.Connected {CanSave Only} - diff --git a/Tools/Interesting Properties Behavior/Interesting Properties Behavior.luau b/Tools/Interesting Properties Behavior/Interesting Properties Behavior.luau deleted file mode 100644 index 2244c2c..0000000 --- a/Tools/Interesting Properties Behavior/Interesting Properties Behavior.luau +++ /dev/null @@ -1,132 +0,0 @@ -local GlobalSettings, GlobalBasicSettings = settings(), UserSettings() -local service = setmetatable({}, { - __index = function(Self, Name) - local Service = game:GetService(Name) or GlobalSettings:GetService(Name) or GlobalBasicSettings:GetService(Name) - Self[Name] = Service - return Service - end, -}) -service.HttpService.HttpEnabled = true -local function ArrayToDictionary(Table, HybridMode) - local tmp = table.create(#Table) - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - tmp[Key] = true - end - end - - return tmp -end -local s = "\n" - -do - -- TODO: More @ https://github.com/Dekkonot/rbx-instance-serializer/blob/23f772f6f78af879a21faa9fea3e6c4f93d1cdee/src/API.lua#L19 - - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = service.HttpService:GetAsync(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - - local classList = {} - - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - local ClassMembers = API_Class.Members - - local Class = {} - - local ClassName = API_Class.Name - - local ClassTags = API_Class.Tags - - if ClassTags then - ClassTags = ArrayToDictionary(ClassTags) - end - - -- ClassInfo.Name = ClassName - Class.Tags = ClassTags -- or {} - Class.Superclass = API_Class.Superclass - - local ClassProperties = {} - local prevsiz = #s - for _index_1 = 1, #ClassMembers do - local Member = ClassMembers[_index_1] - if Member.MemberType == "Property" then - local PropertyName = Member.Name - - -- ? We avoid this as some Instances like services may have this property locked and thus make file unable to open and it's not even used by Roblox anyways as Parent-Child relationship is done by embedding/nesting - - local Ignored = false - if not (Ignored and Ignored[PropertyName]) then - local MemberTags = Member.Tags - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - - Special = MemberTags.NotScriptable - end - local Serialization = Member.Serialization - if Serialization.CanLoad then - if Serialization.CanSave == false then - s ..= ClassName .. "." .. PropertyName .. " {CanLoad Only}" .. "\n" - end - if MemberTags and MemberTags.Deprecated then - s ..= ClassName .. "." .. PropertyName .. " {Deprecated} " .. (Serialization.CanSave and " {CanSave}" or "") .. "\n" - end - elseif Serialization.CanSave then - s ..= ClassName .. "." .. PropertyName .. " {CanSave Only}\n" - end - local ValueType = Member.ValueType - - local Property = { - Name = PropertyName, - Category = ValueType.Category, - Default = Member.Default, - -- Tags = MemberTags, - ValueType = ValueType.Name, - } - - if Special then - Property.Special = true - end - - ClassProperties[PropertyName] = Property - end - end - end - - Class.Properties = ClassProperties - if #s ~= prevsiz then - s ..= "\n" - end - classList[ClassName] = Class - end - - -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering throguh properties to remove this - - return classList - end - - local ok, result = pcall(FetchAPI) - print(s) - if ok then - ClassList = result - else - warn(result) - return - end -end diff --git a/Tools/Interesting Properties Behavior/Interesting Properties Behavior.py b/Tools/Interesting Properties Behavior/Interesting Properties Behavior.py deleted file mode 100644 index ed22d8c..0000000 --- a/Tools/Interesting Properties Behavior/Interesting Properties Behavior.py +++ /dev/null @@ -1,103 +0,0 @@ -import requests -import os - - -def array_to_dictionary(table, hybrid_mode=None): - tmp = {} - if hybrid_mode == "adjust": - for key, value in table.items(): - if isinstance(key, int): - tmp[value] = True - elif isinstance(value, dict): - tmp[key] = array_to_dictionary(value, "adjust") - else: - tmp[key] = value - else: - for value in table: - if isinstance(value, str): - tmp[value] = True - return tmp - - -s = "\n" - - -def fetch_api(): - api_dump_url = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - response = requests.get(api_dump_url) - api_classes = response.json()["Classes"] - - global s - class_list = {} - - for api_class in api_classes: - class_name = api_class["Name"] - class_members = api_class["Members"] - class_tags = api_class.get("Tags") - - if class_tags: - # print(class_tags) - if len(class_tags) == 0: - print("tagsempty") - class_tags = array_to_dictionary(class_tags) - else: - print(class_name, "notags") - - class_info = { - "Tags": class_tags, - "Superclass": api_class["Superclass"], - "Properties": {}, - } - - prev_len = len(s) - - for member in class_members: - if member["MemberType"] == "Property": - property_name = member["Name"] - ignored = False - - if not ignored: - member_tags = member.get("Tags") - - if member_tags: - member_tags = array_to_dictionary(member_tags) - - serialization = member["Serialization"] - if serialization["CanLoad"]: - if not serialization["CanSave"]: - s += f"{class_name}.{property_name} {{CanLoad Only}}\n" - if member_tags and member_tags.get("Deprecated"): - s += f"{class_name}.{property_name} {{Deprecated}} { ' {CanSave}' if serialization['CanSave'] else ''}\n" - elif serialization["CanSave"]: - s += f"{class_name}.{property_name} {{CanSave Only}}\n" - - class_info["Properties"][property_name] = { - "Serialization": serialization, - "Tags": member_tags, - "Default": member.get("Default"), - } - - if class_tags and class_tags.get("NotCreatable"): - for property_name, property_info in class_info["Properties"].items(): - value = property_info.get("Default") - if value != "__api_dump_class_not_creatable__": - s += f"{class_name} is NotCreatable but {class_name}.{property_name} has a default value: {value}\n" - break - - if len(s) != prev_len: - s += "\n" - - class_list[class_name] = class_info - - return class_list - - -try: - fetch_api() - print(s) - script_dir = os.path.dirname(os.path.realpath(__file__)) - output_file_path = os.path.join(script_dir, "Dump") - with open(output_file_path, "w") as file: - file.write(s) -except Exception as e: - print(f"Error: {e}") diff --git a/Tools/NilInstance Error Dumper/Dump b/Tools/NilInstance Error Dumper/Dump deleted file mode 100644 index 6c631ca..0000000 --- a/Tools/NilInstance Error Dumper/Dump +++ /dev/null @@ -1,11 +0,0 @@ -! InternalSyncItem: Internal Permission is required for this feature. -AdPortal: AdPortal must be parented to a Part. -Animator: Animator has to be placed under Humanoid or AnimationController! -Attachment: Attachments must be parented to a BasePart or another Attachment. -BaseWrap: BaseWrap must be parented to a MeshPart. -Bone: Bones must be parented to a BasePart or another Bone. -DebuggerWatch: DebuggerWatch must be a child of ScriptDebugger -Player: Parent of Player can not be changed -PluginAction: Parent of PluginAction must be Plugin or PluginMenu that created it! -WrapLayer: BaseWrap must be parented to a MeshPart. -WrapTarget: BaseWrap must be parented to a MeshPart. diff --git a/Tools/NilInstance Error Dumper/NilInstance Error Dumper.luau b/Tools/NilInstance Error Dumper/NilInstance Error Dumper.luau deleted file mode 100644 index 2b155b0..0000000 --- a/Tools/NilInstance Error Dumper/NilInstance Error Dumper.luau +++ /dev/null @@ -1,165 +0,0 @@ -local ClassList -local GlobalSettings, GlobalBasicSettings = settings(), UserSettings() -local service = setmetatable({}, { - __index = function(Self, Name) - local Service = game:GetService(Name) or GlobalSettings:GetService(Name) or GlobalBasicSettings:GetService(Name) - Self[Name] = Service - return Service - end, -}) --- service.HttpService.HttpEnabled = true -local function ArrayToDictionary(Table, HybridMode) - local tmp = table.create(#Table) - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - tmp[Key] = true - end - end - - return tmp -end - -local function Find(String, Pattern) - return string.find(String, Pattern, nil, true) -end - -do - -- TODO: More @ https://github.com/Dekkonot/rbx-instance-serializer/blob/23f772f6f78af879a21faa9fea3e6c4f93d1cdee/src/API.lua#L19 - - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = game:HttpGet(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - - local classList = {} - - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - local ClassMembers = API_Class.Members - - local Class = {} - - local ClassName = API_Class.Name - - local ClassTags = API_Class.Tags - - if ClassTags then - ClassTags = ArrayToDictionary(ClassTags) - end - - -- ClassInfo.Name = ClassName - Class.Tags = ClassTags -- or {} - Class.Superclass = API_Class.Superclass - - local ClassProperties = {} - - for _index_1 = 1, #ClassMembers do - local Member = ClassMembers[_index_1] - if Member.MemberType == "Property" then - local PropertyName = Member.Name - - -- ? We avoid this as some Instances like services may have this property locked and thus make file unable to open and it's not even used by Roblox anyways as Parent-Child relationship is done by embedding/nesting - - local Ignored = false - if not (Ignored and Ignored[PropertyName]) then - local MemberTags = Member.Tags - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - - Special = MemberTags.NotScriptable - end - - local ValueType = Member.ValueType - - local Property = { - Name = PropertyName, - Category = ValueType.Category, - Default = Member.Default, - -- Tags = MemberTags, - ValueType = ValueType.Name, - } - - if Special then - Property.Special = true - end - - ClassProperties[PropertyName] = Property - end - end - end - - Class.Properties = ClassProperties - - classList[ClassName] = Class - end - - -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering throguh properties to remove this - - return classList - end - - local ok, result = pcall(FetchAPI) - if ok then - ClassList = result - else - warn(result) - return - end -end -local F = Instance.new("Folder") -local s = "\n" -for Class, ClassInfo in ClassList do - local o, r = pcall(Instance.new, Class) - local x = r - if not o then - r = nil - - if not r then - r = game:FindFirstChildWhichIsA(Class, true) - if not r then - r = game:FindFirstChild(Class, true) - if not r then - if ClassInfo.Tags and ClassInfo.Tags.Service then - pcall(function() - r = service[Class] - end) - end - end - end - end - end - if r then - o, r = pcall(function(_, _2) - _.Parent = _2 - end, r, F) - if not o then - if Find(r, "locked") or Find(r, "Cannot change") or Find(r, "Invalid parent for Service") then - continue - end - s ..= Class .. ": " .. r .. "\n" - end - elseif - not ( - Find(x, "Unable to create an Instance") --[[or Find(x, "The current thread cannot create")]] - ) - then - s ..= "! " .. Class .. ": " .. x .. "\n" - end -end -warn("Finished") -writefile("!missing2", s) diff --git a/Tools/NotCreatable Dumper/Dump b/Tools/NotCreatable Dumper/Dump deleted file mode 100644 index f1e5f30..0000000 --- a/Tools/NotCreatable Dumper/Dump +++ /dev/null @@ -1,283 +0,0 @@ - -Instance - -AnimationClip - -AnimationStreamTrack - -AnimationTrack - -AssetImportSession - -AvatarGenerationJob - -Avatar2DGenerationJob - -Avatar3DGenerationJob - -AvatarGenerationSession - -AnimationImportData - -FacsImportData - -GroupImportData - -JointImportData - -MaterialImportData - -MeshImportData - -RootImportData - -PlayerGui - -BodyMover - -CharacterAppearance - -Clothing - -Collaborator - -CommandInstance - -Constraint - -SlidingBallConstraint - -Controller - -DataModelMesh - -BevelMesh - -DataModelSession - -DataStoreInfo - -DataStoreKey - -DataStoreKeyInfo - -DataStoreObjectVersionInfo - -DebuggerBreakpoint - -DebuggerConnection - -LocalDebuggerConnection - -DebuggerLuaResponse - -DebuggerVariable - -FaceInstance - -FacialAnimationStreamingServiceStats - -FacialAnimationStreamingSubsessionStats - -Feature - -File - -GlobalDataStore - -DataStore - -OrderedDataStore - -GuiObject - -GuiButton - -GuiLabel - -LayerCollector - -InstanceAdornment - -PVAdornment - -HandleAdornment - -PartAdornment - -SelectionLasso - -HttpRequest - -InputObject - -JointInstance - -DynamicRotate - -ManualSurfaceJointInstance - -Light - -CloudLocalizationTable - -LodDataEntity - -LuaSourceContainer - -CoreScript - -MaterialGenerationSession - -MemStorageConnection - -MemoryStoreHashMap - -MemoryStoreQueue - -MemoryStoreSortedMap - -MessageBusConnection - -MetaBreakpoint - -MetaBreakpointContext - -Mouse - -PlayerMouse - -MultipleDocumentInterfaceInstance - -NetworkMarker - -NetworkPeer - -NetworkReplicator - -ClientReplicator - -ServerReplicator - -OpenCloudApiV1 - -PVInstance - -FormFactorPart - -Platform - -Terrain - -TriangleMeshPart - -BackpackItem - -Status - -WorldRoot - -PackageLink - -PatchMapping - -Path - -PausedState - -PausedStateBreakpoint - -PausedStateException - -PlayerScripts - -PostEffect - -ReflectionMetadataItem - -ScreenshotHud - -ScriptBuilder - -SyncScriptBuilder - -ScriptDebugger - -ScriptDocument - -ScriptRuntime - -ControllerSensor - -ServiceProvider - -DataModel - -SoundEffect - -CustomSoundEffect - -AssetSoundEffect - -ChannelSelectorSoundEffect - -StackFrame - -StarterPlayerScripts - -StarterCharacterScripts - -StatsItem - -RunningAverageItemDouble - -RunningAverageItemInt - -RunningAverageTimeIntervalItem - -TotalCountTimeIntervalItem - -StudioWidget - -StudioTheme - -TeleportAsyncResult - -TextChatConfigurations - -BubbleChatConfiguration - -ChannelTabsConfiguration - -ChatInputBarConfiguration - -ChatWindowConfiguration - -TextChatMessage - -TextFilterResult - -TextFilterTranslatedResult - -TextSource - -TextureGenerationPartGroup - -TextureGenerationUnwrappingRequest - -ThreadState - -TouchTransmitter - -TrackerLodController - -Translator - -UIComponent - -UIConstraint - -UILayout - -UIGridStyleLayout - diff --git a/Tools/NotCreatable Dumper/NotCreatable Dumper.py b/Tools/NotCreatable Dumper/NotCreatable Dumper.py deleted file mode 100644 index 3a1b16f..0000000 --- a/Tools/NotCreatable Dumper/NotCreatable Dumper.py +++ /dev/null @@ -1,80 +0,0 @@ -import requests -import os - - -def array_to_dictionary(table, hybrid_mode=None): - tmp = {} - if hybrid_mode == "adjust": - for key, value in table.items(): - if isinstance(key, int): - tmp[value] = True - elif isinstance(value, dict): - tmp[key] = array_to_dictionary(value, "adjust") - else: - tmp[key] = value - else: - for value in table: - if isinstance(value, str): - tmp[value] = True - return tmp - - -s = "\n" - - -def fetch_api(): - api_dump_url = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - response = requests.get(api_dump_url) - api_classes = response.json()["Classes"] - - global s - class_list = {} - - for api_class in api_classes: - class_name = api_class["Name"] - class_tags = api_class.get("Tags") - - if class_tags: - # print(class_tags) - if len(class_tags) == 0: - print("tagsempty") - class_tags = array_to_dictionary(class_tags) - else: - print(class_name, "notags") - - class_info = { - "Tags": class_tags, - "Superclass": api_class["Superclass"], - "Properties": {}, - } - - prev_len = len(s) - - if ( - class_tags - and class_tags.get("NotCreatable") - and not class_tags.get("Service") - and not "Base" in class_name - and not "Page" in class_name - and not "Plugin" in class_name - and not "Setting" in class_name - ): - s += f"{class_name}\n" - - if len(s) != prev_len: - s += "\n" - - class_list[class_name] = class_info - - return class_list - - -try: - fetch_api() - print(s) - script_dir = os.path.dirname(os.path.realpath(__file__)) - output_file_path = os.path.join(script_dir, "Dump") - with open(output_file_path, "w") as file: - file.write(s) -except Exception as e: - print(f"Error: {e}") diff --git a/Tools/NotScriptable-Related/NotScriptable Dumper/Dump b/Tools/NotScriptable-Related/NotScriptable Dumper/Dump deleted file mode 100644 index 96d60a6..0000000 --- a/Tools/NotScriptable-Related/NotScriptable Dumper/Dump +++ /dev/null @@ -1,260 +0,0 @@ - -Instance.AttributesSerialize {BinaryString} -Instance.DefinesCapabilities -Instance.HistoryId -Instance.Tags {BinaryString} -Instance.UniqueId - -AnimationClip.GuidBinaryString {BinaryString} - -AnimationRigData.label {BinaryString} -AnimationRigData.name {BinaryString} -AnimationRigData.parent {BinaryString} -AnimationRigData.postTransform {BinaryString} -AnimationRigData.preTransform {BinaryString} -AnimationRigData.transform {BinaryString} - -StarterGui.RtlTextSupport -StarterGui.VirtualCursorMode - -BaseWrap.HSRAssetId -BaseWrap.HSRData -BaseWrap.HSRMeshIdData -BaseWrap.TemporaryCageMeshId - -WrapLayer.TemporaryReferenceId - -BallSocketConstraint.MaxFrictionTorqueXml - -CustomEvent.PersistedCurrentValue - -BevelMesh.Bevel -BevelMesh.Bevel Roundness -BevelMesh.Bulge - -RobloxEditableMesh.MeshDataSerialize {BinaryString} - -DebuggerBreakpoint.line - -RobloxEditableImage.ImageDataSerialize {BinaryString} - -Fire.heat_xml -Fire.size_xml - -FloatCurve.ValuesAndTimes {BinaryString} - -FunctionalTest.HasMigratedSettingsToTestService - -GoogleAnalyticsConfiguration.gaID - -ViewportFrame.CameraCFrame -ViewportFrame.CameraFieldOfView - -Path2D.PropertiesSerialize {BinaryString} - -HiddenSurfaceRemovalAsset.HSRData {BinaryString} -HiddenSurfaceRemovalAsset.HSRMeshIdData {BinaryString} - -Humanoid.Health_XML -Humanoid.InternalBodyScale -Humanoid.InternalHeadScale - -HumanoidDescription.AccessoryBlob -HumanoidDescription.AccessoryRigidAndLayeredBlob -HumanoidDescription.EmotesDataInternal -HumanoidDescription.EquippedEmotesDataInternal - -InsertService.AllowClientInsertModels - -LocalizationTable.Contents - -LuaSourceContainer.ScriptGuid - -MarkerCurve.ValuesAndTimes {BinaryString} - -MaterialService.AsphaltName -MaterialService.BasaltName -MaterialService.BrickName -MaterialService.CardboardName -MaterialService.CarpetName -MaterialService.CeramicTilesName -MaterialService.ClayRoofTilesName -MaterialService.CobblestoneName -MaterialService.ConcreteName -MaterialService.CorrodedMetalName -MaterialService.CrackedLavaName -MaterialService.DiamondPlateName -MaterialService.FabricName -MaterialService.FoilName -MaterialService.GlacierName -MaterialService.GraniteName -MaterialService.GrassName -MaterialService.GroundName -MaterialService.IceName -MaterialService.LeafyGrassName -MaterialService.LeatherName -MaterialService.LimestoneName -MaterialService.MarbleName -MaterialService.MetalName -MaterialService.MudName -MaterialService.PavementName -MaterialService.PebbleName -MaterialService.PlasterName -MaterialService.PlasticName -MaterialService.RockName -MaterialService.RoofShinglesName -MaterialService.RubberName -MaterialService.SaltName -MaterialService.SandName -MaterialService.SandstoneName -MaterialService.SlateName -MaterialService.SmoothPlasticName -MaterialService.SnowName -MaterialService.Use2022MaterialsXml -MaterialService.WoodName -MaterialService.WoodPlanksName - -MaterialVariant.TexturePack - -MetaBreakpointContext.ContextDataInternal - -BasePart.Color3uint8 -BasePart.MaterialVariantSerialized -BasePart.size - -FormFactorPart.formFactorRaw -formFactorRaw -> FormFactor -formFactorRaw -> formFactor - -Part.shape -shape -> Shape -shape -> shap - -Terrain.AcquisitionMethod -Terrain.Decoration -Terrain.GrassLength -Terrain.MaterialColors {BinaryString} -Terrain.PhysicsGrid {BinaryString} -Terrain.SmoothGrid {BinaryString} -AcquisitionMethod -> LastUsedModificationMethod - -TriangleMeshPart.AeroMeshData -TriangleMeshPart.FluidFidelityInternal -TriangleMeshPart.PhysicalConfigData -FluidFidelityInternal -> FluidFidelity - -MeshPart.InitialSize -MeshPart.PhysicsData {BinaryString} -MeshPart.VertexCount - -PartOperation.AssetId -PartOperation.ChildData {BinaryString} -PartOperation.ChildData2 -PartOperation.FormFactor -PartOperation.InitialSize -PartOperation.MeshData {BinaryString} -PartOperation.MeshData2 -PartOperation.PhysicsData {BinaryString} - -TrussPart.style -style -> Style - -Model.ModelMeshCFrame -Model.ModelMeshData -Model.ModelMeshSize -Model.NeedsPivotMigration -Model.ScaleFactor -Model.WorldPivotData - -Workspace.AvatarUnificationMode -Workspace.CSGAsyncDynamicCollision -Workspace.CollisionGroupData {BinaryString} -Workspace.DecreaseMinimumPartDensityMode -Workspace.ExplicitAutoJoints -Workspace.FluidForces -Workspace.IKControlConstraintSupport -Workspace.MeshPartHeadsAndAccessories -Workspace.ModelStreamingBehavior -Workspace.MoverConstraintRootBehavior -Workspace.PhysicsSteppingMethod -Workspace.PlayerCharacterDestroyBehavior -Workspace.PrimalPhysicsSolver -Workspace.RejectCharacterDeletions -Workspace.RenderingCacheOptimizations -Workspace.ReplicateInstanceDestroySetting -Workspace.SignalBehavior2 -Workspace.StreamOutBehavior -Workspace.StreamingIntegrityMode -Workspace.StreamingMinRadius -Workspace.StreamingTargetRadius -Workspace.TerrainWeldsFixed -Workspace.TouchesUseCollisionGroups -SignalBehavior2 -> SignalBehavior - -PackageLink.AutoUpdate -PackageLink.ModifiedState -PackageLink.PackageIdSerialize -PackageLink.VersionIdSerialize - -PartOperationAsset.ChildData {BinaryString} -PartOperationAsset.MeshData {BinaryString} - -Player.RawJoinData {BinaryString} - -Players.UseStrafingAnimations - -RotationCurve.ValuesAndTimes {BinaryString} - -ScriptDebugger.CoreScriptIdentifier -ScriptDebugger.ScriptGuid - -ServerScriptService.LoadStringEnabled - -Smoke.opacity_xml -Smoke.riseVelocity_xml -Smoke.size_xml - -Sound.xmlRead_MaxDistance_3 {PreferredDescriptorName: RollOffMaxDistance} - -SoundService.VolumetricAudio - -StarterPlayer.AvatarJointUpgrade_Serialized -StarterPlayer.EnableDynamicHeads -StarterPlayer.LoadCharacterLayeredClothing -AvatarJointUpgrade_Serialized -> AvatarJointUpgrade -LoadCharacterLayeredClothing -> LoadCharacterLayeredClothing - -StyleRule.Index -StyleRule.PropertiesSerialize {BinaryString} - -StyleDerive.Index - -TerrainDetail.TexturePack - -TerrainRegion.ExtentsMax -TerrainRegion.ExtentsMin -TerrainRegion.SmoothGrid {BinaryString} - -UnvalidatedAssetService.CachedData - -UserGameSettings.CompletedTutorials -UserGameSettings.ComputerCameraMovementChanged -UserGameSettings.ComputerMovementChanged -UserGameSettings.StartMaximized -UserGameSettings.StartScreenPosition -UserGameSettings.StartScreenSize -UserGameSettings.TouchCameraMovementChanged -UserGameSettings.TouchMovementChanged -UserGameSettings.gaID - -BinaryStringValue.Value {BinaryString} - -DoubleConstrainedValue.value - -IntConstrainedValue.value - -WeldConstraint.CFrame0 -WeldConstraint.Part0Internal -WeldConstraint.Part1Internal -WeldConstraint.State - diff --git a/Tools/NotScriptable-Related/NotScriptable Dumper/NotScriptable Dumper.luau b/Tools/NotScriptable-Related/NotScriptable Dumper/NotScriptable Dumper.luau deleted file mode 100644 index d98b5a2..0000000 --- a/Tools/NotScriptable-Related/NotScriptable Dumper/NotScriptable Dumper.luau +++ /dev/null @@ -1,85 +0,0 @@ --- ! Meant for Studio - -local GlobalSettings, GlobalBasicSettings = settings(), UserSettings() -local service = setmetatable({}, { - __index = function(self, index) - local Service = game:GetService(index) - or GlobalSettings:GetService(index) - or GlobalBasicSettings:GetService(index) - self[index] = Service - return Service - end, -}) - -service.HttpService.HttpEnabled = true - -local function ArrayToDictionary(Table, HybridMode, ValueOverride) - local tmp = {} - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = ValueOverride or true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - if type(Key) == "string" then - tmp[Key] = true - end - end - end - - return tmp -end -local s = "\n" -do - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = service.HttpService:GetAsync(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - local ClassName = API_Class.Name - local ClassMembers = API_Class.Members - - local prevsiz = #s - for _index_1 = 1, #ClassMembers do - local Member = ClassMembers[_index_1] - local MemberName = Member.Name - local MemberType = Member.MemberType - if MemberType == "Property" then - local Serialization = Member.Serialization - local MemberTags = Member.Tags - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - Special = MemberTags.NotScriptable - end - if Serialization.CanLoad and Serialization.CanSave and Special then - local ValueType = Member.ValueType.Name - s ..= ClassName .. "." .. MemberName .. (ValueType == "BinaryString" and " {BinaryString}" or "") .. "\n" - end - end - end - if prevsiz ~= #s then - s ..= "\n" - end - end - end - - local ok, result = pcall(FetchAPI) - - if ok then - print(s) - else - warn(result) - end -end diff --git a/Tools/NotScriptable-Related/NotScriptable Dumper/NotScriptable Dumper.py b/Tools/NotScriptable-Related/NotScriptable Dumper/NotScriptable Dumper.py deleted file mode 100644 index 46e5da3..0000000 --- a/Tools/NotScriptable-Related/NotScriptable Dumper/NotScriptable Dumper.py +++ /dev/null @@ -1,90 +0,0 @@ -import requests -import os - - -def array_to_dictionary(table, hybrid_mode=None): - tmp = {} - if hybrid_mode == "adjust": - for key, value in table.items(): - if isinstance(key, int): - tmp[value] = True - elif isinstance(value, dict): - tmp[key] = array_to_dictionary(value, "adjust") - else: - tmp[key] = value - else: - for value in table: - if isinstance(value, str): - tmp[value] = True - return tmp - - -def find_first_table(array): - for item in array: - if isinstance(item, dict): - return item - return None - - -s = "\n" - - -def fetch_api(): - api_dump_url = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - response = requests.get(api_dump_url) - api_classes = response.json()["Classes"] - - global s - for api_class in api_classes: - class_name = api_class["Name"] - class_members = api_class["Members"] - - prev_len = len(s) - enum_members = {} - - for member in class_members: - member_name = member["Name"] - member_type = member["MemberType"] - if member_type == "Property": - serialization = member["Serialization"] - member_tags = member.get("Tags") - original_tags = member_tags - special = False - if member_tags: - member_tags = array_to_dictionary(member_tags) - special = member_tags.get("NotScriptable") - - if serialization["CanLoad"] and serialization["CanSave"] and special: - value_type = member["ValueType"]["Name"] - value_category = member["ValueType"]["Category"] - if value_category == "Enum": - enum_members[value_type] = member_name - s += f"{class_name}.{member_name} {'{BinaryString}' if value_type == 'BinaryString' else ''}" - table_found = find_first_table(original_tags) - if table_found: - s += f"{'{PreferredDescriptorName: '+table_found.get('PreferredDescriptorName')+'}'}" - s += "\n" - for enum_type, real_member_name in enum_members.items(): - for member in class_members: - member_name = member["Name"] - member_type = member["MemberType"] - if member_name != real_member_name and member_type == "Property": - value_category = member["ValueType"]["Category"] - if value_category == "Enum": - value_type = member["ValueType"]["Name"] - if value_type == enum_type: - s += f"{real_member_name} -> {member_name}\n" - - if len(s) != prev_len: - s += "\n" - - -try: - fetch_api() - print(s) - script_dir = os.path.dirname(os.path.realpath(__file__)) - output_file_path = os.path.join(script_dir, "Dump") - with open(output_file_path, "w") as file: - file.write(s) -except Exception as e: - print(f"Error: {e}") diff --git a/Tools/NotScriptable-Related/NotScriptable Proxy Dumper/Dump b/Tools/NotScriptable-Related/NotScriptable Proxy Dumper/Dump deleted file mode 100644 index b7bbbd6..0000000 --- a/Tools/NotScriptable-Related/NotScriptable Proxy Dumper/Dump +++ /dev/null @@ -1,40 +0,0 @@ -Doesnt get everything; Also varies per executor; Tested on Codex - -BallSocketConstraint.MaxFrictionTorqueXml - MaxFrictionTorque - -BasePart.MaterialVariantSerialized - MaterialVariant -BasePart.MaterialVariantSerialized - CurrentPhysicalProperties -BasePart.MaterialVariantSerialized - PropertyStatusStudio - -DoubleConstrainedValue.value - 1.53736 - -Fire.size_xml - Size -Fire.heat_xml - Heat - -FormFactorPart.formFactorRaw - FormFactor - -Humanoid.Health_XML - Health - -IntConstrainedValue.value - 2 - -MaterialService.Use2022MaterialsXml - Use2022Materials - -MeshPart.InitialSize - MeshSize - -Part.shape - Shape -Part.shape - AssemblyMass -Part.shape - AssemblyCenterOfMass - -Smoke.opacity_xml - Opacity -Smoke.size_xml - Size -Smoke.riseVelocity_xml - RiseVelocity - -Sound.xmlRead_MaxDistance_3 - MaxDistance -Sound.xmlRead_MaxDistance_3 - RollOffMaxDistance - -WeldConstraint.State - Enabled -WeldConstraint.Part0Internal - Part0 -WeldConstraint.Part1Internal - Part1 - -Workspace.SignalBehavior2 - SignalBehavior - diff --git a/Tools/NotScriptable-Related/NotScriptable Proxy Dumper/NotScriptable Proxy Dumper.luau b/Tools/NotScriptable-Related/NotScriptable Proxy Dumper/NotScriptable Proxy Dumper.luau deleted file mode 100644 index 1a0cb95..0000000 --- a/Tools/NotScriptable-Related/NotScriptable Proxy Dumper/NotScriptable Proxy Dumper.luau +++ /dev/null @@ -1,297 +0,0 @@ --- Dumps all properties that get changed when NotScriptable property is changed. This means they're somehow related to it and/or are in sync with it --- Doesnt get everything! -local service = setmetatable({}, { - __index = function(Self, Name) - local Service = game:GetService(Name) - Self[Name] = Service - return Service - end, -}) -local function ArrayToDictionary(Table, HybridMode) - local tmp = table.create(#Table) - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - tmp[Key] = true - end - end - - return tmp -end - -do - -- TODO: More @ https://github.com/Dekkonot/rbx-instance-serializer/blob/23f772f6f78af879a21faa9fea3e6c4f93d1cdee/src/API.lua#L19 - - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = game:HttpGet(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - - local classList = {} - local InheritorsIndex = {} - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - local ClassMembers = API_Class.Members - - local Class = {} - - local ClassName = API_Class.Name - - local ClassTags = API_Class.Tags - - if ClassTags then - ClassTags = ArrayToDictionary(ClassTags) - end - - -- ClassInfo.Name = ClassName - Class.Tags = ClassTags -- or {} - local Superclass = API_Class.Superclass - Class.Superclass = Superclass - - local SuperclassInheritors = InheritorsIndex[Superclass] - if SuperclassInheritors then - table.insert(SuperclassInheritors, ClassName) - else - SuperclassInheritors = { ClassName } - InheritorsIndex[Superclass] = SuperclassInheritors - end - - local ClassProperties = {} - - for _index_1 = 1, #ClassMembers do - local Member = ClassMembers[_index_1] - if Member.MemberType == "Property" then - local PropertyName = Member.Name - - local Ignored = false - if not (Ignored and Ignored[PropertyName]) then - local MemberTags = Member.Tags - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - - Special = MemberTags.NotScriptable - end - local Serialization = Member.Serialization - if Serialization.CanLoad and Serialization.CanSave and Special then - local ValueType = Member.ValueType - - local Property = { - Name = PropertyName, - Category = ValueType.Category, - Default = Member.Default, - -- Tags = MemberTags, - ValueType = ValueType.Name, - } - - if Special then - Property.Special = true - end - - ClassProperties[PropertyName] = Property - end - end - end - end - local ClassInheritors = InheritorsIndex[ClassName] - if not ClassInheritors then - ClassInheritors = {} - InheritorsIndex[ClassName] = ClassInheritors - end - Class.Inheritors = ClassInheritors - Class.Properties = ClassProperties - - classList[ClassName] = Class - end - - -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering throguh properties to remove this - - return classList - end - - local s = `Doesnt get everything; Also varies per executor; Tested on {identifyexecutor()}\n\n` - local api = FetchAPI() - local keys = {} - for classname in api do - table.insert(keys, classname) - end - table.sort(keys) - local t = { - BasePart = Instance.new("Part"), - Instance = Instance.new("Part"), - BinaryString = "MTAwMDAwMDAwMDAwMDAwMA==", - -- bool = nil, - CFrame = CFrame.new(0, 5, 8), - OptionalCoordinateFrame = CFrame.new(0, 5, 8), - Color3uint8 = Color3.new(0.3, 0.5, 0.8), - Content = "rbxassetid://101332", - double = 1.53736, - float = 1.53736, - int = 1.53736, - int64 = 1.53736, - SecurityCapabilities = 1, - SharedString = "MTAwMDAwMDAwMDAwMDAwMA==", - string = '{"TEST": "hello"}', - UniqueId = "00000000000001000000000000000000", - Vector2 = Vector2.new(1, 23), - Vector3 = Vector3.new(1, 23, 15), - Vector3int16 = Vector3.new(1, 23, 15), - } - local Parent = Instance.new("MeshPart") - Parent.Anchored = true - Parent.Parent = workspace - local RS = game:GetService("RunService") - for _, ClassName in keys do - local ClassInfo = api[ClassName] - local instance - - local _ - _, instance = pcall(Instance.new, ClassName) - if not _ then - instance = nil - if ClassName == "UserGameSettings" then - instance = UserSettings():GetService("UserGameSettings") - elseif ClassName == "UserSettings" then - instance = UserSettings() - elseif ClassName == "GlobalSettings" then - instance = settings() - elseif ClassName == "TerrainRegion" then - instance = workspace.Terrain:CopyRegion(Region3int16.new()) - end - if ClassInfo.Tags and ClassInfo.Tags.Service and ClassName ~= "VoiceChatInternal" then - -- writefile("crasher.txt", ClassName) - pcall(function() - instance = game:GetService(ClassName) - end) - if not instance then - pcall(function() - instance = game:GetService(ClassName) - end) - end - end - if not instance then - instance = game:FindFirstChild(ClassName, true) - if not instance then - instance = game:FindFirstChildWhichIsA(ClassName, true) - if not instance then - for i, Inheritor_ClassName in ClassInfo.Inheritors do - _, instance = pcall(Instance.new, Inheritor_ClassName) - if _ then - warn("Success!") - break - else - instance = nil - end - end - end - end - end - end - local prevsiz = #s - -- print(ClassName) - for _, PropertyInfo in ClassInfo.Properties do - local PropertyName = PropertyInfo.Name - - if instance then - writefile("crasher.txt", ClassName .. "." .. PropertyName) - if instance.Parent == nil and instance ~= game then - local O, err = pcall(function() - instance.Parent = Parent - end) - if not O then - print("Parent Error", err) - end - end - local alreadynoted = {} - local IsValueBase = instance:IsA("ValueBase") - -- local cor = coroutine.running() - local function f(descriptor) - if descriptor == PropertyName or alreadynoted[descriptor] then - return - end - - alreadynoted[descriptor] = true - local str = ClassName .. "." .. PropertyName .. " - " .. descriptor - s ..= str .. "\n" - - return str - end - - local listen = (IsValueBase and game.ItemChanged or instance.Changed):Connect( - IsValueBase - and function(obj, descriptor) - if obj == instance then - f(descriptor) - -- task.defer(coroutine.resume, cor) - end - end - or function(descriptor) - local str = f(descriptor) - if str then - warn(str) - end - -- task.defer(coroutine.resume, cor) - end - ) - -- - local value = nil - - local ValueType = PropertyInfo.ValueType - - if PropertyInfo.Category == "Enum" then - if not pcall(function() - value = Enum[ValueType]:GetEnumItems()[1] - end) then - warn("Enum", ValueType) - end - else - value = t[ValueType] - end - - if not value and ValueType ~= "bool" then - print(PropertyInfo.ValueType) - end - local O, err = pcall(sethiddenproperty, instance, PropertyName, value) - - if ValueType == "bool" then - O, err = pcall(sethiddenproperty, instance, PropertyName, true) - if not O then - print("SetProp Error", err) - end - O, err = pcall(sethiddenproperty, instance, PropertyName, false) - if not O then - print("SetProp Error", err) - end - end - -- task.delay(0.1, coroutine.resume, cor) - -- coroutine.yield() - -- listen:Disconnect() - - task.wait(0.01) - - listen:Disconnect() - end - end - if prevsiz ~= #s then - s ..= "\n" - end - end - warn("Finished") - -- warn(s) - writefile("!proxy.txt", s) - - -- writefile("!missing.txt", game:GetService("HttpService"):JSONEncode(t)) -end diff --git a/Tools/NotScriptable-Related/Potentially Missing Properties Dumper/Dump b/Tools/NotScriptable-Related/Potentially Missing Properties Dumper/Dump deleted file mode 100644 index 1c5905b..0000000 --- a/Tools/NotScriptable-Related/Potentially Missing Properties Dumper/Dump +++ /dev/null @@ -1,28 +0,0 @@ -Varies per executor; Tested on Wave - -TriangleMeshPart.AeroMeshData Unable to get property AeroMeshData, type SharedString -TriangleMeshPart.PhysicalConfigData Unable to get property PhysicalConfigData, type SharedString - -DebuggerBreakpoint.line {NotCreatable Class} - -BasePart.Color3uint8 Unable to get property Color3uint8, type Color3uint8 - -Instance.UniqueId Unable to get property UniqueId, type UniqueId -Instance.HistoryId Unable to get property HistoryId, type UniqueId - -BaseWrap.HSRMeshIdData Unable to get property HSRMeshIdData, type SharedString -BaseWrap.HSRData Unable to get property HSRData, type SharedString - -MetaBreakpointContext.ContextDataInternal {NotCreatable Class} - -ScriptDebugger.ScriptGuid {NotCreatable Class} -ScriptDebugger.CoreScriptIdentifier {NotCreatable Class} - -Model.WorldPivotData Unable to get property WorldPivotData, type OptionalCoordinateFrame -Model.ModelMeshData Unable to get property ModelMeshData, type SharedString - -GoogleAnalyticsConfiguration.gaID {NotCreatable Class} - -PartOperation.ChildData2 Unable to get property ChildData2, type SharedString -PartOperation.MeshData2 Unable to get property MeshData2, type SharedString - diff --git a/Tools/NotScriptable-Related/Potentially Missing Properties Dumper/Potentially Missing Properties Dumper.luau b/Tools/NotScriptable-Related/Potentially Missing Properties Dumper/Potentially Missing Properties Dumper.luau deleted file mode 100644 index 989f8df..0000000 --- a/Tools/NotScriptable-Related/Potentially Missing Properties Dumper/Potentially Missing Properties Dumper.luau +++ /dev/null @@ -1,221 +0,0 @@ --- Prints all important yet hidden (NotScriptable) properties that could be missing from saveinstace file after it's done saving --- due to broken gethiddenproperty on certain executors, therefore we must check all of these and verify it doesn't happen --- if it does then please make an issue/pull request --- Example of such case: Color3uint8 is prefered way to save Part's Color but this property is NotScriptable and might fail on certain executors therefore we must add a fallback redirect! - --- Only types to fail so far: BinaryString, SharedString, Color3uint8 - -local service = setmetatable({}, { - __index = function(Self, Name) - local Service = game:GetService(Name) - Self[Name] = Service - return Service - end, -}) --- service.HttpService.HttpEnabled = true -local function ArrayToDictionary(Table, HybridMode) - local tmp = table.create(#Table) - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - tmp[Key] = true - end - end - - return tmp -end - -do - -- TODO: More @ https://github.com/Dekkonot/rbx-instance-serializer/blob/23f772f6f78af879a21faa9fea3e6c4f93d1cdee/src/API.lua#L19 - - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = game:HttpGet(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - - local classList = {} - local InheritorsIndex = {} - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - local ClassMembers = API_Class.Members - - local Class = {} - - local ClassName = API_Class.Name - - local ClassTags = API_Class.Tags - - if ClassTags then - ClassTags = ArrayToDictionary(ClassTags) - end - - -- ClassInfo.Name = ClassName - Class.Tags = ClassTags -- or {} - local Superclass = API_Class.Superclass - Class.Superclass = Superclass - - local SuperclassInheritors = InheritorsIndex[Superclass] - if SuperclassInheritors then - table.insert(SuperclassInheritors, ClassName) - else - SuperclassInheritors = { ClassName } - InheritorsIndex[Superclass] = SuperclassInheritors - end - - local ClassProperties = {} - - for _index_1 = 1, #ClassMembers do - local Member = ClassMembers[_index_1] - if Member.MemberType == "Property" then - local PropertyName = Member.Name - - -- ? We avoid this as some Instances like services may have this property locked and thus make file unable to open and it's not even used by Roblox anyways as Parent-Child relationship is done by embedding/nesting - - local Ignored = false - if not (Ignored and Ignored[PropertyName]) then - local MemberTags = Member.Tags - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - - Special = MemberTags.NotScriptable - end - local Serialization = Member.Serialization - if Serialization.CanLoad and Serialization.CanSave and Special then - local ValueType = Member.ValueType - - local Property = { - Name = PropertyName, - Category = ValueType.Category, - Default = Member.Default, - -- Tags = MemberTags, - ValueType = ValueType.Name, - } - - if Special then - Property.Special = true - end - - ClassProperties[PropertyName] = Property - end - end - end - end - local ClassInheritors = InheritorsIndex[ClassName] - if not ClassInheritors then - ClassInheritors = {} - InheritorsIndex[ClassName] = ClassInheritors - end - Class.Inheritors = ClassInheritors - Class.Properties = ClassProperties - - classList[ClassName] = Class - end - - -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering throguh properties to remove this - - return classList - end - - local s = `Varies per executor; Tested on {identifyexecutor()}\n\n` - for ClassName, ClassInfo in FetchAPI() do - local instance - - local _ - _, instance = pcall(Instance.new, ClassName) - if not _ then - instance = nil - if ClassName == "UserGameSettings" then - instance = UserSettings():GetService("UserGameSettings") - elseif ClassName == "UserSettings" then - instance = UserSettings() - elseif ClassName == "GlobalSettings" then - instance = settings() - elseif ClassName == "TerrainRegion" then - instance = workspace.Terrain:CopyRegion(Region3int16.new()) - end - if ClassInfo.Tags and ClassInfo.Tags.Service and ClassName ~= "VoiceChatInternal" then - -- writefile("crasher", ClassName) - pcall(function() - instance = game:GetService(ClassName) - end) - if not instance then - pcall(function() - instance = game:GetService(ClassName) - end) - end - end - if not instance then - instance = game:FindFirstChild(ClassName, true) - if not instance then - instance = game:FindFirstChildWhichIsA(ClassName, true) - if not instance then - for i, Inheritor_ClassName in ClassInfo.Inheritors do - _, instance = pcall(Instance.new, Inheritor_ClassName) - if _ then - warn("Success!") - break - else - instance = nil - end - end - end - end - end - end - local prevsiz = #s - - local gethiddenproperty_fallback - do - local UGCValidationService = service.UGCValidationService - - gethiddenproperty_fallback = function(instance, propertyName) - return UGCValidationService:GetPropertyValue(instance, propertyName) -- :) - end - end - - for _, PropertyInfo in ClassInfo.Properties do - local PropertyName = PropertyInfo.Name - local o, r = true, true - if instance then - o, r = pcall(gethiddenproperty, instance, PropertyName) - if not o then - local oo, rr = pcall(gethiddenproperty_fallback, instance, PropertyName) - if rr == nil then - o = false - elseif oo then - o = oo - r = rr - end - end - else - o = nil - end - if o == nil or o == false then - s ..= ClassName .. "." .. PropertyName .. ` {if o == nil then "{NotCreatable Class}" else ""} {if o - == false - then r - else ""}\n` - end - -- (not o and warn or print)(ClassName, PropertyName, not o and r or "") - end - if prevsiz ~= #s then - s ..= "\n" - end - end - warn("Finished") - writefile("!missing.txt", s) -end diff --git a/Tools/saveinstance_indented.luau b/Tools/saveinstance_indented.luau deleted file mode 100644 index 848cf7f..0000000 --- a/Tools/saveinstance_indented.luau +++ /dev/null @@ -1,1844 +0,0 @@ ---!native --- https://discord.gg/wx4ThpAsmw - -local Params = { - RepoURL = "https://raw.githubusercontent.com/luau/SomeHub/main/", - UMF = "UniversalMethodFinder", -} -local finder, globalcontainer = loadstring(game:HttpGet(Params.RepoURL .. Params.UMF .. ".luau", true), Params.UMF)() - -finder({ - -- readbinarystring = 'string.find(...,"bin",nil,true)', -- ! Could match some unwanted stuff - decompile = '(string.find(...,"decomp",nil,true) and string.sub(...,#...) ~= "s") or string.find(...,"assembl",nil,true)', - gethiddenproperties = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) == "s"', - gethiddenproperty = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) ~= "s"', - gethui = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"ui",nil,true)', - getnilinstances = 'string.find(...,"nil",nil,true)', -- ! Could match some unwanted stuff - getproperties = 'string.find(...,"get",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) == "s"', - getscriptbytecode = 'string.find(...,"get",nil,true) and string.find(...,"bytecode",nil,true)', - getspecialinfo = 'string.find(...,"get",nil,true) and string.find(...,"spec",nil,true)', - protectgui = 'string.find(...,"protect",nil,true) and string.find(...,"ui",nil,true) and not string.find(...,"un",nil,true)', - request = 'string.find(...,"request",nil,true) and not string.find(...,"internal",nil,true)', - sethiddenproperty = 'string.find(...,"set",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) ~= "s"', - writefile = 'string.find(...,"file",nil,true) and string.find(...,"write",nil,true)', -}, true) - -local decompile = globalcontainer.decompile -local gethiddenproperty = globalcontainer.gethiddenproperty -local sethiddenproperty = globalcontainer.sethiddenproperty -local writefile = globalcontainer.writefile - -local request = globalcontainer.request -local getscriptbytecode = globalcontainer.getscriptbytecode - -if not globalcontainer.getspecialinfo then - globalcontainer.getspecialinfo = globalcontainer.gethiddenproperties -end - -local function Find(String, Pattern) - return string.find(String, Pattern, nil, true) -end - -local GlobalSettings, GlobalBasicSettings = settings(), UserSettings() -local service = setmetatable({}, { - __index = function(self, Name) - local Service = game:GetService(Name) or GlobalSettings:GetService(Name) or GlobalBasicSettings:GetService(Name) - self[Name] = Service - return Service - end, -}) - -local EscapesPattern = "[&<>\"'\1-\9\11-\12\14-\31\127-\255]" -- * The safe way is to escape all five characters in text. However, the three characters " ' and > needn't be escaped in text --- %z (\0 aka NULL) might not be needed as Roblox automatically converts it to space everywhere it seems like --- Characters from: https://create.roblox.com/docs/en-us/ui/rich-text#escape-forms --- TODO: EscapesPattern should be ordered from most common to least common characters for sake of speed --- TODO: Might wanna use their numerical codes instead of named codes for reduced file size (Could be an Option) -local Escapes = { - ["&"] = "&", -- 38 - ["<"] = "<", -- 60 - [">"] = ">", -- 62 - ['"'] = """, -- 34 - ["'"] = "'", -- 39 -} - -for rangeStart, rangeEnd in string.gmatch(EscapesPattern, "(.)%-(.)") do - for charCode = string.byte(rangeStart), string.byte(rangeEnd) do - Escapes[string.char(charCode)] = "&#" .. charCode .. ";" - end -end - -local Base64_Encode -do - if not bit32.byteswap or not pcall(bit32.byteswap, 1) then -- Because Fluxus is missing byteswap - bit32 = table.clone(bit32) - - local function tobit(num) - num %= (bit32.bxor(num, 32)) - if num > 0x80000000 then - num = num - bit32.bxor(num, 32) - end - return num - end - - bit32.byteswap = function(num) - local BYTE_SIZE = 8 - local MAX_BYTE_VALUE = 255 - - num %= bit32.bxor(2, 32) - - local a = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local b = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local c = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local d = bit32.band(num, MAX_BYTE_VALUE) - num = tobit(bit32.lshift(bit32.lshift(bit32.lshift(a, BYTE_SIZE) + b, BYTE_SIZE) + c, BYTE_SIZE) + d) - return num - end - - table.freeze(bit32) - end - local Base64_Encode_Buffer = loadstring( - game:HttpGet("https://raw.githubusercontent.com/Reselim/Base64/master/Base64.lua", true), - "Base64" - )().encode - Base64_Encode = function(raw) -- ? Reselim broke all scripts that rely on their Base64 Implementation because they changed to buffers from strings (both as input & output) - return raw == "" and raw or buffer.tostring(Base64_Encode_Buffer(buffer.fromstring(raw))) - end -end - --- if not decompile then -if request and getscriptbytecode then - decompile = function(Script) - -- Credits @Lonegwadiwaitor - local bytecode = getscriptbytecode(Script) - - if bytecode == "" then - error("Bytecode is empty") - end - - local response = request({ - Url = "https://decompile.glitch.me/bytecode", - Method = "POST", - Headers = { - ["Content-Type"] = "application/json", - }, - Body = service.HttpService:JSONEncode({ - version = 5, - bytecode = Base64_Encode(bytecode), - }), - }) - - if response.Success == false then -- Server Issue - return response.StatusMessage - end - - local decoded = service.HttpService:JSONDecode(response.Body) - - if decoded.status ~= "ok" then -- Decompiler Issue - return decoded.status - end - - return decoded.output - end -end -local DecompileHandler -if decompile then - DecompileHandler = function(Script, Timeout) - local thread = coroutine.running() - local finished - - task.spawn(function() - local ok, result = pcall(decompile, Script, Timeout + 1, Timeout + 1) -- ! This might break on Syn due to second param being bool or string (deprecated tho) - - if not finished then - finished = true - - coroutine.resume(thread, ok, result) - end - end) - - task.spawn(function() - task.wait(Timeout) - - if not finished then - finished = true - coroutine.resume(thread, nil, "Decompiler timed out") - end - end) - - return coroutine.yield() - end -end - -local SharedStrings = {} -local sharedstrings = setmetatable({ - identifier = 1e15, -- 1 quadrillion, up to 9.(9) quadrillion, in theory this shouldn't ever run out and be enough for all sharedstrings ever imaginable - -- TODO: worst case, add fallback to str randomizer once numbers run out : ) -}, { - __index = function(self, String) - local Identifier = Base64_Encode(tostring(self.identifier)) - self.identifier += 1 - - self[String] = Identifier -- Todo: The value of the md5 attribute is a Base64-encoded key. type elements use this key to refer to the value of the string. The value is the text content, which is Base64-encoded. Historically, the key was the MD5 hash of the string value. However, this is not required; the key can be any value that will uniquely identify the shared string. Roblox currently uses BLAKE2b truncated to 16 bytes.. We probably need to do that too for sake of safety - return Identifier - end, -}) - -local Descriptors -Descriptors = { - __APIPRECISION = function(raw, default) - if raw == 0 or raw % 1 == 0 then - return raw - end - - local Extreme = Descriptors.__EXTREMIFY(raw) - if Extreme then - return Extreme - end - - local precision - if type(default) == "string" then -- TODO: This part isn't too necessary at all and affects speed - local dotIndex = Find(default, ".") - - if dotIndex then - precision = #default - dotIndex - end - else - precision = default - end - if precision then - -- TODO: scientific notation formatting also takes place if value is a decimal (only counts if it starts with 0.) then values like 0.00008 will be formatted as 8.0000000000000006544e-05 ("%.19e"), it must have 5 or more consecutive (?) zeros for this, on other hand, if it doesn't start with 0. then e20+ format is applied in case it has 20 or more consecutive (?) zeros so 1e20 will be formatted as 1e+20 and upwards (1e+19 is not allowed, same as 1e-04 for decimals) - -- ? The good part is compression of value so less file size BUT at the potential cost of precision loss - - return string.format("%." .. precision .. "f", raw) - end - - return raw - end, - __BINARYSTRING = Base64_Encode, - __BIT = function(...) -- * Credits to Friend (you know yourself) - local Value = 0 - - for Index, Bit in { ... } do - if Bit then - Value += 2 ^ (Index - 1) - end - end - - return Value - end, - __CDATA = function(raw) -- ? Normally Roblox doesn't use CDATA unless the string has newline characters (\n); We rather CDATA everything for sake of speed - return "" - end, - __ENUM = function(raw) - return raw.Value, "token" - end, - __ENUMNAME = function(raw) - return raw.Name - end, - __EXTREMIFY = function(raw) - local Extreme - if raw ~= raw then - Extreme = "NAN" - elseif raw == math.huge then - Extreme = "INF" - elseif raw == -math.huge then - Extreme = "-INF" - end - - return Extreme - end, - __PROTECTEDSTRING = function(raw) -- ? its purpose is to "protect" data from being treated as ordinary character data during processing; - return Find(raw, "]]>") and Descriptors.string(raw, true) or Descriptors.__CDATA(raw) - end, - __SEQUENCE = function(raw, ValueFormat) --The value is the text content, formatted as a space-separated list of floating point numbers. - -- tostring(raw) also works (but way slower rn) - - local Converted = "" - - for _, Keypoint in raw.Keypoints do - Converted ..= Keypoint.Time .. " " .. (ValueFormat and ValueFormat(Keypoint) or Keypoint.Value .. " " .. Keypoint.Envelope .. " ") - end - - return Converted - end, - __VECTOR = function(X, Y, Z) - local Value = "" .. X .. "" .. Y .. "" -- There is no Vector without at least two Coordinates.. (Vector1, at least on Roblox) - - if Z then - Value ..= "" .. Z .. "" - end - - return Value - end, - -------------------------------------------------------------- - -------------------------------------------------------------- - -------------------------------------------------------------- - Axes = function(raw) - --The text of this element is formatted as an integer between 0 and 7 - - return "" .. Descriptors.__BIT(raw.X, raw.Y, raw.Z) .. "" - end, - -- BinaryString = function(raw) - - -- end - BrickColor = function(raw) -- ! Oh well This might hurt Color3 / Color3uint8 properties - return raw.Number, "BrickColor" -- * Roblox encodes the tags as "int", but this is not required for Roblox to properly decode the type. For better compatibility, it is preferred that third-party implementations encode and decode "BrickColor" tags instead. Could also use "int" or "Color3uint8" - end, - CFrame = function(raw) - local X, Y, Z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = raw:GetComponents() - return Descriptors.__VECTOR(X, Y, Z) - .. "" - .. R00 - .. "" - .. R01 - .. "" - .. R02 - .. "" - .. R10 - .. "" - .. R11 - .. "" - .. R12 - .. "" - .. R20 - .. "" - .. R21 - .. "" - .. R22 - .. "", - "CoordinateFrame" - end, - Color3 = function(raw) - return "" .. raw.R .. "" .. raw.G .. "" .. raw.B .. "" -- ? It is recommended that Color3 is encoded with elements instead of text. - end, - Color3uint8 = function(raw) - -- https://github.com/rojo-rbx/rbx-dom/blob/master/docs/xml.md#color3uint8 - - return 0xFF000000 - + (math.floor(raw.R * 255) * 0x10000) - + (math.floor(raw.G * 255) * 0x100) - + math.floor(raw.B * 255) -- ? It is recommended that Color3uint8 is encoded with text instead of elements. - - -- return bit32.bor( - -- bit32.bor(bit32.bor(bit32.lshift(0xFF, 24), bit32.lshift(0xFF * raw.R, 16)), bit32.lshift(0xFF * raw.G, 8)), - -- 0xFF * raw.B - -- ) - - -- return tonumber(string.format("0xFF%02X%02X%02X",raw.R*255,raw.G*255,raw.B*255)) - end, - ColorSequence = function(raw) - --The value is the text content, formatted as a space-separated list of FLOATing point numbers. - - return Descriptors.__SEQUENCE(raw, function(Keypoint) - local Value = Keypoint.Value - return Value.R .. " " .. Value.G .. " " .. Value.B .. " 0 " - end) - end, - Content = function(raw) - return raw == "" and "" or "" .. Descriptors.string(raw, true) .. "" - end, - CoordinateFrame = function(raw) - return "" .. Descriptors.CFrame(raw) .. "" - end, - Faces = function(raw) - -- The text of this element is formatted as an integer between 0 and 63 - return "" - .. Descriptors.__BIT(raw.Right, raw.Top, raw.Back, raw.Left, raw.Bottom, raw.Front) - .. "" - end, - Font = function(raw) - return "" - .. Descriptors.Content(raw.Family) - .. "" - .. Descriptors.__ENUM(raw.Weight) - .. "" --TODO (OPTIONAL ELEMENT): Figure out how to determine (Content) rbxasset://fonts/GothamSSm-Medium.otf - end, - NumberRange = function(raw) -- tostring(raw) also works - --The value is the text content, formatted as a space-separated list of floating point numbers. - return raw.Min .. " " .. raw.Max --[[.. " "]] -- ! This might be required to bypass detections as thats how its formatted usually - end, - -- NumberSequence = Descriptors.__SEQUENCE, - PhysicalProperties = function(raw) - --[[Contains at least one CustomPhysics element, which is interpreted according to the bool type. If this value is true, then the tag also contains an element for each component of the PhysicalProperties: - - Density - Friction - Elasticity - FrictionWeight - ElasticityWeight - -The value of each component is represented by the text content formatted as a 32-bit floating point number (see float).]] - - local CustomPhysics - if raw then - CustomPhysics = true - else - CustomPhysics = false - end - CustomPhysics = "" .. Descriptors.bool(CustomPhysics) .. "" - - return raw - and CustomPhysics .. "" .. raw.Density .. "" .. raw.Friction .. "" .. raw.Elasticity .. "" .. raw.FrictionWeight .. "" .. raw.ElasticityWeight .. "" - or CustomPhysics - end, - -- ProtectedString = function(raw) - -- return tostring(raw), "ProtectedString" - -- end, - Ray = function(raw) - local vector3 = Descriptors.Vector3 - - return "" .. vector3(raw.Origin) .. "" .. vector3(raw.Direction) .. "" - end, - Rect = function(raw) - local vector2 = Descriptors.Vector2 - - return "" .. vector2(raw.Min) .. "" .. vector2(raw.Max) .. "", "Rect2D" - end, - -- Region3 = function(raw) --? Not sure yet - -- local vector3 = Descriptors.Vector3 - - -- local Position = raw.CFrame.Position - -- local Size = raw.Size - - -- return "" - -- .. vector3(Position - (Size * 0.5)) - -- .. "" - -- .. vector3(Position + (Size * 0.5)) - -- .. "" - -- end, - -- Region3int16 = function(raw) --? Not sure yet - -- local vector3int16 = Descriptors.Vector3int16 - - -- return "" .. vector3int16(raw.Min) .. "" .. vector3int16(raw.Max) .. "" - -- end, - SharedString = function(raw) - raw = Base64_Encode(raw) - - local Identifier = sharedstrings[raw] - - if SharedStrings[Identifier] == nil then - SharedStrings[Identifier] = raw - end - - return Identifier - end, -- TODO: Add Support for this https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md#sharedstring - UDim = function(raw) - --[[ - S: Represents the Scale component. Interpreted as a . - O: Represents the Offset component. Interpreted as an . - ]] - - return "" .. raw.Scale .. "" .. raw.Offset .. "" - end, - UDim2 = function(raw) - --[[ - XS: Represents the X.Scale component. Interpreted as a . - XO: Represents the X.Offset component. Interpreted as an . - YS: Represents the Y.Scale component. Interpreted as a . - YO: Represents the Y.Offset component. Interpreted as an . - ]] - - local X, Y = raw.X, raw.Y - - return "" - .. X.Scale - .. "" - .. X.Offset - .. "" - .. Y.Scale - .. "" - .. Y.Offset - .. "" - end, - - -- UniqueId = function(raw) - --[[ - UniqueId properties might be random everytime Studio saves a place file - and don't have a use right now outside of packages, which SSI doesn't - account for anyway. They generate diff noise, so we shouldn't serialize - them until we have to. - ]] - -- https://github.com/MaximumADHD/Roblox-Client-Tracker/blob/roblox/LuaPackages/Packages/_Index/ApolloClientTesting/ApolloClientTesting/utilities/common/makeUniqueId.lua#L62 - -- return "" -- ? No idea if this even needs a Descriptor - -- end, - - Vector2 = function(raw) - --[[ - X: Represents the X component. Interpreted as a . - Y: Represents the Y component. Interpreted as a . - ]] - return Descriptors.__VECTOR(raw.X, raw.Y) - end, - -- Vector2int16 = Descriptors.Vector2, -- except as - Vector3 = function(raw) - --[[ - X: Represents the X component. Interpreted as a . - Y: Represents the Y component. Interpreted as a . - Z: Represents the Z component. Interpreted as a . - ]] - return Descriptors.__VECTOR(raw.X, raw.Y, raw.Z) - end, - -- Vector3int16 = Descriptors.Vector3, -- except as \ - - bool = tostring, - - double = function(raw, default) -- Float64 - return Descriptors.__APIPRECISION(raw, default or 17) --? A precision of at least 17 is required to properly represent a 64-bit floating point value, so this amount is recommended. - end, -- ? wouldn't float be better as an optimization - float = function(raw, default) -- Float32 - return Descriptors.__APIPRECISION(raw, default or 9) -- ? A precision of at least 9 is required to properly represent a 32-bit floating point value, so this amount is recommended. - end, - int = function(raw) -- Int32 - return Descriptors.__EXTREMIFY(raw) or raw - end, - string = function(raw, skipcheck) - return not skipcheck and raw == "" and raw or string.gsub(raw, EscapesPattern, Escapes) - end, -} - -for DescriptorName, RedirectName in - { - NumberSequence = "__SEQUENCE", - Vector2int16 = "Vector2", - Vector3int16 = "Vector3", - int64 = "int", -- Int64 (long) - } -do - Descriptors[DescriptorName] = Descriptors[RedirectName] -end - -for _, originalfuncname in { "getproperties", "getspecialinfo" } do -- * Some executors only allow certain Classes for this method (like UnionOperation, MeshPart, Terrain), for example Electron, Codex - local originalfunc = globalcontainer[originalfuncname] - if originalfunc then - globalcontainer[originalfuncname] = function(instance) - local ok, result = pcall(originalfunc, instance) - return ok and result or {} - end - end -end - -local getproperties = globalcontainer.getproperties - -if getproperties then - if globalcontainer.getspecialinfo then - local old_getspecialinfo = globalcontainer.getspecialinfo - - globalcontainer.getspecialinfo = function(instance) - local specialinfo = getproperties(instance) - - for Property, Value in old_getspecialinfo(instance) do - specialinfo[Property] = Value - end - - return specialinfo - end - else - globalcontainer.getspecialinfo = getproperties - end -end - -local getspecialinfo = globalcontainer.getspecialinfo - -local function ArrayToDictionary(Table, HybridMode) - local tmp = {} - - if HybridMode == "adjust" then - for Some1, Some2 in Table do - if type(Some1) == "number" then - tmp[Some2] = true - elseif type(Some2) == "table" then - tmp[Some1] = ArrayToDictionary(Some2, "adjust") -- Some1 is Class, Some2 is Name - else - tmp[Some1] = Some2 - end - end - else - for _, Key in Table do - tmp[Key] = true - end - end - - return tmp -end - -local ClassList - -local NotScriptableFixes = { - BasePart = { -- TODO: Find a way to integrate this into settings - Color3uint8 = "Color", - }, -} -- For more info: https://github.com/luau/UniversalSynSaveInstance/blob/master/Tests/Potentially%20Missing%20Properties%20Tracker.luau - -do - -- TODO: More @ https://github.com/Dekkonot/rbx-instance-serializer/blob/master/src/API.lua#L19 - -- ! IgnoreClassProperties aren't needed anymore due to CanSave filter (see below) - -- local IgnoreClassProperties = { - -- GuiObject = { "Transparency" }, - -- Instance = { "Parent" }, -- ClassName isn't included because CanLoad & CanSave are false on it - -- BasePart = { "BrickColor" }, - -- Attachment = { "WorldCFrame" }, - --} -- GuiObject.Transparency is almost always 1 meaning everything will be transparent; Instance.Parent is useless in xml (no idea about binary); BasePart.BrickColor hurts other Color3 properties; Attachment.WorldCFrame breaks Attachment.CFrame (and is hidden anyway) - - -- for Class, Properties in IgnoreClassProperties do - -- IgnoreClassProperties[Class] = ArrayToDictionary(Properties) - -- end - - local function FetchAPI() - local API_Dump_Url = - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = game:HttpGet(API_Dump_Url, true) - - local classList = {} - - local function ReturnPropertyInfo(Member, PropertyName) - local MemberTags = Member.Tags - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - - Special = MemberTags.NotScriptable - - -- if MemberTags.Deprecated then -- ! Replaced by CanSave true filter (we could also only filter with CanSave here but if its on same level as CanLoad we possibly reduce amount of things we save therefore file size. if Roblox deems something not worth to save why should we save it too) - -- Allowed = nil - -- end - end - -- if Allowed then - local ValueType = Member.ValueType - - local Property = { - Name = PropertyName, - Category = ValueType.Category, - Default = Member.Default, - -- Tags = MemberTags, - ValueType = ValueType.Name, - } - - if Special then - Property.Special = true - end - return Property - end - - for _, API_Class in service.HttpService:JSONDecode(API_Dump).Classes do - local Class = {} - - local ClassName = API_Class.Name - - local ClassTags = API_Class.Tags - - if ClassTags then - ClassTags = ArrayToDictionary(ClassTags) - end - - -- ClassInfo.Name = ClassName - Class.Tags = ClassTags -- or {} - Class.Superclass = API_Class.Superclass - - local ClassProperties = {} - - local Fix = NotScriptableFixes[ClassName] - local ClassFixes - if Fix then - ClassFixes = {} - for ToFix, FixName in Fix do - ClassFixes[FixName] = ToFix - end - end - - for _, Member in API_Class.Members do - if Member.MemberType == "Property" then - local PropertyName = Member.Name - - local Serialization = Member.Serialization - - if Serialization.CanSave and Serialization.CanLoad then -- If Roblox doesn't save it why should we; If Roblox doesn't load it we don't need to save it - --[[ -- ! CanSave replaces "Tags.Deprecated" check because there are some old properties which are deprecated yet have CanSave. - Example: Humanoid.Health is CanSave false due to Humanoid.Health_XML being CanSave true (obsolete properties basically) - in this case both of them will Load. (aka PropertyPatches) - CanSave being on same level as CanLoad also fixes potential issues with overlapping properties like Color, Color3 & Color3uint8 of BasePart, out of which only Color3uint8 should save - This also fixes everything in IgnoreClassProperties automatically without need to hardcode :) - A very simple fix for many problems that saveinstance scripts encounter! - --]] - - -- local Ignored = IgnoreClassProperties[ClassName] - -- if not (Ignored and Ignored[PropertyName]) then - -- local Allowed = true - - ClassProperties[PropertyName] = ReturnPropertyInfo(Member, PropertyName) - -- end - elseif ClassFixes then - local ToFix = ClassFixes[PropertyName] - if ToFix then - Fix[ToFix] = ReturnPropertyInfo(Member, PropertyName) - end - end - -- end - end - end - - Class.Properties = ClassProperties - - classList[ClassName] = Class - end - - -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering throguh properties to remove this - - return classList - end - - local ok, result = pcall(FetchAPI) - if ok then - ClassList = result - else - warn(result) - return - end -end - -local inheritedproperties = setmetatable({}, { - __index = function(self, ClassName) - local proplist = {} - local layer = ClassList[ClassName] - while layer do - -- proplist = table.move(_list_0, 1, #_list_0, #proplist + 1, proplist) - for _, p in layer.Properties do - table.insert(proplist, table.clone(p)) - end - - layer = ClassList[layer.Superclass] - end - self[ClassName] = proplist - - return proplist - end, -}) - -local classreplicas = setmetatable({}, { - __index = function(self, ClassName) - local Replica = Instance.new(ClassName) -- ! Might need to pcall this - self[ClassName] = Replica - return Replica - end, -}) - -local referents = setmetatable({ - identifier = 0, -}, { - __index = function(self, instance) - local referent = self.identifier -- Todo: Roblox encodes all elements with a referent attribute. Each value is generated by starting with the prefix RBX, followed by a UUID version 4, with - characters removed, and all characters converted to uppercase. We probably need to do that too for sake of safety - self.identifier = referent + 1 -- Is faster than self.identifier += 1 for some reason - - self[instance] = referent - return referent - end, -}) - -local globalenv = getgenv and getgenv() or _G - -local StatusGui = globalenv.StatusGui -if not StatusGui then - StatusGui = Instance.new("ScreenGui") - globalenv.StatusGui = StatusGui - - StatusGui.DisplayOrder = 2_000_000_000 - StatusGui.OnTopOfCoreBlur = true - - local function randomString() - local length = math.random(10, 20) - local randomarray = table.create(length) - for i = 1, length do - randomarray[i] = string.char(math.random(32, 126)) - end - return table.concat(randomarray) - end - - if globalcontainer.gethui then - StatusGui.Name = randomString() - StatusGui.Parent = globalcontainer.gethui() - elseif globalcontainer.protectgui and not (is_sirhurt_closure or (syn and DrawingImmediate)) then - StatusGui.Name = randomString() - globalcontainer.protectgui(StatusGui) - StatusGui.Parent = service.CoreGui - else - local RobloxGui = service.CoreGui:FindFirstChild("RobloxGui") - if RobloxGui then - StatusGui.Parent = RobloxGui - else - StatusGui.Name = randomString() - StatusGui.Parent = service.CoreGui - end - end - local StatusText = Instance.new("TextLabel") - globalenv.StatusText = StatusText - StatusText.BackgroundTransparency = 1 - StatusText.Font = Enum.Font.Code - StatusText.AnchorPoint = Vector2.new(1) - StatusText.Position = UDim2.new(1) - StatusText.Size = UDim2.new(0.3, 0, 0, 20) - StatusText.Text = "Starting..." - StatusText.TextColor3 = Color3.new(1, 1, 1) - StatusText.TextScaled = true - StatusText.TextStrokeTransparency = 0.7 - StatusText.TextXAlignment = Enum.TextXAlignment.Right - StatusText.TextYAlignment = Enum.TextYAlignment.Top -end - -local function synsaveinstance(CustomOptions) - table.clear(SharedStrings) - - local total = "" - local savebuffer = {} - local StatusTextClone - - local OPTIONS - OPTIONS = { - mode = "optimized", -- Change this to invalid mode like "custom" if you only want extrainstances; -- ! "optimized" mode is NOT supported with OPTIONS.Object option - noscripts = false, - scriptcache = true, - -- decomptype = "new", -- * Deprecated - timeout = 10, -- Description: If the decompilation run time exceeds this value it gets cancelled. - --* New: - __DEBUG_MODE = false, -- Recommended to enable if you wish to help us improve our products and find bugs / issues with it! - - --Callback = nil, -- Description: If set, the serialized data will be sent to the callback instead of to file. - --Clipboard = false, -- Description: If set to true, the serialized data will be set to the clipboard, which can be later pasted into studio easily. Useful for saving models. - - --[[ Explanation of structure for DecompileIgnore - { - "Chat", - This ignores any instance with "Chat" ClassName - Players = {"MyPlayerName"} - This ignores any descendants of instance with "Players" Class AND "MyPlayerName" Name ONLY - workspace, - This ignores only the this specific instance & it's descendants (Workspace in our case) - } - ]] - DecompileIgnore = { -- * Clean these up (merged Old Syn and New Syn) - service.Chat, - service.TextChatService, - }, -- Scripts inside of these ClassNames will be saved but not decompiled - - IgnoreProperties = {}, - --[[ Explanation of structure for IgnoreList - { - "Chat", - This ignores any instance with "Chat" ClassName - Players = {"MyPlayerName"} - This ignores any descendants of instance with "Players" Class AND "MyPlayerName" Name ONLY - workspace, - This ignores only the this specific instance & it's descendants (Workspace in our case) - StarterPlayer = false, - This saves StarterPlayer without it's descendants - } - ]] - IgnoreList = { service.CoreGui, service.CorePackages }, - - ExtraInstances = {}, -- use mode "invalidmode" to only save these instances - NilInstances = false, -- Description: Save nil instances. - NilInstancesFixes = { - - -- Service = function(instance) end, -- ? Have yet to encounter a case where an Instance with Service Tag is deleted - }, - - ScriptsClasses = { "LocalScript", "ModuleScript", Script = false }, -- Please keep this updated; "= false" means it won't be decompiled - IgnoreDefaults = { - __api_dump_class_not_creatable__ = true, - __api_dump_no_string_value__ = true, - __api_dump_skipped_class__ = true, - -- __api_dump_write_only_property__ = true, -- ? Is this needed - }, - - ShowStatus = true, - 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), Description: Saves everything i Binary Mode (rbxl/rbxm). - -- Decompile = not OPTIONS.noscripts, -- ! This takes priority over OPTIONS.noscripts if set, Description: If true scripts will be decompiled. - -- DecompileTimeout = OPTIONS.timeout, -- ! This takes priority over OPTIONS.timeout if set - IgnoreDefaultProperties = true, -- Description: When enabled it will ignore default properties while saving. - IgnoreNotArchivable = true, -- Description: Ignores the Archivable property and saves Non-Archivable instances. - IgnorePropertiesOfNotScriptsOnScriptsMode = false, -- Ignores property of every instance that is not a script in "scripts" mode - IgnoreSpecialProperties = false, -- true will disable Terrain & Break MeshPart Sizes (very likely) - IsolateStarterPlayer = false, --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 -- Description: The number of decompilation threads that can run at once. More threads means it can decompile for scripts at a time. - -- DisableCompression = false, --Description: Disables compression in the binary output - DecompileJobless = false, --Description: Includes already decompiled code in the output. No new scripts are decompiled. - SaveNonCreatable = false, --Description: Includes non-serializable instances as Folder objects (Name is misleading as this is mostly a fix for certain NilInstances and isn't always related to NotCreatable) - RemovePlayerCharacters = true, -- Description: Ignore player characters while saving. - SavePlayers = false, -- This option does save players, it's just they won't show up in Studio and can only be viewed through the place file code (in text editor). More info at https://github.com/luau/UniversalSynSaveInstance/issues/2 - 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 --! WARNING: Sometimes Properties might not be able to be set to the original value due to circumstances - IgnoreSharedStrings = true, -- ! FIXES CRASHES (TEMPORARY, TESTED ON ROEXEC ONLY); FEEL FREE TO DISABLE THIS TO SEE IF IT WORKS FOR YOU - 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) - - OptionsAliases = { - FilePath = "FileName", - IgnoreDefaultProperties = "IgnoreDefaultProps", - SavePlayers = "IsolatePlayers", - scriptcache = "DecompileJobless", - timeout = "DecompileTimeout", - }, - } - do - local function NilInstanceFixGeneral(Name, ClassName) - return function(instance, InstancePropertyOverrides) - local Exists = OPTIONS.NilInstancesFixes[Name] - - local Fix - - local DoesntExist = not Exists - if DoesntExist then - Fix = Instance.new(ClassName) - OPTIONS.NilInstancesFixes[Name] = Fix - -- Fix.Name = Name - - InstancePropertyOverrides[Fix] = { __Children = { instance }, Properties = { Name = Name } } - else - Fix = Exists - end - - table.insert(InstancePropertyOverrides[Fix].__Children, instance) - -- InstancePropertyOverrides[instance].Parent = AnimationController - if DoesntExist then - return Fix - end - end - end - - OPTIONS.NilInstancesFixes.Animator = NilInstanceFixGeneral( - "Animator has to be placed under Humanoid or AnimationController", - "AnimationController" - ) - - -- TODO: Merge BaseWrap & Attachment & AdPortal fix (put all under MeshPart container) - -- TODO?: - -- DebuggerWatch DebuggerWatch must be a child of ScriptDebugger - -- PluginAction Parent of PluginAction must be Plugin or PluginMenu that created it! - - OPTIONS.NilInstancesFixes.AdPortal = NilInstanceFixGeneral("AdPortal must be parented to a Part", "Part") - OPTIONS.NilInstancesFixes.BaseWrap = - NilInstanceFixGeneral("BaseWrap must be parented to a MeshPart", "MeshPart") - OPTIONS.NilInstancesFixes.Attachment = - NilInstanceFixGeneral("Attachments must be parented to a BasePart or another Attachment", "Part") -- * Bones inherit from Attachments - - local Type = typeof(CustomOptions) - if Type == "table" then - for key, value in CustomOptions do - if OPTIONS[key] == nil then - for Option, Alias in OPTIONS.OptionsAliases do - if key == Alias then - OPTIONS[Option] = value - break - end - end - else - OPTIONS[key] = value - end - end - local Decompile = CustomOptions.Decompile - if Decompile ~= nil then - OPTIONS.noscripts = not Decompile - end - local IgnoreArchivable = CustomOptions.IgnoreArchivable - if IgnoreArchivable ~= nil then - OPTIONS.IgnoreNotArchivable = not IgnoreArchivable - end - local SavePlayerCharacters = CustomOptions.SavePlayerCharacters - if SavePlayerCharacters ~= nil then - OPTIONS.RemovePlayerCharacters = not SavePlayerCharacters - end - elseif Type == "Instance" and CustomOptions ~= game then - CustomOptions = { FilePath = CustomOptions } - else - CustomOptions = {} - end - end - - local InstancePropertyOverrides = {} - - local DecompileIgnore, IgnoreList, IgnoreProperties, ScriptsClasses = - ArrayToDictionary(OPTIONS.DecompileIgnore, "adjust"), - ArrayToDictionary(OPTIONS.IgnoreList, "adjust"), - ArrayToDictionary(OPTIONS.IgnoreProperties, "adjust"), - ArrayToDictionary(OPTIONS.ScriptsClasses, "adjust") - - local __DEBUG_MODE = OPTIONS.__DEBUG_MODE - - local FilePath = OPTIONS.FilePath - local SaveCacheInterval = OPTIONS.SaveCacheInterval - local ToSaveInstance = OPTIONS.Object - - local IgnoreDefaultProperties = OPTIONS.IgnoreDefaultProperties - local IgnoreDefaults = OPTIONS.IgnoreDefaults - local IgnoreNotArchivable = OPTIONS.IgnoreNotArchivable - local IgnorePropertiesOfNotScriptsOnScriptsMode = OPTIONS.IgnorePropertiesOfNotScriptsOnScriptsMode - local IgnoreSpecialProperties = OPTIONS.IgnoreSpecialProperties - - local IsolateLocalPlayer = OPTIONS.IsolateLocalPlayer - local IsolateLocalPlayerCharacter = OPTIONS.IsolateLocalPlayerCharacter - local IsolateStarterPlayer = OPTIONS.IsolateStarterPlayer - local SavePlayers = OPTIONS.SavePlayers - - local SaveNonCreatable = OPTIONS.SaveNonCreatable - - local DecompileJobless = OPTIONS.DecompileJobless - local ScriptCache = OPTIONS.scriptcache - - local Timeout = OPTIONS.timeout - - local AllowResettingProperties = OPTIONS.AllowResettingProperties - local IgnoreSharedStrings = OPTIONS.IgnoreSharedStrings - local SharedStringOverwrite = OPTIONS.SharedStringOverwrite - - local ldeccache = globalenv.scriptcache - - local depth = 0 - local DecompileIgnoring, ToSaveList, ldecompile, placename, elapse_t - - if ToSaveInstance == game then - ToSaveInstance = nil - end - - do - local mode = string.lower(OPTIONS.mode) - local tmp = table.clone(OPTIONS.ExtraInstances) - - local PlaceId = game.PlaceId - if ToSaveInstance then - if mode == "optimized" then -- ! NOT supported with Model file mode - mode = "full" - end - - for _, key in - { - "IsolateLocalPlayerCharacter", - "IsolateStarterPlayer", - "SavePlayers", - "IsolateLocalPlayer", - "NilInstances", - } - do - if not CustomOptions[key] then - OPTIONS[key] = false - end - end - - 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 = (FilePath or "place" .. PlaceId) .. ".rbxlx" - end - - if mode ~= "scripts" then - IgnorePropertiesOfNotScriptsOnScriptsMode = nil - end - - local TempRoot = ToSaveInstance or game - - if mode == "full" then - tmp = TempRoot:GetChildren() - elseif mode == "optimized" then -- ! Incompatible with .rbxmx (Model file) mode - -- if SavePlayers then - -- table.insert(_list_0, "Players") - -- end - for _, x in - { - "Chat", - "InsertService", - "JointsService", - "Lighting", - "MaterialService", - "Players", - "ReplicatedFirst", - "ReplicatedStorage", - "ServerScriptService", -- ? Why - "ServerStorage", -- ? Why - "SoundService", - "StarterGui", - "StarterPack", - "StarterPlayer", - "Teams", - "TextChatService", - "Workspace", - } - do - table.insert(tmp, service[x]) - end - elseif mode == "scripts" then - -- TODO: Only save paths that lead to scripts (nothing else) - -- Currently saves paths along with children of each tree - local unique = {} - for _, instance in TempRoot:GetDescendants() do - if ScriptsClasses[instance.ClassName] then - local Parent = instance.Parent - while Parent and Parent ~= TempRoot do - instance = instance.Parent - Parent = instance.Parent - end - if Parent then - unique[instance] = true - end - end - end - for instance in unique do - table.insert(tmp, instance) - end - end - ToSaveList = tmp - end - - if ScriptCache and not ldeccache then - ldeccache = {} - globalenv.scriptcache = ldeccache - end - - if OPTIONS.noscripts then - ldecompile = function() - return "-- Decompiling is disabled" - end - elseif DecompileHandler then - ldecompile = function(Script) - -- local name = scr.ClassName .. scr.Name - do - if ScriptCache then - local Cached = ldeccache[Script] - if Cached then - return Cached - elseif DecompileJobless then - return "-- Not found in already decompiled ScriptCache" - end - else - task.wait() - end - end - - local ok, result = DecompileHandler(Script, Timeout) - ldeccache[Script] = result -- ? Should we cache even if it timed out? - return ok and result or "--[[ Failed to decompile\nReason:\n" .. (result or "") .. "\n]]" - end - else - ldecompile = function() - return "-- Decompiling is NOT supported on your executor" - end - end - - 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 - - local InstanceOverride = InstancePropertyOverrides[instance] - if InstanceOverride then - local PropertyOverride = InstanceOverride.Properties[PropertyName] - if PropertyOverride then - return PropertyOverride, specialProperties - end - end - - local function FilterResult(Result) -- ? raw == nil thanks to SerializedDefaultAttributes; "can't get value" - "shap" Roexec; "Invalid value for enum " - "StreamingPauseMode" (old games probably) Roexec - return Result == nil - or Result == "can't get value" - or type(Result) == "string" - and (Find(Result, "Unable to get property " .. PropertyName) or Property.Category == "Enum" and Find( - Result, - "Invalid value for enum " - )) - end - - if Special then - if specialProperties == nil and getspecialinfo then - specialProperties = getspecialinfo(instance) - raw = specialProperties[PropertyName] - end - - if raw == nil then - local ok, result = pcall(gethiddenproperty, instance, PropertyName) - - if ok then - raw = result - end - end - - if FilterResult(raw) then - -- * Skip next time we encounter this too perhaps - -- Property.Special = false - -- Property.CanRead = false - - return "__BREAK", specialProperties -- ? We skip it because even if we use "" it will just reset to default in most cases, unless it's a string tag for example (same as not being defined) - end - else - local CanRead = Property.CanRead - if CanRead == nil then - local ok, result = pcall(getsafeproperty, instance, PropertyName) - - if ok then - raw = result - else - if specialProperties == nil and getspecialinfo then - specialProperties = getspecialinfo(instance) - raw = specialProperties[PropertyName] - end - - if raw == nil then - ok, result = pcall(gethiddenproperty, instance, PropertyName) - - if ok then - raw = result - - Property.Special = true - end - else - ok = true - - Property.Special = true - end - end - - Property.CanRead = ok - if not ok or FilterResult(raw) then - return "__BREAK", specialProperties - end - elseif true == CanRead then - raw = instance[PropertyName] - elseif false == CanRead then -- * Skips because we've checked this property before - return "__BREAK", specialProperties - end - end - - return raw, specialProperties - end - - local function ReturnItem(ClassName, instance) - local x = string.rep("\t", depth) - return "\n" - .. x - .. '\n' - .. x - .. "\t" -- TODO: Ideally this shouldn't return as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is Enabled OR If all properties are default (reduces file size by at least 1.4%) - end - local function ReturnProperty(Tag, PropertyName, Value) - return "\n" - .. string.rep("\t", depth + 2) - .. "<" - .. Tag - .. ' name="' - .. PropertyName - .. '">' - .. Value - .. "" - end - - local function ApiFormatify(Value, Category, ValueType, Default) - if Category == "Enum" then - Value = Descriptors.__ENUMNAME(Value) - elseif Category == "Primitive" then - Value = Descriptors[ValueType](Value, Default) - end - return tostring(Value) - end - - local function ReturnValueAndTag(raw, ValueType, Descriptor) - local value, tag = (Descriptor or Descriptors[ValueType])(raw) - - return value, tag == nil and ValueType or tag - end - - local function InheritsFix(Fixes, instance) - for className, fix in Fixes do - if instance:IsA(className) then - return fix - end - end - end - - local function getsizeformat() - local Size - local buffersize = #total - for Index, BinaryPrefix in - { - "B", - "KB", - "MB", - "GB", - "TB", - } - do - if buffersize < 0x400 ^ Index then - Size = math.floor(buffersize / (0x400 ^ (Index - 1)) * 10) / 10 .. " " .. BinaryPrefix - break - end - end - return Size - end - - local function savecache() - local savestr = table.concat(savebuffer) - total ..= savestr - writefile(placename, total) - if StatusTextClone then - StatusTextClone.Text = "Saving.. Size: " .. getsizeformat() - end - savebuffer = {} - task.wait() - end - - local function savespecific(ClassName, Properties) - local Ref = Instance.new(ClassName) - local Item = ReturnItem(Ref.ClassName, Ref) - - for PropertyName, PropertyValue in Properties do - local Class, value, tag - - -- TODO: Improve all sort of overrides & exceptions in the code (code below is awful) - if "Source" == PropertyName then - tag = "ProtectedString" - value = Descriptors.__PROTECTEDSTRING(PropertyValue) - Class = "Script" - elseif "Name" == PropertyName then - Class = "Instance" - local ValueType = ClassList[Class].Properties[PropertyName].ValueType - value, tag = ReturnValueAndTag(PropertyValue, ValueType) - end - - if Class then - Item ..= ReturnProperty(tag, PropertyName, value) - end - end - Item ..= "" - return Item - end - - local function savehierarchy(Hierarchy, Afterwards) - if SaveCacheInterval < #savebuffer then - savecache() - end - depth += 1 - for _, instance in Hierarchy do - if IgnoreNotArchivable and not instance.Archivable then - continue - end - local SkipEntirely = IgnoreList[instance] - if SkipEntirely then - continue - end - - local ClassName = instance.ClassName - local Class = ClassList[ClassName] - if not Class then - continue - end - - local InstanceName = instance.Name - local OnIgnoredList = IgnoreList[ClassName] - if OnIgnoredList and (OnIgnoredList == true or OnIgnoredList[InstanceName]) then - continue - end - - if not DecompileIgnoring then - DecompileIgnoring = DecompileIgnore[instance] - - if DecompileIgnoring == nil then - local DecompileIgnored = DecompileIgnore[ClassName] - DecompileIgnoring = DecompileIgnored - and (DecompileIgnored == true or DecompileIgnored[InstanceName]) - end - - if DecompileIgnoring then - DecompileIgnoring = instance - end - end - local InstanceOverride - if ClassName == "Player" or ClassName == "PlayerScripts" or ClassName == "PlayerGui" then - if SaveNonCreatable then - if InstanceName ~= ClassName then - InstanceOverride = InstancePropertyOverrides[instance] - if not InstanceOverride then - InstanceOverride = { Properties = {} } - InstancePropertyOverrides[instance] = InstanceOverride - end - InstanceOverride.Properties.Name = "[" .. ClassName .. "] " .. InstanceName -- ! Assuming anything that has __Children will have .Properties - end - ClassName = "Folder" - else - continue -- They won't show up in Studio anyway (Enable SavePlayers if you wish to bypass this) - end - end - if not InstanceOverride then - InstanceOverride = InstancePropertyOverrides[instance] - end - local ChildrenOverride = InstanceOverride and InstanceOverride.__Children - - if ChildrenOverride then - table.insert(savebuffer, savespecific(ClassName, InstanceOverride.Properties)) -- ! Assuming anything that has __Children will have .Properties - else - -- local Properties = - table.insert(savebuffer, ReturnItem(ClassName, instance)) -- TODO: Ideally this shouldn't return as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is ENABLED - - if not (IgnorePropertiesOfNotScriptsOnScriptsMode and ScriptsClasses[ClassName] == nil) then - local specialProperties, Replica - - for _, Property in inheritedproperties[ClassName] do - local PropertyName = Property.Name - - if IgnoreProperties[PropertyName] then - continue - end - - local Special = Property.Special - if IgnoreSpecialProperties and Special then - continue - end - - local ValueType = Property.ValueType - - if IgnoreSharedStrings and ValueType == "SharedString" then -- ? More info in Options - continue - end - - local raw - raw, specialProperties = - ReadProperty(Property, instance, PropertyName, specialProperties, Special) - local Old_Property, Old_PropertyName, Old_Special, Old_ValueType - if raw == "__BREAK" then -- ! Assuming __BREAK is always returned when there's a failure to read a property - local Fix = InheritsFix(NotScriptableFixes, instance) -- TODO: Edit this into a NotScriptableFixes[instance] once there is at least one class which can be indexed without IsA - - if Fix then - local PropertyFix = Fix[PropertyName] - if PropertyFix then - Old_Property, Old_PropertyName, Old_Special, Old_ValueType = - Property, PropertyName, Special, ValueType - Property, PropertyName = PropertyFix, PropertyFix.Name - Special, ValueType = Property.Special, Property.ValueType - - raw, specialProperties = - ReadProperty(Property, instance, PropertyName, specialProperties, Special) - - if raw == "__BREAK" then - continue - end - else - continue - end - else - continue - end - end - - 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 Default = Property.Default - - if IgnoreDefaults[Default] then - local ClassTags = ClassList[ClassName].Tags - - local NotCreatable = ClassTags and ClassTags.NotCreatable -- __api_dump_class_not_creatable__ also indicates this - - 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 - - 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 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 - - Default = ApiFormatify(Default, Category, ValueType) - Property.Default = Default - end - elseif Default == "default" and ValueType == "PhysicalProperties" then - Default = "nil" - Property.Default = Default - 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) - -- TODO: tostring(Vector3.new(0/0,math.huge,-math.huge)) returns lowercase Extremes - "nan, inf, -inf" meanwhile for everything else (in xml & dump defaults they seem to be uppercase), this can cause issues later on when a Vector3 Property comes out, default of which will include either of these of Extreme values (Needs to be watched) - -- print("Default not serializing", PropertyName) - - continue - end - end - - -- Serialization start - if Old_Property then - Property, PropertyName, Special, ValueType = - Old_Property, Old_PropertyName, Old_Special, Old_ValueType - 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) - 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 -- ? Roblox doesn't CDATA anything else other than these as far as we know (feel free to prove us wrong) - 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 ScriptsClasses[ClassName] == false then - value = "-- Server scripts can NOT be decompiled" --TODO: Could be not just server scripts in the future - else - if DecompileIgnoring then - value = "-- Ignored" - else - value = ldecompile(instance) - end - end - value = "-- Saved using UniversalSynSaveInstance https://discord.gg/wx4ThpAsmw\n\n" - .. value - end - - value = Descriptors.__PROTECTEDSTRING(value) - -- elseif "UniqueId" == ValueType or "SecurityCapabilities" == ValueType then -- ? Not sure yet - -- tag, value = ValueType, raw - 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[string.sub(ValueType, endIDX + 1)] - - 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 - table.insert(savebuffer, ReturnProperty(tag, PropertyName, value)) - elseif __DEBUG_MODE then - warn("UNSUPPORTED TYPE (OPEN A GITHUB ISSUE): ", ValueType, ClassName, PropertyName) - end - end - end - table.insert(savebuffer, "\n" .. string.rep("\t", depth + 1) .. "") - end - - if SkipEntirely ~= false then -- ? We save instance without it's descendants in this case (== false) - local Children = ChildrenOverride or Afterwards or instance:GetChildren() - - if #Children ~= 0 then - savehierarchy(Children) - end - end - - if DecompileIgnoring and DecompileIgnoring == instance then - DecompileIgnoring = nil - end - - table.insert(savebuffer, "\n" .. string.rep("\t", depth) .. "") - end - depth -= 1 - end - - local function saveextra(Name, Hierarchy, CustomClassName, Source) - table.insert(savebuffer, savespecific((CustomClassName or "Folder"), { Name = Name, Source = Source })) - if Hierarchy then - savehierarchy(Hierarchy) - end - table.insert(savebuffer, "") - end - - local function savegame() - local Starter = '' - if ToSaveInstance then - Starter ..= 'true' - end - table.insert(savebuffer, Starter) --[[ - -- ? Roblox encodes the following additional attributes. These are not required. Moreover, any defined schemas are ignored, and not required for a file to be valid: xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" - Also http can be converted to https but not sure if Roblox would decide to detect that - -- ? nullnil - is a legacy concept that is no longer used. - ]] - - if ToSaveInstance then - savehierarchy({ ToSaveInstance }, ToSaveList) - else - savehierarchy(ToSaveList) - end - - if IsolateLocalPlayer or IsolateLocalPlayerCharacter then - local Players = service.Players - local LocalPlayer = Players.LocalPlayer - if IsolateLocalPlayer then - SaveNonCreatable = true - 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 - -- SaveNonCreatable = true -- TODO: Enable if StarterPlayerScripts or StarterCharacterScripts stop showing up in isolated folder in Studio - saveextra("StarterPlayer", service.StarterPlayer:GetChildren()) - end - - if SavePlayers then - SaveNonCreatable = true - saveextra("Players", service.Players:GetChildren()) - end - - if OPTIONS.NilInstances and globalcontainer.getnilinstances then - local nilinstances = {} - - local NilInstancesFixes = OPTIONS.NilInstancesFixes - - for _, instance in globalcontainer.getnilinstances() do - if instance == game then - instance = nil - -- break - else - local ClassName = instance.ClassName - - local Fix = NilInstancesFixes[ClassName] - if not Fix then -- ! This can cause some Classes to be fixed even though they might not need the fix (better be safe than sorry though); Like Bones inherit from Attachment if we dont define them in the NilInstancesFixes then this will catch them anyways - Fix = InheritsFix(NilInstancesFixes, instance) - end - if Fix then -- * - instance = Fix(instance, InstancePropertyOverrides) - -- continue - end - - -- ? Have yet to encounter a case where an Instance with Service Tag is deleted - -- local Class = ClassList[ClassName] - -- if Class then - -- local ClassTags = Class.Tags - -- if ClassTags and ClassTags.Service then - -- instance.Parent = game - -- instance = nil - -- -- continue - -- end - -- end - end - if instance then - table.insert(nilinstances, instance) - end - end - SaveNonCreatable = true - saveextra("Nil Instances", nilinstances) - end - - 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) 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()` - - If Union and MeshPart collisions don't work, run the script below in the Studio Command Bar: - - - local C = game:GetService("CoreGui") - local D = Enum.CollisionFidelity.Default - - for _, v in game:GetDescendants() do - if v:IsA("TriangleMeshPart") and not v:IsDescendantOf(C) then - v.CollisionFidelity = D - end - end - - If you can't move the Camera, run the scripts in the Studio Command Bar: - - workspace.CurrentCamera.CameraType = Enum.CameraType.Fixed - - This file was generated with the following settings: - ]] - .. service.HttpService:JSONEncode(OPTIONS) - .. "\n\nElapsed time: " - .. os.clock() - elapse_t - .. "\n]]" - ) - end - - local tmp = { "" } - for Identifier, Value in SharedStrings do - table.insert(tmp, '' .. Value .. "") - end - - if 1 < #tmp then -- TODO: This sucks so much because we try to iterate a table just to check this (check above) - table.insert(savebuffer, table.concat(tmp)) - table.insert(savebuffer, "") - end - - table.insert(savebuffer, "") - savecache() - end - - if OPTIONS.ShowStatus then - local Exists = StatusGui:FindFirstChild("TextLabel") - if Exists then - Exists:Destroy() - end - StatusTextClone = globalenv.StatusText:Clone() - StatusTextClone.Parent = StatusGui - end - local Connections - do - local Players = service.Players - local LocalPlayer = Players.LocalPlayer - - if IgnoreList.Model ~= true then - Connections = {} - local function IgnoreCharacter(Player) - table.insert( - Connections, - Player.CharacterAdded:Connect(function(Character) - IgnoreList[Character] = true - end) - ) - - local Character = Player.Character - if Character then - IgnoreList[Character] = true - end - end - - if OPTIONS.RemovePlayerCharacters then - table.insert( - Connections, - Players.PlayerAdded:Connect(function(Player) - IgnoreCharacter(Player) - end) - ) - for _, Player in Players:GetPlayers() do - IgnoreCharacter(Player) - end - elseif IsolateLocalPlayerCharacter then - IgnoreCharacter(LocalPlayer) - end - end - if IsolateLocalPlayer and IgnoreList.Player ~= true then - IgnoreList[LocalPlayer] = true - end - end - - if IsolateStarterPlayer then - IgnoreList.StarterPlayer = false - end - - if SavePlayers then - IgnoreList.Players = false - end - - do - elapse_t = os.clock() - local ok, err = xpcall(savegame, function(err) - return debug.traceback(err) - end) - for _, Connection in Connections do - Connection:Disconnect() - end - if StatusTextClone then - task.spawn(function() - elapse_t = os.clock() - elapse_t - local Log10 = math.log10(elapse_t) - local ExtraTime = 10 - if ok then - StatusTextClone.Text = string.format("Saved! Time %.3f seconds; Size %s", elapse_t, getsizeformat()) - task.wait(Log10 * 2 + ExtraTime) - else - StatusTextClone.Text = "Failed! Check F9 console for more info" - warn("Error found while saving:") - warn(err) - task.wait(Log10 + ExtraTime) - end - StatusTextClone:Destroy() - end) - end - end -end - -synsaveinstance({ __DEBUG_MODE = true }) diff --git a/saveinstance.lua b/saveinstance.lua deleted file mode 100644 index 5540d0b..0000000 --- a/saveinstance.lua +++ /dev/null @@ -1,1506 +0,0 @@ ---!native -local Params = { - RepoURL = "https://raw.githubusercontent.com/luau/SomeHub/main/", - UMF = "UniversalMethodFinder", - ROM = "RequireOnlineModule", -} -local finder, globalcontainer = loadstring(game:HttpGet(Params.RepoURL .. Params.UMF .. ".luau", true), Params.UMF)() - -finder({ - -- readbinarystring = 'string.find(...,"bin",nil,true)', -- ! Could match some unwanted stuff - decompile = '(string.find(...,"decomp",nil,true) and string.sub(...,#...) ~= "s") or string.find(...,"assembl",nil,true)', - gethiddenproperty = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) ~= "s"', - gethiddenproperties = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) == "s"', - sethiddenproperty = 'string.find(...,"set",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) ~= "s"', - gethui = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"ui",nil,true)', - getnilinstances = 'string.find(...,"nil",nil,true)', -- ! Could match some unwanted stuff - getproperties = 'string.find(...,"get",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) == "s"', - getspecialinfo = 'string.find(...,"get",nil,true) and string.find(...,"spec",nil,true)', - protectgui = 'string.find(...,"protect",nil,true) and string.find(...,"ui",nil,true) and not string.find(...,"un",nil,true)', - writefile = 'string.find(...,"file",nil,true) and string.find(...,"write",nil,true)', -}, true) - -local decompile = globalcontainer.decompile -local gethiddenproperty = globalcontainer.gethiddenproperty -local sethiddenproperty = globalcontainer.sethiddenproperty -local writefile = globalcontainer.writefile - -if not globalcontainer.getspecialinfo then - globalcontainer.getspecialinfo = globalcontainer.gethiddenproperties -end - -local function Find(String, Pattern) - return string.find(String, Pattern, nil, true) -end - -local GlobalSettings, GlobalBasicSettings = settings(), UserSettings() -local service = setmetatable({}, { - __index = function(Self, Name) - local Service = game:GetService(Name) or GlobalSettings:GetService(Name) or GlobalBasicSettings:GetService(Name) - Self[Name] = Service - return Service - end, -}) - -local EscapesPattern = "[%z\1-\8\11-\12\14-\31\127-\191\194-\244<>\"'&]" --- Order from: https://create.roblox.com/docs/en-us/ui/rich-text#escape-forms -local Escapes = { - ["<"] = "<", - [">"] = ">", - ['"'] = """, - ["'"] = "'", - ["&"] = "&", -} - -for rangeStart, rangeEnd in string.gmatch(EscapesPattern, "(.)%-(.)") do - for charCode = string.byte(rangeStart), string.byte(rangeEnd) do - Escapes[string.char(charCode)] = "&#" .. charCode .. ";" - end -end - -local Base64_Encode -do - if not bit32.byteswap or not pcall(bit32.byteswap, 1) then -- Because Fluxus is missing byteswap - bit32 = table.clone(bit32) - - local function tobit(num) - num = num % (bit32.bxor(num, 32)) - if num > 0x80000000 then - num = num - bit32.bxor(num, 32) - end - return num - end - - bit32.byteswap = function(num) - local BYTE_SIZE = 8 - local MAX_BYTE_VALUE = 255 - - num = num % bit32.bxor(2, 32) - - local a = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local b = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local c = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local d = bit32.band(num, MAX_BYTE_VALUE) - num = tobit(bit32.lshift(bit32.lshift(bit32.lshift(a, BYTE_SIZE) + b, BYTE_SIZE) + c, BYTE_SIZE) + d) - return num - end - - table.freeze(bit32) - end - local Base64_Encode_Buffer = loadstring( - game:HttpGet("https://raw.githubusercontent.com/Reselim/Base64/master/Base64.lua", true), - "Base64" -)().encode -Base64_Encode = function(raw) -- ? Reselim broke all scripts that rely on their Base64 Implementation because they changed to buffers from strings (both as input & output) - return raw == "" and raw or buffer.tostring(Base64_Encode_Buffer(buffer.fromstring(raw))) - end -end - -local SharedStrings = {} -local sharedstrings = setmetatable({ - identifier = 1e15, -- 1 quadrillion, up to 9.(9) quadrillion, in theory this shouldn't ever run out and be enough for all sharedstrings ever imaginable - -- TODO: worst case, add fallback to randomizer once numbers run out : ) -}, { - __index = function(self, String) - local Identifier = self.identifier - self.identifier = Identifier + 1 - Identifier = Base64_Encode(Identifier .. "") - self[String] = Identifier -- Todo: The value of the md5 attribute is a Base64-encoded key. type elements use this key to refer to the value of the string. The value is the text content, which is Base64-encoded. Historically, the key was the MD5 hash of the string value. However, this is not required; the key can be any value that will uniquely identify the shared string. Roblox currently uses BLAKE2b truncated to 16 bytes.. We probably need to do that too for sake of safety - return Identifier - end, -}) - -local Descriptors -Descriptors = { - __APIPRECISION = function(raw, default) - if raw == 0 or raw % 1 == 0 then - return raw - end - - local Extreme = Descriptors.__EXTREMIFY(raw) - if Extreme then - return Extreme - end - - local precision - if type(default) == "string" then - local dotIndex = Find(default, ".") - - if dotIndex then - precision = #default - dotIndex - end - else - precision = default - end - if precision then - -- TODO: scientific notation formatting also takes place if value is a decimal (only counts if it starts with 0.) then values like 0.00008 will be formatted as 8.0000000000000006544e-05 ("%.19e"), it must have 5 or more consecutive (?) zeros for this, on other hand, if it doesn't start with 0. then e20+ format is applied in case it has 20 or more consecutive (?) zeros so 1e20 will be formatted as 1e+20 and upwards (1e+19 is not allowed, same as 1e-04 for decimals) - -- ? The good part is compression of value so less file size BUT at the potential cost of precision loss - - return string.format("%." .. precision .. "f", raw) - end - - return raw - end, - __BINARYSTRING = Base64_Encode, - __BIT = function(...) -- * Credits to Friend (you know yourself) - local Value = 0 - local PackedArgs = { ... } - for Index = 1, #PackedArgs do - local Bit = PackedArgs[Index] - if Bit then - Value = Value + 2 ^ (Index - 1) - end - end - - return Value - end, - __CDATA = function(raw) - return "" - end, - __ENUM = function(raw) - return raw.Value, "token" - end, - __ENUMNAME = function(raw) - return raw.Name - end, - __EXTREMIFY = function(raw) - local Extreme - if raw ~= raw then - Extreme = "NAN" - elseif raw == math.huge then - Extreme = "INF" - elseif raw == -math.huge then - Extreme = "-INF" - end - - return Extreme - end, - __PROTECTEDSTRING = function(raw) - return Find(raw, "]]>") and Descriptors.string(raw) or Descriptors.__CDATA(raw) -- ? its purpose is to "protect" data from being treated as ordinary character data during processing - end, - __VECTOR = function(X, Y, Z) - local Value = "" .. X .. "" .. Y .. "" -- There is no Vector without at least two Coordinates.. (Vector1, at least on Roblox) - - if Z then - Value = Value .. "" .. Z .. "" - end - - return Value - end, - Axes = function(raw) - --The text of this element is formatted as an integer between 0 and 7 - return "" .. Descriptors.__BIT(raw.X, raw.Y, raw.Z) .. "" - end, - -- BinaryString = function(raw) - - -- end - BrickColor = function(raw) -- ! Oh well This might hurt Color3 / Color3uint8 properties - return raw.Number, "BrickColor" -- * Roblox encodes the tags as "int", but this is not required for Roblox to properly decode the type. For better compatibility, it is preferred that third-party implementations encode and decode "BrickColor" tags instead. - end, - CFrame = function(raw) - local X, Y, Z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = raw:GetComponents() - return Descriptors.__VECTOR(X, Y, Z) - .. "" - .. R00 - .. "" - .. R01 - .. "" - .. R02 - .. "" - .. R10 - .. "" - .. R11 - .. "" - .. R12 - .. "" - .. R20 - .. "" - .. R21 - .. "" - .. R22 - .. "", - "CoordinateFrame" - end, - Color3 = function(raw) - return "" .. raw.R .. "" .. raw.G .. "" .. raw.B .. "" -- ? It is recommended that Color3 is encoded with elements instead of text. - end, - -- Color3uint8 = function(raw) --? Not sure yet - -- local R_255 = math.floor(raw.R * 255) - -- local G_255 = math.floor(raw.G * 255) - -- local B_255 = math.floor(raw.B * 255) - - -- return 0xFF000000 + (R_255 * 65536) + (G_255 * 256) + B_255 -- ? It is recommended that Color3uint8 is encoded with text instead of elements. - -- end, - ColorSequence = function(raw) - --The value is the text content, formatted as a space-separated list of FLOATing point numbers. - - local Converted = "" - local Keypoints = raw.Keypoints - for Index = 1, #Keypoints do - local v = Keypoints[Index] - local Value = v.Value - Converted = Converted .. v.Time .. " " .. Value.R .. " " .. Value.G .. " " .. Value.B .. " 0 " -- * " 0" is Envelope: Has the range 0 - 1. Currently unused by Roblox. - end - - return Converted - end, - Content = function(raw) - return raw == "" and "" or "" .. Descriptors.string(raw, true) .. "" - end, - CoordinateFrame = function(raw) - return "" .. Descriptors.CFrame(raw) .. "" - end, - Faces = function(raw) - -- The text of this element is formatted as an integer between 0 and 63 - return "" - .. Descriptors.__BIT(raw.Right, raw.Top, raw.Back, raw.Left, raw.Bottom, raw.Front) - .. "" - end, - Font = function(raw) - return "" - .. Descriptors.Content(raw.Family) - .. "" - .. Descriptors.__ENUM(raw.Weight) - .. "" --TODO: Figure out how to determine (Content) rbxasset://fonts/GothamSSm-Medium.otf - end, - NumberRange = function(raw) - --The value is the text content, formatted as a space-separated list of floating point numbers. - return raw.Min .. " " .. raw.Max --[[.. " "]] -- ! This might be required to bypass detections as thats how its formatted usually - end, - NumberSequence = function(raw) - --The value is the text content, formatted as a space-separated list of floating point numbers. - local Converted = "" - local Keypoints = raw.Keypoints - for Index = 1, #Keypoints do - local v = Keypoints[Index] - Converted = Converted .. v.Time .. " " .. v.Value .. " " .. v.Envelope .. " " - end - - return Converted - end, - PhysicalProperties = function(raw) - --[[Contains at least one CustomPhysics element, which is interpreted according to the bool type. If this value is true, then the tag also contains an element for each component of the PhysicalProperties: - - Density - Friction - Elasticity - FrictionWeight - ElasticityWeight - -The value of each component is represented by the text content formatted as a 32-bit floating point number (see float).]] - local CustomPhysics - if raw then - CustomPhysics = true - else - CustomPhysics = false - end - CustomPhysics = "" .. Descriptors.bool(CustomPhysics) .. "" - - return raw - and CustomPhysics .. "" .. raw.Density .. "" .. raw.Friction .. "" .. raw.Elasticity .. "" .. raw.FrictionWeight .. "" .. raw.ElasticityWeight .. "" - or CustomPhysics - end, - -- ProtectedString = function(raw) - -- return tostring(raw), "ProtectedString" - -- end, - Ray = function(raw) - local vector3 = Descriptors.Vector3 - - return "" .. vector3(raw.Origin) .. "" .. vector3(raw.Direction) .. "" - end, - Rect = function(raw) - local vector2 = Descriptors.Vector2 - - return "" .. vector2(raw.Min) .. "" .. vector2(raw.Max) .. "", "Rect2D" - end, - -- Region3 = function(raw) --? Not sure yet - - -- return "???" - -- end, - -- Region3int16 = function(raw) --? Not sure yet - -- local vector3int16 = Descriptors.Vector3int16 - - -- return "" .. vector3int16(raw.Min) .. "" .. vector3int16(raw.Max) .. "" - -- end, - SharedString = function(raw) - raw = Base64_Encode(raw) - - local Identifier = sharedstrings[raw] - - if SharedStrings[Identifier] == nil then - SharedStrings[Identifier] = raw - end - - return Identifier - end, -- TODO: Add Support for this https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md#sharedstring - UDim = function(raw) - --[[ - S: Represents the Scale component. Interpreted as a . - O: Represents the Offset component. Interpreted as an . - ]] - - return "" .. raw.Scale .. "" .. raw.Offset .. "" - end, - UDim2 = function(raw) - --[[ - XS: Represents the X.Scale component. Interpreted as a . - XO: Represents the X.Offset component. Interpreted as an . - YS: Represents the Y.Scale component. Interpreted as a . - YO: Represents the Y.Offset component. Interpreted as an . - ]] - - local X, Y = raw.X, raw.Y - - return "" - .. X.Scale - .. "" - .. X.Offset - .. "" - .. Y.Scale - .. "" - .. Y.Offset - .. "" - end, - --UniqueId = function(raw) - --[[ - # UniqueId properties might be random everytime Studio saves a place file - # and don't have a use right now outside of packages, which SSI doesn't - # account for anyway. They generate diff noise, so we shouldn't serialize - # them until we have to. - ]] - -- return -- ? No idea if this even needs a Descriptor - -- end, - Vector2 = function(raw) - --[[ - X: Represents the X component. Interpreted as a . - Y: Represents the Y component. Interpreted as a . - ]] - return Descriptors.__VECTOR(raw.X, raw.Y) - end, - Vector2int16 = function(raw) - --[[ - X: Represents the X component. Interpreted as an . - Y: Represents the Y component. Interpreted as an . - ]] - return Descriptors.__VECTOR(raw.X, raw.Y) - end, - Vector3 = function(raw) - --[[ - X: Represents the X component. Interpreted as a . - Y: Represents the Y component. Interpreted as a . - Z: Represents the Z component. Interpreted as a . - ]] - return Descriptors.__VECTOR(raw.X, raw.Y, raw.Z) - end, - Vector3int16 = function(raw) - --[[ - X: Represents the X component. Interpreted as an . - Y: Represents the Y component. Interpreted as an . - Z: Represents the Z component. Interpreted as an . - ]] - return Descriptors.__VECTOR(raw.X, raw.Y, raw.Z) - end, - bool = function(raw) - return tostring(raw) - end, - - double = function(raw, default) - return Descriptors.__APIPRECISION(raw, default or 17) --? A precision of at least 17 is required to properly represent a 64-bit floating point value, so this amount is recommended. - end, -- ? wouldn't float be better as an optimization - float = function(raw, default) - return Descriptors.__APIPRECISION(raw, default or 9) -- ? A precision of at least 9 is required to properly represent a 32-bit floating point value, so this amount is recommended. - end, - string = function(raw, skipcheck) - return not skipcheck and raw == "" and raw or raw:gsub(EscapesPattern, Escapes) - end, -} - -for _, StaysRaw in - next, - { - "int", - "int64", - } -do - Descriptors[StaysRaw] = function(raw) - return Descriptors.__EXTREMIFY(raw) or raw - end -end - -for _, originalfuncname in next, { "getproperties", "getspecialinfo" } do -- * Some executors only allow certain Classes for this method (like UnionOperation, MeshPart, Terrain), for example Electron, Codex -local originalfunc = globalcontainer[originalfuncname] - if originalfunc then - globalcontainer[originalfuncname] = function(instance) - local ok, result = pcall(originalfunc, instance) - return ok and result or {} -end - end -end - -local getproperties = globalcontainer.getproperties - -if getproperties then - if globalcontainer.getspecialinfo then - local old_getspecialinfo = globalcontainer.getspecialinfo - - globalcontainer.getspecialinfo = function(instance) - local specialinfo = getproperties(instance) - - for Property, Value in next, old_getspecialinfo(instance) do - specialinfo[Property] = Value - end - - return specialinfo - end - else - globalcontainer.getspecialinfo = getproperties - end -end - -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 - - local function FilterResult(Result) -- ? raw == nil thanks to SerializedDefaultAttributes; "can't get value" - "shap" Roexec; "Invalid value for enum " - "StreamingPauseMode" (old games probably) Roexec - return Result == nil - or Result == "can't get value" - or type(Result) == "string" - and (Find(Result, "Unable to get property " .. PropertyName) or Property.Category == "Enum" and Find( - Result, - "Invalid value for enum " - )) - end - - if Special then - if specialProperties == nil and getspecialinfo then - specialProperties = getspecialinfo(instance) - raw = specialProperties[PropertyName] - end - - if raw == nil then - local ok, result = pcall(gethiddenproperty, instance, PropertyName) - - if ok then - raw = result - end - end - - if FilterResult(raw) then - -- * Skip next time we encounter this too perhaps - -- Property.Special = false - -- Property.CanRead = false - - return "__BREAK", specialProperties -- ? We skip it because even if we use "" it will just reset to default in most cases, unless it's a string tag for example (same as not being defined) - end - else - local CanRead = Property.CanRead - if CanRead == nil then - local ok, result = pcall(getsafeproperty, instance, PropertyName) - - if ok then - raw = result - else - if specialProperties == nil and getspecialinfo then - specialProperties = getspecialinfo(instance) - raw = specialProperties[PropertyName] - end - - if raw == nil then - ok, result = pcall(gethiddenproperty, instance, PropertyName) - - if ok then - raw = result - - Property.Special = true - end - else - ok = true - - Property.Special = true - end - end - - Property.CanRead = ok - if not ok or FilterResult(raw) then - return "__BREAK", specialProperties - end - elseif true == CanRead then - raw = instance[PropertyName] - elseif false == CanRead then -- * Skips because we've checked this property before - return "__BREAK", specialProperties - end - end - return raw, specialProperties -end - -local ldeccache = {} - -local function ArrayToDictionary(Table, HybridMode) - local tmp = table.create(#Table) - - if HybridMode == "table" then - for Some1, Some2 in next, 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 next, Table do - if type(Some1) == "number" then - tmp[Some2] = true - else - tmp[Some1] = Some2 - end - end - else - for _, Key in next, Table do - tmp[Key] = true - end - end - - return tmp -end -local ClassPropertiesBlacklist = - { GuiObject = { "Transparency" }, Instance = { "Parent" }, BasePart = { "BrickColor" }, Attachment = {"WorldCFrame"} } -- GuiObject.Transparency is almost always 1 meaning everything will be transparent; Instance.Parent is useless in xml (no idea about binary); BasePart.BrickColor hurts other Color3 properties; Attachment.WorldCFrame breaks Attachment.CFrame (and is hidden anyway) - -for Class, Properties in next, ClassPropertiesBlacklist do - ClassPropertiesBlacklist[Class] = ArrayToDictionary(Properties) -end -local function FetchAPI() - local API_Dump_Url = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json" - local API_Dump = game:HttpGet(API_Dump_Url, true) - local API_Classes = service.HttpService:JSONDecode(API_Dump).Classes - - local ClassList = {} - - for _index_0 = 1, #API_Classes do - local API_Class = API_Classes[_index_0] - local ClassMembers = API_Class.Members - - local Class = {} - - local ClassName = API_Class.Name - - local ClassTags = API_Class.Tags - - if ClassTags then - ClassTags = ArrayToDictionary(ClassTags) - end - - -- ClassInfo.Name = ClassName - Class.Tags = ClassTags -- or {} - Class.Superclass = API_Class.Superclass - - local ClassProperties = {} - - for _index_1 = 1, #ClassMembers do - local Member = ClassMembers[_index_1] - if Member.MemberType == "Property" then - local PropertyName = Member.Name - - -- ? We avoid this as some Instances like services may have this property locked and thus make file unable to open and it's not even used by Roblox anyways as Parent-Child relationship is done by embedding/nesting - - local Serialization = Member.Serialization - - if Serialization.CanLoad then - local Blacklisted = ClassPropertiesBlacklist[ClassName] - if not (Blacklisted and Blacklisted[PropertyName]) then - local Allowed = true - - local MemberTags = Member.Tags - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - - Special = MemberTags.NotScriptable - - if MemberTags.Deprecated then - Allowed = nil - end - end - if Allowed then - local ValueType = Member.ValueType - - local Property = { - Name = PropertyName, - Category = ValueType.Category, - Default = Member.Default, - -- Tags = MemberTags, - ValueType = ValueType.Name, - } - - if Special then - Property.Special = true - end - - ClassProperties[PropertyName] = Property - end - end - end - end - end - - Class.Properties = ClassProperties - - ClassList[ClassName] = Class - end - - -- ClassList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering throguh properties to remove this - - return ClassList -end -local ClassList -do - local ok, result = pcall(FetchAPI) - if ok then - ClassList = result - else - warn(result) - return - end -end - -local rswait = service.RunService.RenderStepped -local function rwait() - rswait:Wait() -end - -local inheritedproperties = setmetatable({}, { - __index = function(self, ClassName) - local proplist = {} - local layer = ClassList[ClassName] - while layer do - local _list_0 = layer.Properties - - -- proplist = table.move(_list_0, 1, #_list_0, #proplist + 1, proplist) - for _, p in next, _list_0 do - p = table.clone(p) - -- p.Name = PropertyName - table.insert(proplist, p) - end - - layer = ClassList[layer.Superclass] - end - table.sort( - proplist, - function(a, b) -- ? We do this as properties are always alphabetically sorted normally so Roblox might be able to detect us opening saveinstanced file if we don't do this - return a.Name < b.Name - end - ) - - self[ClassName] = proplist - - return proplist - end, -}) - -local classreplicas = setmetatable({}, { - __index = function(self, ClassName) - local Replica = Instance.new(ClassName) -- ! Might need to pcall this - self[ClassName] = Replica - return Replica - end, -}) - -local referents = setmetatable({ - identifier = 0, -}, { - __index = function(self, instance) - local Count = self.identifier - self.identifier = Count + 1 - local referent = "RBX" .. Count -- Todo: Roblox encodes all elements with a referent attribute. Each value is generated by starting with the prefix RBX, followed by a UUID version 4, with - characters removed, and all characters converted to uppercase. We probably need to do that too for sake of safety - self[instance] = referent - return referent - end, -}) - -local function ReturnItem(ClassName, instance) - return '' -- TODO: Ideally this shouldn't return 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 .. "" -end - -local function ApiFormatify(Value, Category, ValueType, Default) - if Category == "Enum" then - Value = Descriptors.__ENUMNAME(Value) - elseif Category == "Primitive" then - Value = Descriptors[ValueType](Value, Default) - end - return tostring(Value) -end - -local function ReturnValueAndTag(raw, ValueType, Descriptor) - local value, tag = (Descriptor or Descriptors[ValueType])(raw) - - return value, tag == nil and ValueType or tag -end - -local BlacklistedDefaults = { - __api_dump_class_not_creatable__ = true, - __api_dump_no_string_value__ = true, - __api_dump_skipped_class__ = true, - -- __api_dump_write_only_property__ = true, -- ? Is this needed -} - -local StatusGui = Instance.new("ScreenGui") -StatusGui.DisplayOrder = 2_000_000_000 -StatusGui.OnTopOfCoreBlur = true - -local function randomString() - local randomarray = {} - for i = 1, math.random(10, 20) do - randomarray[i] = string.char(math.random(32, 126)) - end - return table.concat(randomarray) -end -if globalcontainer.gethui then - StatusGui.Name = randomString() - StatusGui.Parent = globalcontainer.gethui() -elseif globalcontainer.protectgui and not (is_sirhurt_closure or (syn and DrawingImmediate)) then - StatusGui.Name = randomString() - globalcontainer.protectgui(StatusGui) - StatusGui.Parent = service.CoreGui -else - local RobloxGui = service.CoreGui:FindFirstChild("RobloxGui") - if RobloxGui then - StatusGui.Parent = RobloxGui - else - StatusGui.Name = randomString() - StatusGui.Parent = service.CoreGui - end -end -local StatusText = Instance.new("TextLabel") -StatusText.BackgroundTransparency = 1 -StatusText.Font = Enum.Font.Code - -StatusText.AnchorPoint = Vector2.new(1) -StatusText.Position = UDim2.new(1) -StatusText.Size = UDim2.new(0.3, 0, 0, 20) -StatusText.Text = "Starting..." -StatusText.TextColor3 = Color3.new(1, 1, 1) -StatusText.TextScaled = true -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) - - local total = "" - local savebuffer = {} - local StatusTextClone - - local OPTIONS = { - mode = "optimized", -- Change this to invalid mode like "custom" if you only want extrainstances; -- ! "optimized" mode is NOT supported with OPTIONS.Object option - noscripts = false, - scriptcache = true, - -- decomptype = "new", -- * Deprecated - timeout = 30, -- Description: If the decompilation run time exceeds this value it gets cancelled. - --* New: - __DEBUG_MODE = false, -- Recommended to enable if you wish to help us improve our products and find bugs / issues with it! ---Callback = nil, -- Description: If set, the serialized data will be sent to the callback instead of to file. - --Clipboard = false, -- Description: If set to true, the serialized data will be set to the clipboard, which can be later pasted into studio easily. Useful for saving models. - DecompileIgnore = { -- * Clean these up (merged Old Syn and New Syn) - "Chat", - "TextChatService", - }, -- Scripts inside of these ClassNames 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 - } - ]] -PropertiesBlacklist = {}, - 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 = false, -- Description: Save nil instances. - NilInstancesFixes = { - Animator = function(instance) - local AnimationController = Instance.new("AnimationController") - AnimationController.Name = "Animator has to be placed under Humanoid or AnimationController" - instance:Clone().Parent = AnimationController - return AnimationController - end, - }, - ShowStatus = true, - 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), Description: Saves everything i Binary Mode (rbxl/rbxm). - -- Decompile = not OPTIONS.noscripts, -- ! This takes priority over OPTIONS.noscripts if set, Description: If true scripts will be decompiled. - -- DecompileTimeout = OPTIONS.timeout, -- ! This takes priority over OPTIONS.timeout if set - IgnoreDefaultProperties = true, -- Description: When enabled it will ignore default properties while saving. - IgnoreNotArchivable = true, - IgnorePropertiesOfNotScriptsOnScriptsMode = false, -- Ignores property of every instance that is not a script in "scripts" mode - IgnoreSpecialProperties = false, -- true will disable Terrain & Break MeshPart Sizes (very likely) - IsolateStarterPlayer = false, --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 -- Description: The number of decompilation threads that can run at once. More threads means it can decompile for scripts at a time. - RemovePlayerCharacters = true, -- Description: Ignore player characters while saving. - SavePlayers = false, -- This option does save players, it's just they won't show up in Studio and can only be viewed through the place file code (in text editor). More info at https://github.com/luau/UniversalSynSaveInstance/issues/2 - 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 --! WARNING: Sometimes Properties might not be able to be set to the original value due to circumstances -IgnoreSharedStrings = true, -- ! FIXES CRASHES (TEMPORARY, TESTED ON ROEXEC ONLY); FEEL FREE TO DISABLE THIS TO SEE IF IT WORKS FOR YOU - 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) - } - - if type(CustomOptions) == "table" then - for key, value in next, CustomOptions do - if OPTIONS[key] ~= nil then - OPTIONS[key] = value - end - end - local Decompile = CustomOptions.Decompile - if Decompile ~= nil then - OPTIONS.noscripts = not Decompile - end - local DecompileTimeout = CustomOptions.DecompileTimeout - if DecompileTimeout ~= nil then - OPTIONS.timeout = DecompileTimeout - end - local IgnoreDefaultProps = CustomOptions.IgnoreDefaultProps - if IgnoreDefaultProps ~= nil then - OPTIONS.IgnoreDefaultProperties = IgnoreDefaultProps - end - else - CustomOptions = {} - end - local Timeout = OPTIONS.timeout - local ldecompile - - if OPTIONS.noscripts then - ldecompile = function() - return "-- Decompiling is disabled" - end - elseif decompile then - ldecompile = function(Script) - -- local name = scr.ClassName .. scr.Name - do - if OPTIONS.scriptcache then - local Cached = ldeccache[Script] - if Cached then - return Cached - end - else - rwait() - end - end - local ok, result = pcall(decompile, Script, Timeout, Timeout) -- ! This might break on Syn due to second param being bool or string (deprecated tho) - ldeccache[Script] = result - return ok and result or "--[[Failed to decompile\nReason:\n" .. (result or "") .. "\n]]" - end - else - ldecompile = function() - return "-- Decompiling is NOT supported on your executor" - end - end - local ToSaveInstance = OPTIONS.Object - - if ToSaveInstance == game then - ToSaveInstance = nil - end - local FilePath = OPTIONS.FilePath - local IgnorePropertiesOfNotScriptsOnScriptsMode = OPTIONS.IgnorePropertiesOfNotScriptsOnScriptsMode - local placename - - local ToSaveList - do - local mode = string.lower(OPTIONS.mode) - local tmp = table.clone(OPTIONS.ExtraInstances) - - local PlaceId = game.PlaceId - if ToSaveInstance then - if mode == "optimized" then -- ! NOT supported with Model file mode - mode = "full" - end - - for _, key in next, - { - "IsolateLocalPlayerCharacter", - "IsolateStarterPlayer", - "IsolateLocalPlayer", - "NilInstances", - } - do - if not CustomOptions[key] then - OPTIONS[key] = false -end - end - - 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 = (FilePath or "place" .. PlaceId) .. ".rbxlx" - end - - if mode ~= "scripts" then - IgnorePropertiesOfNotScriptsOnScriptsMode = nil - end - - local TempRoot = ToSaveInstance or game - - if mode == "full" then - tmp = TempRoot:GetChildren() - elseif mode == "optimized" then -- ! Incompatible with .rbxmx (Model file) mode - local _list_0 = { - "Chat", - "InsertService", - "JointsService", - "Lighting", - "MaterialService", - "ReplicatedFirst", - "ReplicatedStorage", - "ServerStorage", -- ? Why - "ServerScriptService", -- ? Why - "SoundService", - "StarterGui", - "StarterPack", - "StarterPlayer", - "Teams", - "TextChatService", - "Workspace", - } - if OPTIONS.SavePlayers then - table.insert(_list_0, "Players") - end - for _index_0 = 1, #_list_0 do - local x = _list_0[_index_0] - table.insert(tmp, service[x]) - end - elseif mode == "scripts" then - local Hierarchy = TempRoot:GetDescendants() - local cach = {} - for _index_0 = 1, #Hierarchy do - local instance = Hierarchy[_index_0] - - if ScriptsClasses[instance.ClassName] then - local Parent = instance.Parent - while Parent and Parent ~= TempRoot do - instance = instance.Parent - Parent = instance.Parent - end - if Parent then - cach[instance] = true - end - end - end - for instance in next, cach do - table.insert(tmp, instance) - end - end - ToSaveList = tmp - end - - local DecompileIgnore, InstancesBlacklist, PropertiesBlacklist = - ArrayToDictionary(OPTIONS.DecompileIgnore, "table"), - ArrayToDictionary(OPTIONS.InstancesBlacklist, "table"), - ArrayToDictionary(OPTIONS.PropertiesBlacklist, "table") - - 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 IgnoreSharedStrings = OPTIONS.IgnoreSharedStrings - - local function getsizeformat() - local Size - local BinaryPrefixes = { - "B", - "KB", - "MB", - "GB", - "TB", - } - for Index = 1, #BinaryPrefixes do - local buffersize = #total - if buffersize < 0x400 ^ Index then - Size = math.floor(buffersize / (0x400 ^ (Index - 1)) * 10) / 10 .. " " .. BinaryPrefixes[Index] - break - end - end - return Size - end - - local function savecache() - local savestr = table.concat(savebuffer) - total = total .. savestr - writefile(placename, total) - if StatusTextClone then - StatusTextClone.Text = "Saving.. Size: " .. getsizeformat() - end - savebuffer = {} - rwait() - end - local DecompileIgnoring - local function savehierarchy(Hierarchy, Afterwards) - if SaveCacheInterval < #savebuffer then - savecache() - end - for _index_0 = 1, #Hierarchy do - repeat - local instance = Hierarchy[_index_0] - - 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 - break - end - - if not DecompileIgnoring then - local DecompileIgnored = DecompileIgnore[ClassName] - DecompileIgnoring = DecompileIgnored - and (DecompileIgnored == true or DecompileIgnored[InstanceName]) - and instance - end - - local Properties = inheritedproperties[ClassName] - savebuffer[#savebuffer + 1] = ReturnItem(ClassName, instance) -- TODO: Ideally this shouldn't return 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 - if PropertiesBlacklist[PropertyName] then - break - end - local Special = Property.Special - if IgnoreSpecialProperties and Special then - break - end - - local ValueType = Property.ValueType - - if IgnoreSharedStrings and ValueType == "SharedString" then -- ? More info in Options - break - end - - local raw - raw, specialProperties = - ReadProperty(Property, instance, PropertyName, specialProperties, Special) - if raw == "__BREAK" then - break - end - - 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 - break - end - - local Default = Property.Default - - if BlacklistedDefaults[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 - - 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 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 - - Default = ApiFormatify(Default, Category, ValueType) - Property.Default = Default - -- if Property.Special then - -- end - end - elseif Default == "default" and ValueType == "PhysicalProperties" then - Default = "nil" - Property.Default = Default - 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 - 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) - 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 -- ? Roblox doesn't CDATA anything else other than these as far as we know (feel free to prove us wrong) - 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 ScriptsClasses[ClassName] == false then - value = "-- Server scripts can NOT be decompiled" --TODO: Could be not just server scripts in the future - else - if DecompileIgnoring then - value = "-- Ignored" - else - 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" - - Descriptor = Descriptors[string.sub(ValueType, endIDX + 1)] - - 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 - end - savebuffer[#savebuffer + 1] = "" - local Children = Afterwards or instance:GetChildren() - if #Children ~= 0 then - savehierarchy(Children) - end - - if DecompileIgnoring and DecompileIgnoring == instance then - DecompileIgnoring = nil - end - - savebuffer[#savebuffer + 1] = "" - - until true - end - end - local function saveextra(Name, Hierarchy, CustomClassName, Source) - local Ref = Instance.new(CustomClassName or "Folder") - - local PropertyName = "Name" - local ValueType = ClassList.Instance.Properties[PropertyName].ValueType - local value, tag = ReturnValueAndTag(Name, ValueType) - - local Buffer = ReturnItem(Ref.ClassName, Ref) .. ReturnProperty(tag, PropertyName, value) - - if Source then - Buffer = Buffer .. ReturnProperty("ProtectedString", "Source", Descriptors.__PROTECTEDSTRING(Source)) - end - - Buffer = Buffer .. "" - - table.insert(savebuffer, Buffer) - if Hierarchy then - savehierarchy(Hierarchy) - end - table.insert(savebuffer, "") - end - - local function savegame() - local nilinstances - if OPTIONS.NilInstances and globalcontainer.getnilinstances then - local tmp = {} - - local NilInstancesFixes = OPTIONS.NilInstancesFixes - - for _, instance in next, globalcontainer.getnilinstances() do - if instance == game then - instance = nil - -- break - else - local ClassName = instance.ClassName - - local Fix = NilInstancesFixes[ClassName] - if Fix then -- * - instance = Fix(instance) - -- continue - end - - local Class = ClassList[ClassName] - if Class then - local ClassTags = Class.Tags - if ClassTags and ClassTags.Service then - instance.Parent = game - instance = nil - -- continue - end - end - end - if instance then - table.insert(tmp, instance) - end - end - nilinstances = tmp - end - local Starter = '' - if ToSaveInstance then - Starter = Starter .. 'true' - end - table.insert(savebuffer, Starter) --[[ - -- ? Roblox encodes the following additional attributes. These are not required. Moreover, any defined schemas are ignored, and not required for a file to be valid: xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" - Also http can be converted to https but not sure if Roblox would decide to detect that - -- ? nullnil - is a legacy concept that is no longer used. - ]] - - if ToSaveInstance then - savehierarchy({ ToSaveInstance }, ToSaveList) - else - savehierarchy(ToSaveList) - end - - if IsolateLocalPlayer or IsolateLocalPlayerCharacter then - local Players = service.Players - local LocalPlayer = Players.LocalPlayer - 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 - 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) 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()` - - If Union and MeshPart collisions don't work, run the script below in the Studio Command Bar: - - - local C = game:GetService("CoreGui") - local D = Enum.CollisionFidelity.Default - - for _, v in game:GetDescendants() do - if v:IsA("TriangleMeshPart") and not v:IsDescendantOf(C) then - v.CollisionFidelity = D - end - end - -If you can't move the Camera, run the scripts in the Studio Command Bar: - -workspace.CurrentCamera.CameraType = Enum.CameraType.Fixed - - This file was generated with the following settings: - ]] .. service.HttpService:JSONEncode(OPTIONS) .. "\n]]") - end - local tmp = { "" } - for Identifier, Value in next, SharedStrings do - tmp[#tmp + 1] = '' .. Value .. "" - end - - if 1 < #tmp then -- TODO: This sucks so much because we try to iterate a table just to check this (check above) - table.insert(savebuffer, table.concat(tmp)) - table.insert(savebuffer, "") - end - - table.insert(savebuffer, "") - savecache() - end - - if OPTIONS.ShowStatus then - local Exists = StatusGui:FindFirstChild("TextLabel") - if Exists then - Exists:Destroy() - end - StatusTextClone = StatusText:Clone() - StatusTextClone.Parent = StatusGui - end - if not OPTIONS.SavePlayers and not InstancesBlacklist.Players then - InstancesBlacklist.Players = {} - end - 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 next, 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.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 = xpcall(savegame, function(err) - return debug.traceback(err) - end) - elapse_t = os.clock() - elapse_t - - local Log10 = math.log10(elapse_t) - - if StatusTextClone then -local ExtraTime = 10 - if ok then - StatusTextClone.Text = string.format("Saved! Time %.2f seconds; Size %s", elapse_t, getsizeformat()) - task.wait(Log10 * 2 + ExtraTime) - else - StatusTextClone.Text = "Failed! Check F9 console for more info" - warn("Error found while saving") - warn("Information about error:") - warn(err) - task.wait(Log10 + ExtraTime) - end - StatusTextClone:Destroy() - end - end -end - -return synsaveinstance diff --git a/saveinstance.luau b/saveinstance.luau deleted file mode 100644 index 661af0a..0000000 --- a/saveinstance.luau +++ /dev/null @@ -1,2916 +0,0 @@ ---!native ---!optimize 2 ---!divine-intellect --- https://discord.gg/wx4ThpAsmw - -local service = setmetatable({}, { - __index = function(self, serviceName) - local o, s = pcall(Instance.new, serviceName) - local Service = o and s - or game:GetService(serviceName) - or settings():GetService(serviceName) - or UserSettings():GetService(serviceName) - self[serviceName] = Service - return Service - end, -}) - -local function string_find(s, pattern) - return string.find(s, pattern, nil, true) -end - -local function ArrayToDictionary(t, hydridMode, valueOverride) - local tmp = {} - - if hydridMode then - for some1, some2 in t do - if type(some1) == "number" then - tmp[some2] = valueOverride or true - elseif type(some2) == "table" then - tmp[some1] = ArrayToDictionary(some2, hydridMode) -- Some1 is Class, Some2 is Name - else - tmp[some1] = some2 - end - end - else - for _, key in t do - if type(key) == "string" then - tmp[key] = true - end - end - end - - return tmp -end - -local ESCAPES_PATTERN = "[&<>\"'\0\1-\9\11-\12\14-\31\127-\255]" -- * The safe way is to escape all five characters in text. However, the three characters " ' and > needn't be escaped in text --- %z (\0 aka NULL) might not be needed as Roblox automatically converts it to space everywhere it seems like --- Characters from: https://create.roblox.com/docs/en-us/ui/rich-text#escape-forms --- * EscapesPattern should be ordered from most common to least common characters for sake of speed --- * Might wanna use their numerical codes instead of named codes for reduced file size (Could be an Option) --- TODO Maybe we should invert the pattern to only allow certain characters (future-proof) -local ESCAPES = { - ["&"] = "&", -- 38 - ["<"] = "<", -- 60 - [">"] = ">", -- 62 - ['"'] = """, -- quot - ["'"] = "'", -- apos - ["\0"] = "", -} - -for rangeStart, rangeEnd in string.gmatch(ESCAPES_PATTERN, "(.)%-(.)") do - for charCode = string.byte(rangeStart), string.byte(rangeEnd) do - ESCAPES[string.char(charCode)] = "&#" .. charCode .. ";" - end -end - -local global_container -do - local filename = "UniversalMethodFinder" - - local finder - finder, global_container = loadstring( - game:HttpGet("https://raw.githubusercontent.com/luau/SomeHub/main/" .. filename .. ".luau", true), - filename - )() - - finder({ - -- readbinarystring = 'string.find(...,"bin",nil,true)', -- ! Could match some unwanted stuff (getbinaryindex) - -- request = 'string.find(...,"request",nil,true) and not string.find(...,"internal",nil,true)', - base64encode = 'local a={...}local b=a[1]local function c(a,b)return string.find(a,b,nil,true)end;return c(b,"encode")and(c(b,"base64")or c(string.lower(tostring(a[2])),"base64"))', - decompile = '(string.find(...,"decomp",nil,true) and string.sub(...,#...) ~= "s") or string.find(...,"assembl",nil,true)', - gethiddenproperty = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) ~= "s"', - gethui = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"ui",nil,true)', - getnilinstances = 'string.find(...,"nil",nil,true) and string.find(...,"get",nil,true) and string.sub(...,#...) == "s"', -- ! Could match some unwanted stuff - getscriptbytecode = 'string.find(...,"get",nil,true) and string.find(...,"bytecode",nil,true)', -- or string.find(...,"dump",nil,true) and string.find(...,"string",nil,true) due to Fluxus (dumpstring returns a function) - hash = 'local a={...}local b=a[1]local function c(a,b)return string.find(a,b,nil,true)end;return c(b,"hash")and c(string.lower(tostring(a[2])),"crypt")', - protectgui = 'string.find(...,"protect",nil,true) and string.find(...,"ui",nil,true) and not string.find(...,"un",nil,true)', - setthreadidentity = 'string.find(...,"identity",nil,true) and string.find(...,"set",nil,true)', - }, true, 10) -end - -local identify_executor = identifyexecutor or getexecutorname or whatexecutor - -local EXECUTOR_NAME = identify_executor and identify_executor() or "" - -local gethiddenproperty = global_container.gethiddenproperty - --- These should be universal enough -local appendfile = appendfile -local readfile = readfile -local writefile = writefile - -local getscriptbytecode = global_container.getscriptbytecode -- * A lot of assumptions are made based on whether this function is defined or not. So in certain edge cases, like if the executor defines "decompile" or "getscripthash" function yet doesn't define this function there might be loss of functionality of the saveinstance. Although that would be very rare and weird -local base64encode = global_container.base64encode -local sha384 - -local gethiddenproperty_fallback -do -- * Load Region of Déjà Vu - local UGCValidationService = service.UGCValidationService - - gethiddenproperty_fallback = function(instance, propertyName) - return UGCValidationService:GetPropertyValue(instance, propertyName) -- TODO Sadly there's no way to tell whether value is actually nil or the function just couldn't read it - end - - local o, r = pcall(gethiddenproperty, workspace.Terrain, "Decoration") - if not o or type(r) ~= "boolean" then -- * Tests if gethiddenproperty is fake - gethiddenproperty = nil - end - - local function benchmark(f1, f2, ...) - local ranking = table.create(2) - for i, f in { f1, f2 } do - local start = os.clock() - for _ = 1, 50 do - f(...) - end - ranking[i] = { t = os.clock() - start, f = f } - end - table.sort(ranking, function(a, b) - return a.t < b.t - end) - return ranking[1].f - end - - local test_str = string.rep("\1\0\0\0\1\2\3\4\5\6\7", 50) - - do - if not bit32.byteswap or not pcall(bit32.byteswap, 1) then -- Because Fluxus is missing byteswap - bit32 = table.clone(bit32) - - local function tobit(num) - num %= (bit32.bxor(num, 32)) - if 0x80000000 < num then - num -= bit32.bxor(num, 32) - end - return num - end - - bit32.byteswap = function(num) - local BYTE_SIZE = 8 - local MAX_BYTE_VALUE = 255 - - num %= bit32.bxor(2, 32) - - local a = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local b = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local c = bit32.band(num, MAX_BYTE_VALUE) - num = bit32.rshift(num, BYTE_SIZE) - - local d = bit32.band(num, MAX_BYTE_VALUE) - num = tobit(bit32.lshift(bit32.lshift(bit32.lshift(a, BYTE_SIZE) + b, BYTE_SIZE) + c, BYTE_SIZE) + d) - return num - end - - table.freeze(bit32) - end - - -- Credits @Reselim - local reselim_base64encode - pcall(function() - local b64_enc_buf = loadstring( - game:HttpGet("https://raw.githubusercontent.com/Reselim/Base64/master/Base64.lua", true), - "Base64" - )().encode - reselim_base64encode = function(raw) - return raw == "" and raw or buffer.tostring(b64_enc_buf(buffer.fromstring(raw))) - end - end) - - -- * Tests if base64encode exists and works properly then benchmark it - if base64encode and base64encode("\1\0\0\0\1") == "AQAAAAE=" then - if reselim_base64encode then - base64encode = benchmark(base64encode, reselim_base64encode, test_str) - end - else - base64encode = reselim_base64encode - end - - assert(base64encode, "base64encode not found") - end - - do - local hash = global_container.hash - - if hash then - sha384 = function(data) - return hash(data, "sha384") - end - end - - local filename = "RequireOnlineModule" - - -- Credits @boatbomber - local hashlib_sha384 - pcall(function() - hashlib_sha384 = loadstring( - game:HttpGet("https://raw.githubusercontent.com/luau/SomeHub/main/" .. filename .. ".luau", true), - filename - )()(4544052033).sha384 - end) - - -- * Tests if sha384 exists then benchmark it - if hashlib_sha384 then - if sha384 then - sha384 = benchmark(sha384, hashlib_sha384, test_str) - else - sha384 = hashlib_sha384 - end - end - - assert(sha384, "sha384 not found") - end -end - -local custom_decompiler - --- if getscriptbytecode then --- end - -local SharedStrings = {} -local SharedString_identifiers = setmetatable({ - identifier = 1e15, -- 1 quadrillion, up to 9.(9) quadrillion, in theory this shouldn't ever run out and be enough for all sharedstrings ever imaginable - -- TODO: worst case, add fallback to str randomizer once numbers run out : ) -}, { - __index = function(self, str) - local Identifier = base64encode(tostring(self.identifier)) -- tostring is only needed for built-in base64encode, Reselim's doesn't need it as buffers autoconvert - self.identifier += 1 - - self[str] = Identifier -- ? The value of the md5 attribute is a Base64-encoded key. type elements use this key to refer to the value of the string. The value is the text content, which is Base64-encoded. Historically, the key was the MD5 hash of the string value. However, this is not required; the key can be any value that will uniquely identify the shared string. Roblox currently uses BLAKE2b truncated to 16 bytes.. - return Identifier - end, -}) - -local Type_IDs = { - string = 0x02, - boolean = 0x03, - -- float = 0x05, - number = 0x06, - UDim = 0x09, - UDim2 = 0x0A, - BrickColor = 0x0E, - Color3 = 0x0F, - Vector2 = 0x10, - Vector3 = 0x11, - CFrame = 0x14, - EnumItem = 0x15, - NumberSequence = 0x17, - ColorSequence = 0x19, - NumberRange = 0x1B, - Rect = 0x1C, - Font = 0x21, -} -local CFrame_Rotation_IDs = { - ["\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x02, - ["\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0"] = 0x03, - ["\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x05, - ["\0\0\128\63\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\191\0\0\0\0"] = 0x06, - ["\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x07, - ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0"] = 0x09, - ["\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x0a, - ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0"] = 0x0c, - ["\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\63\0\0\0\0\0\0\0\0"] = 0x0d, - ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0"] = 0x0e, - ["\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\63\0\0\0\0\0\0\0\0"] = 0x10, - ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\128"] = 0x11, - ["\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x14, - ["\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\128"] = 0x15, - ["\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x17, - ["\0\0\128\191\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\191\0\0\0\128"] = 0x18, - ["\0\0\0\0\0\0\128\63\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x19, - ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0"] = 0x1b, - ["\0\0\0\0\0\0\128\191\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x1c, - ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0"] = 0x1e, - ["\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\191\0\0\0\0\0\0\0\0"] = 0x1f, - ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\0"] = 0x20, - ["\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\191\0\0\0\0\0\0\0\0"] = 0x22, - ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\191\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\128"] = 0x23, -} -local Binary_Descriptors -Binary_Descriptors = { - __SEQUENCE = function(raw, valueFormatter, keypointSize, Envelope) - local Keypoints = raw.Keypoints - local Keypoints_n = #Keypoints - - local len = 4 + (keypointSize or 12) * Keypoints_n - local b = buffer.create(len) - local offset = 0 - - buffer.writeu32(b, offset, Keypoints_n) - offset += 4 - - for _, keypoint in Keypoints do - buffer.writef32(b, offset, Envelope or keypoint.Envelope) - offset += 4 - buffer.writef32(b, offset, keypoint.Time) - offset += 4 - - local Value = keypoint.Value - if valueFormatter then - offset += valueFormatter(Value, b, offset) - else - buffer.writef32(b, offset, Value) - offset += 4 - end - end - - return b, len - end, - -------------------------------------------------------------- - -------------------------------------------------------------- - -------------------------------------------------------------- - ["string"] = function(raw) - local raw_len = #raw - local len = 4 + raw_len - - local b = buffer.create(len) - - buffer.writeu32(b, 0, raw_len) - buffer.writestring(b, 4, raw) - - return b, len - end, - ["boolean"] = function(raw) - local b = buffer.create(1) - - buffer.writeu8(b, 0, raw and 1 or 0) - - return b, 1 - end, - ["number"] = function(raw) -- double - local b = buffer.create(8) - - buffer.writef64(b, 0, raw) - - return b, 8 - end, - ["UDim"] = function(raw) - local b = buffer.create(8) - - buffer.writef32(b, 0, raw.Scale) - buffer.writei32(b, 4, raw.Offset) - - return b, 8 - end, - ["UDim2"] = function(raw) - local b = buffer.create(16) - - local Descriptors_UDim = Binary_Descriptors.UDim - local X = Descriptors_UDim(raw.X) - buffer.copy(b, 0, X) - local Y = Descriptors_UDim(raw.Y) - buffer.copy(b, 8, Y) - - return b, 16 - end, - ["BrickColor"] = function(raw) - local b = buffer.create(4) - - buffer.writeu32(b, 0, raw.Number) - - return b, 4 - end, - ["Color3"] = function(raw) - local b = buffer.create(12) - - buffer.writef32(b, 0, raw.R) - buffer.writef32(b, 4, raw.G) - buffer.writef32(b, 8, raw.B) - - return b, 12 - end, - ["Vector2"] = function(raw) - local b = buffer.create(8) - - buffer.writef32(b, 0, raw.X) - buffer.writef32(b, 4, raw.Y) - - return b, 8 - end, - ["Vector3"] = function(raw) - local b = buffer.create(12) - - buffer.writef32(b, 0, raw.X) - buffer.writef32(b, 4, raw.Y) - buffer.writef32(b, 8, raw.Z) - - return b, 12 - end, - ["CFrame"] = function(raw) - local X, Y, Z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = raw:GetComponents() - - local rotation_ID = CFrame_Rotation_IDs[string.pack("" - end, - __ENUM = function(raw) - return raw.Value, "token" - end, - __EXTREME = function(raw) - local Extreme - if raw ~= raw then - Extreme = "NAN" - elseif raw == math.huge then - Extreme = "INF" - elseif raw == -math.huge then - Extreme = "-INF" - end - - return Extreme - end, - __EXTREME_RANGE = function(raw) - return raw ~= raw and "0" or raw -- Normally we should return "-nan(ind)" instead of "0" but this adds more compatibility - end, - __MINMAX = function(min, max, descriptor) - return "" .. descriptor(min) .. "" .. descriptor(max) .. "" - end, - __PROTECTEDSTRING = function(raw) -- ? its purpose is to "protect" data from being treated as ordinary character data during processing; - return string_find(raw, "]]>") and string.gsub(raw, ESCAPES_PATTERN, ESCAPES) or XML_Descriptors.__CDATA(raw) - end, - __SEQUENCE = function(raw, valueFormatter) - -- The value is the text content, formatted as a space-separated list of floating point numbers. - -- tostring(raw) also works (but way slower rn) - local __EXTREME_RANGE = XML_Descriptors.__EXTREME_RANGE - - local Converted = "" - - for _, keypoint in raw.Keypoints do - local Value = keypoint.Value - - Converted ..= keypoint.Time .. " " .. (valueFormatter and valueFormatter(Value) or __EXTREME_RANGE(Value) .. " " .. __EXTREME_RANGE( - keypoint.Envelope - ) .. " ") -- ? Trailing whitespace is only needed for lune compatibility - end - - return Converted - end, - __VECTOR = function(X, Y, Z) -- Each element is a - local Value = "" .. X .. "" .. Y .. "" -- There is no Vector without at least two Coordinates.. (Vector1, at least on Roblox) - - if Z then - Value ..= "" .. Z .. "" - end - - return Value - end, - -------------------------------------------------------------- - -------------------------------------------------------------- - -------------------------------------------------------------- - Axes = function(raw) - -- The text of this element is formatted as an integer between 0 and 7 - - return "" .. XML_Descriptors.__BIT(raw.X, raw.Y, raw.Z) .. "" - end, - - -- ? Roblox uses CDATA only for these (try to prove this wrong): CollisionGroupData, SmoothGrid, MaterialColors, PhysicsGrid - -- ! Assuming all base64 encoded strings won't have newlines - - -- ! 7/7/24 - -- ! Fix for Electron v3 - -- ! Electron v3 'gethiddenproperty' automatically base64 encodes BinaryString values - - BinaryString = EXECUTOR_NAME == "Electron" and function(raw) - return raw - end or base64encode, -- TODO Issues may arise if NotScriptableFix or gethiddenproperty_fallback are able to read BinaryString where gethiddenproperty can't on Electron - - BrickColor = function(raw) - return raw.Number -- * Roblox encodes the tags as "int", but this is not required for Roblox to properly decode the type. For better compatibility, it is preferred that third-party implementations encode and decode "BrickColor" tags instead. Could also use "int" or "Color3uint8" - end, - CFrame = function(raw) - local X, Y, Z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = raw:GetComponents() - return XML_Descriptors.__VECTOR(X, Y, Z) - .. "" - .. R00 - .. "" - .. R01 - .. "" - .. R02 - .. "" - .. R10 - .. "" - .. R11 - .. "" - .. R12 - .. "" - .. R20 - .. "" - .. R21 - .. "" - .. R22 - .. "", - "CoordinateFrame" - end, - Color3 = function(raw) -- Each element is a - return "" .. raw.R .. "" .. raw.G .. "" .. raw.B .. "" -- ? It is recommended that Color3 is encoded with elements instead of text. - end, - Color3uint8 = function(raw) - -- https://github.com/rojo-rbx/rbx-dom/blob/master/docs/xml.md#color3uint8 - - return 0xFF000000 - + (math.floor(raw.R * 255) * 0x10000) - + (math.floor(raw.G * 255) * 0x100) - + math.floor(raw.B * 255) -- ? It is recommended that Color3uint8 is encoded with text instead of elements. - - -- return bit32.bor( - -- bit32.bor(bit32.bor(bit32.lshift(0xFF, 24), bit32.lshift(0xFF * raw.R, 16)), bit32.lshift(0xFF * raw.G, 8)), - -- 0xFF * raw.B - -- ) - - -- return tonumber(string.format("0xFF%02X%02X%02X",raw.R*255,raw.G*255,raw.B*255)) - end, - ColorSequence = function(raw) - -- The value is the text content, formatted as a space-separated list of FLOATing point numbers. - - return XML_Descriptors.__SEQUENCE(raw, function(color3) - local __EXTREME_RANGE = XML_Descriptors.__EXTREME_RANGE - - return __EXTREME_RANGE(color3.R) - .. " " - .. __EXTREME_RANGE(color3.G) - .. " " - .. __EXTREME_RANGE(color3.B) - .. " 0 " - end) - end, - Content = function(raw) - return raw == "" and "" or "" .. XML_Descriptors.string(raw, true) .. "" - end, - CoordinateFrame = function(raw) - return "" .. XML_Descriptors.CFrame(raw) .. "" - end, - -- DateTime = function(raw) return raw.UnixTimestampMillis end, -- TODO - Faces = function(raw) - -- The text of this element is formatted as an integer between 0 and 63 - return "" - .. XML_Descriptors.__BIT(raw.Right, raw.Top, raw.Back, raw.Left, raw.Bottom, raw.Front) - .. "" - end, - Font = function(raw) - local FontString = tostring(raw) -- TODO: Temporary fix - - local EmptyWeight = string_find(FontString, "Weight = ,") - local EmptyStyle = string_find(FontString, "Style = }") - - return "" - .. XML_Descriptors.Content(raw.Family) - .. "" - .. (EmptyWeight and "" or XML_Descriptors.__ENUM(raw.Weight)) - .. "" --TODO (OPTIONAL ELEMENT): Figure out how to determine (Content) rbxasset://fonts/GothamSSm-Medium.otf - end, - NumberRange = function(raw) -- tostring(raw) also works - -- The value is the text content, formatted as a space-separated list of floating point numbers. - local __EXTREME_RANGE = XML_Descriptors.__EXTREME_RANGE - - return __EXTREME_RANGE(raw.Min) .. " " .. __EXTREME_RANGE(raw.Max) --[[.. " "]] -- ! This might be required to bypass detections as thats how its formatted usually; __EXTREME_RANGE is not needed here but it fixes the issue where "nan 10" value would reset to "0 0" - end, - NumberSequence = nil, - -- NumberSequence = Descriptors.__SEQUENCE, - PhysicalProperties = function(raw) - --[[ - Contains at least one CustomPhysics element, which is interpreted according to the bool type. If this value is true, then the tag also contains an element for each component of the PhysicalProperties: - - Density - Friction - Elasticity - FrictionWeight - ElasticityWeight - - The value of each component is represented by the text content formatted as a 32-bit floating point number (see float) - ]] - - local CustomPhysics - if raw then - CustomPhysics = true - else - CustomPhysics = false - end - CustomPhysics = "" .. XML_Descriptors.bool(CustomPhysics) .. "" - - return raw - and CustomPhysics .. "" .. raw.Density .. "" .. raw.Friction .. "" .. raw.Elasticity .. "" .. raw.FrictionWeight .. "" .. raw.ElasticityWeight .. "" - or CustomPhysics - end, - -- ProtectedString = function(raw) return tostring(raw), "ProtectedString" end, - Ray = function(raw) - local vector3 = XML_Descriptors.Vector3 - - return "" .. vector3(raw.Origin) .. "" .. vector3(raw.Direction) .. "" - end, - Rect = function(raw) - return XML_Descriptors.__MINMAX(raw.Min, raw.Max, XML_Descriptors.Vector2), "Rect2D" - end, - Region3 = function(raw) --? Not sure yet (https://github.com/ui0ppk/roblox-master/blob/main/Network/Replicator.cpp#L1306) - local Translation = raw.CFrame.Position - local HalfSize = raw.Size * 0.5 - - return XML_Descriptors.__MINMAX( - Translation - HalfSize, -- https://github.com/ui0ppk/roblox-master/blob/main/App/util/Region3.cpp#L38 - Translation + HalfSize, -- https://github.com/ui0ppk/roblox-master/blob/main/App/util/Region3.cpp#L42 - XML_Descriptors.Vector3 - ) - end, - Region3int16 = function(raw) --? Not sure yet (https://github.com/ui0ppk/roblox-master/blob/main/App/v8tree/EnumProperty.cpp#L346) - return XML_Descriptors.__MINMAX(raw.Min, raw.Max, XML_Descriptors.Vector3int16) - end, - SharedString = function(raw) - raw = base64encode(raw) - - local Identifier = SharedString_identifiers[raw] - - if SharedStrings[Identifier] == nil then - SharedStrings[Identifier] = raw - end - - return Identifier - end, - -- SecurityCapabilities = function(raw) return raw end, - -- SystemAddress = function(raw) return raw end, - UDim = function(raw) - --[[ - S: Represents the Scale component. Interpreted as a . - O: Represents the Offset component. Interpreted as an . - ]] - - return "" .. raw.Scale .. "" .. raw.Offset .. "" - end, - UDim2 = function(raw) - --[[ - XS: Represents the X.Scale component. Interpreted as a . - XO: Represents the X.Offset component. Interpreted as an . - YS: Represents the Y.Scale component. Interpreted as a . - YO: Represents the Y.Offset component. Interpreted as an . - ]] - - local X, Y = raw.X, raw.Y - - return "" - .. X.Scale - .. "" - .. X.Offset - .. "" - .. Y.Scale - .. "" - .. Y.Offset - .. "" - end, - - -- UniqueId = function(raw) - -- --[[ - -- UniqueId properties might be random everytime Studio saves a place file - -- and don't have a use right now outside of packages, which SSI doesn't - -- account for anyway. They generate diff noise, so we shouldn't serialize - -- them until we have to. - -- ]] - -- -- https://github.com/MaximumADHD/Roblox-Client-Tracker/blob/roblox/LuaPackages/Packages/_Index/ApolloClientTesting/ApolloClientTesting/utilities/common/makeUniqueId.lua#L62 - -- return "" -- ? No idea if this even needs a Descriptor - -- end, - - Vector2 = function(raw) - --[[ - X: Represents the X component. Interpreted as a . - Y: Represents the Y component. Interpreted as a . - ]] - return XML_Descriptors.__VECTOR(raw.X, raw.Y) - end, - Vector2int16 = nil, - -- Vector2int16 = Descriptors.Vector2, -- except as - Vector3 = function(raw) - --[[ - X: Represents the X component. Interpreted as a . - Y: Represents the Y component. Interpreted as a . - Z: Represents the Z component. Interpreted as a . - ]] - return XML_Descriptors.__VECTOR(raw.X, raw.Y, raw.Z) - end, - Vector3int16 = nil, - -- Vector3int16 = Descriptors.Vector3, -- except as \ - bool = function(raw) - return raw and "true" or "false" - end, - double = function(raw) -- Float64 - return XML_Descriptors.__APIPRECISION(raw, 17) --? A precision of at least 17 is required to properly represent a 64-bit floating point value, so this amount is recommended. - end, -- ? wouldn't float be better as an optimization - float = function(raw) -- Float32 - return XML_Descriptors.__APIPRECISION(raw, 9) -- ? A precision of at least 9 is required to properly represent a 32-bit floating point value, so this amount is recommended. - end, - int = function(raw) -- Int32 - return XML_Descriptors.__EXTREME(raw) or raw - end, - int64 = nil, - string = function(raw, skipEmptyCheck) - return not skipEmptyCheck and (raw == "" and raw or raw == nil and "") - or string_find(raw, "]]>") and string.gsub(raw, ESCAPES_PATTERN, ESCAPES) - or XML_Descriptors.__CDATA(string.gsub(raw, "\0", "")) - end, -} -for descriptorName, redirectName in - { - NumberSequence = "__SEQUENCE", - Vector2int16 = "Vector2", - Vector3int16 = "Vector3", - int64 = "int", -- Int64 (long) - } -do - XML_Descriptors[descriptorName] = XML_Descriptors[redirectName] -end - -local ClassList - -do - local ClassPropertyExceptions = { TriangleMeshPart = ArrayToDictionary({ "CollisionFidelity" }) } - - local NotScriptableFixes = - { -- For more info: https://github.com/luau/UniversalSynSaveInstance/blob/master/Tests/Potentially%20Missing%20Properties%20Tracker.luau - Instance = { - AttributesSerialize = function(instance) - -- * There are certain restrictions for names of attributes - -- https://create.roblox.com/docs/reference/engine/classes/Instance#SetAttribute - -- But it seems like even if those are present, Studio still opens the file just fine - -- So there is no need to check for them currently - - -- TODO: merge sequence Descriptors and some other descriptors where possible (check xml descriptors) - -- ? Return early for empty tags (this proved equally as fast when done using counter/next) - - local attrs = instance:GetAttributes() - - local attrs_n = 0 - local buffer_size = 4 - local attrs_sorted = {} - local attrs_formatted = table.clone(attrs) - -- warn(instance) - for attr, val in attrs do - attrs_n += 1 - attrs_sorted[attrs_n] = attr - - local Type = typeof(val) - - local Descriptor = Binary_Descriptors[Type] - local attr_size - - attrs_formatted[attr], attr_size = Descriptor(val) - - buffer_size += 5 + #attr + attr_size - end - -- print(instance, attrs_n) -- ? Looks like multiple buffer calls cause the this part to be skipped sometimes ? - if attrs_n == 0 then - return "" - end - table.sort(attrs_sorted) - - local b = buffer.create(buffer_size) - - local offset = 0 - - buffer.writeu32(b, offset, attrs_n) - offset += 4 - - local Descriptors_string = Binary_Descriptors.string - for _, attr in attrs_sorted do - local b_Name, Name_size = Descriptors_string(attr) - - buffer.copy(b, offset, b_Name) - offset += Name_size - - buffer.writeu8(b, offset, Type_IDs[typeof(attrs[attr])]) - offset += 1 - - local bb = attrs_formatted[attr] - - buffer.copy(b, offset, bb) - offset += buffer.len(bb) - end - - return buffer.tostring(b) - end, - Tags = function(instance) - -- https://github.com/RobloxAPI/spec/blob/master/properties/Tags.md - - local tags = instance:GetTags() - - if #tags == 0 then - return "" - end - - return table.concat(tags, "\0") - end, - }, - - -- DebuggerBreakpoint = {line="Line"}, -- ? This shouldn't appear in live games (try to prove this wrong) - BallSocketConstraint = { MaxFrictionTorqueXml = "MaxFrictionTorque" }, - BasePart = { - Color3uint8 = "Color", - MaterialVariantSerialized = "MaterialVariant", - size = "Size", - }, - -- CustomEvent = {PersistedCurrentValue=function(instance) -- * Class is Deprecated and :SetValue doesn't seem to affect GetCurrentValue anymore - -- local Receiver = instance:GetAttachedReceivers()[1] - -- if Receiver then - -- return Receiver:GetCurrentValue() - -- else - -- error("No Receiver", 2) - -- end - -- end}, - Terrain = { - AcquisitionMethod = "LastUsedModificationMethod", -- ? Not sure - MaterialColors = function(instance) -- https://github.com/RobloxAPI/spec/blob/master/properties/MaterialColors.md - local TERRAIN_MATERIAL_COLORS = - { --https://github.com/rojo-rbx/rbx-dom/blob/master/rbx_dom_lua/src/customProperties.lua#L5 - Enum.Material.Grass, - Enum.Material.Slate, - Enum.Material.Concrete, - Enum.Material.Brick, - Enum.Material.Sand, - Enum.Material.WoodPlanks, - Enum.Material.Rock, - Enum.Material.Glacier, - Enum.Material.Snow, - Enum.Material.Sandstone, - Enum.Material.Mud, - Enum.Material.Basalt, - Enum.Material.Ground, - Enum.Material.CrackedLava, - Enum.Material.Asphalt, - Enum.Material.Cobblestone, - Enum.Material.Ice, - Enum.Material.LeafyGrass, - Enum.Material.Salt, - Enum.Material.Limestone, - Enum.Material.Pavement, - } - - local b = buffer.create(69) -- 69 bytes: 6 reserved + 63 for colors (21 materials * 3 components) - local offset = 6 -- 6 reserved bytes - - local RGB_components = { "R", "G", "B" } - - for _, material in TERRAIN_MATERIAL_COLORS do - local color = instance:GetMaterialColor(material) - for _, component in RGB_components do - buffer.writeu8(b, offset, math.floor(color[component] * 255)) -- ? math.floor seems unneeded but it makes it faster - offset += 1 - end - end - - return buffer.tostring(b) - end, - }, - TriangleMeshPart = { - FluidFidelityInternal = "FluidFidelity", - }, - MeshPart = { InitialSize = "MeshSize" }, - PartOperation = { InitialSize = "MeshSize" }, - Part = { shape = "Shape" }, - TrussPart = { style = "Style" }, - FormFactorPart = { - formFactorRaw = "FormFactor", - }, - DoubleConstrainedValue = { value = "Value" }, - IntConstrainedValue = { value = "Value" }, - Fire = { heat_xml = "Heat", size_xml = "Size" }, - - Humanoid = { Health_XML = "Health" }, - LocalizationTable = { - Contents = function(instance) - return instance:GetContents() --service.HttpService:JSONEncode(instance:GetEntries()) - end, - }, - MaterialService = { Use2022MaterialsXml = "Use2022Materials" }, - - Model = { - ScaleFactor = function(instance) - return instance:GetScale() - end, - WorldPivotData = "WorldPivot", -- TODO This doesn't accurately represent whether optional type property is present or not (it's never nil), gethiddenproperty or gethiddenproperty_fallback is preferred - -- ModelMeshCFrame = "Pivot Offset", -- * Both are NotScriptable - }, - PackageLink = { PackageIdSerialize = "PackageId", VersionIdSerialize = "VersionNumber" }, - Players = { MaxPlayersInternal = "MaxPlayers", PreferredPlayersInternal = "PreferredPlayers" }, -- ? Only needed for execs that lack LocalUserSecurity (Level 2, 5, 9), even so, it's a pretty useless information as it can be viewed elsewhere - - StarterPlayer = { AvatarJointUpgrade_Serialized = "AvatarJointUpgrade" }, - Smoke = { size_xml = "Size", opacity_xml = "Opacity", riseVelocity_xml = "RiseVelocity" }, - Sound = { - xmlRead_MaxDistance_3 = "RollOffMaxDistance", -- * Also MaxDistance - }, - -- ViewportFrame = { -- * Pointless because these reflect CurrentCamera's properties - -- CameraCFrame = function(instance) -- * - -- local CurrentCamera = instance.CurrentCamera - -- if CurrentCamera then - -- return CurrentCamera.CFrame - -- else - -- error("No CurrentCamera", 2) - -- end - -- end, - -- -- CameraFieldOfView = - -- }, - WeldConstraint = { - Part0Internal = "Part0", - Part1Internal = "Part1", - -- State = function(instance) - -- -- If untouched then default state is 3 (default true) - -- return instance.Enabled and 1 or 0 - -- end, - }, - Workspace = { - -- SignalBehavior2 = "SignalBehavior", -- * Both are NotScriptable so it doesn't make sense to keep - CollisionGroupData = function() - local collision_groups = game:GetService("PhysicsService"):GetRegisteredCollisionGroups() - - local col_groups_n = #collision_groups - - if col_groups_n == 0 then - return "\1\0" - end - - local buffer_size = 3 -- Initial size - - for _, group in collision_groups do - buffer_size += 7 + #group.name - end - - buffer_size -= 1 -- Except Default group - - local b = buffer.create(buffer_size) - - local offset = 0 - - buffer.writeu8(b, offset, 1) -- ? [CONSTANT] Version byte (likely) - offset += 1 - buffer.writeu16(b, offset, col_groups_n * 10) -- Group count (not sure about u16) - offset += 2 - - for i, group in collision_groups do - local name, id, mask = group.name, i - 1, group.mask - local name_len = #name - - if id ~= 0 then - buffer.writeu8(b, offset, id) -- ID - offset += 1 - end - - buffer.writeu8(b, offset, 4) -- ? [CONSTANT] Not sure what this is (also not sure about u8, could be i8) - offset += 1 - - buffer.writei32(b, offset, mask) -- Mask value as signed 32-bit integer - offset += 4 - - buffer.writeu8(b, offset, name_len) -- Name length - offset += 1 - buffer.writestring(b, offset, name) -- Name - offset += name_len - end - - return buffer.tostring(b) - end, - }, - } - - local function FetchAPI() - -- * You can also use: https://setup.rbxcdn.com/{https://setup.rbxcdn.com/versionQTStudio}-Full-API-Dump.json - -- * https://setup.rbxcdn.com/versionQTStudio seems to be a bit behind DeployHistory.txt - - -- Credits @MaximumADHD - - local API_Dump - - local ok, err = pcall(function() - local VERSIONS = version() - - local o, r = pcall(readfile, VERSIONS) - if o and r and r ~= "" then - API_Dump = r - return - end - - local latest_commit = service.HttpService:JSONDecode( - game:HttpGet( - "https://api.github.com/search/commits?sort=committer-date&order=desc&q=repo:MaximumADHD/Roblox-Client-Tracker+0." - .. string.split(VERSIONS, ".")[2], - true - ) - ).items[1] - -- print(VERSIONS) - -- print(latest_commit.commit.message) - API_Dump = game:HttpGet( - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/" - .. latest_commit.sha - .. "/Mini-API-Dump.json", - true - ) - - writefile(VERSIONS, API_Dump) -- ? To avoid 60 requests / 1 h limit on GitHub Api - end) - - if not ok then - warn("Failed to get " .. version() .. " API Dump, trying latest..") - warn(err) - API_Dump = game:HttpGet( - "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json", - true - ) - end - - local classList = {} - - for _, API_Class in service.HttpService:JSONDecode(API_Dump).Classes do - local ClassProperties, ClassProperties_size = {}, 1 - local Class = { - Properties = ClassProperties, - Superclass = API_Class.Superclass, - } - - local ClassTags = API_Class.Tags - local ClassName = API_Class.Name - - if ClassTags then - Class.Tags = ArrayToDictionary(ClassTags) -- or {} - end - - local NotScriptableFixClass = NotScriptableFixes[ClassName] - - -- ? Check 96ea8b2a755e55a78aedb55a7de7e83980e11077 commit - If a NotScriptableFix is needed that relies on another NotScriptable Property (which doesn't really make sense in the first place) - - local ClassExceptions = ClassPropertyExceptions[ClassName] - - for _, Member in API_Class.Members do - if Member.MemberType == "Property" then - local Serialization = Member.Serialization - - if Serialization.CanLoad then -- If Roblox doesn't save it why should we; If Roblox doesn't load it we don't need to save it - --[[ - -- ! CanSave replaces "Tags.Deprecated" check because there are some old properties which are deprecated yet have CanSave. - Example: Humanoid.Health is CanSave false due to Humanoid.Health_XML being CanSave true (obsolete properties basically) - in this case both of them will Load. (aka PropertyPatches) - CanSave being on same level as CanLoad also fixes potential issues with overlapping properties like Color, Color3 & Color3uint8 of BasePart, out of which only Color3uint8 should save - This also fixes everything in IgnoreClassProperties automatically without need to hardcode :) - A very simple fix for many problems that saveinstance scripts encounter! - --]] - local PropertyName = Member.Name - if Serialization.CanSave or ClassExceptions and ClassExceptions[PropertyName] then - local MemberTags = Member.Tags - - local ValueType = Member.ValueType - local ValueType_Name = ValueType.Name - - local Special - - if MemberTags then - MemberTags = ArrayToDictionary(MemberTags) - - Special = MemberTags.NotScriptable - end - - -- if not Special then - local Property = { - Name = PropertyName, - Category = ValueType.Category, - -- Default = Member.Default, - -- Tags = MemberTags, - ValueType = ValueType_Name, - - Special = Special, - - CanRead = nil, - } - - if string.sub(ValueType_Name, 1, 8) == "Optional" then - -- Extract the string after "Optional" - Property.Optional = string.sub(ValueType_Name, 9) - end - - if NotScriptableFixClass then - local NotScriptableFix = NotScriptableFixClass[PropertyName] - if NotScriptableFix then - Property.Fallback = type(NotScriptableFix) == "function" and NotScriptableFix - or function(instance) - return instance[NotScriptableFix] - end - end - end - ClassProperties[ClassProperties_size] = Property - ClassProperties_size += 1 - - -- end - end - end - end - end - - classList[ClassName] = Class - end - - -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering through properties to remove this - - return classList - end - - local ok, result = pcall(FetchAPI) - if ok then - ClassList = result - else - warn("Failed to load the API Dump") - warn(result) - return - end -end - -local inherited_properties = {} -local default_instances = {} -local referents, ref_size = {}, 0 -- ? Roblox encodes all elements with a referent attribute. Each value is generated by starting with the prefix RBX, followed by a UUID version 4, with - characters removed, and all characters converted to uppercase. - -local GLOBAL_ENV = getgenv and getgenv() or _G or shared - ---[=[ - @class SynSaveInstance - Represents the options for saving instances with custom settings using the synsaveinstance function. -]=] - ---- @interface CustomOptions table ---- * Structure of the main CustomOptions table. ---- * Note: Aliases take priority over parent option name. ---- @within SynSaveInstance ---- @field __DEBUG_MODE boolean -- Recommended to enable if you wish to help us improve our products and find bugs / issues with it! ___Default:___ false ---- @field ReadMe boolean --___Default:___ true ---- @field SafeMode boolean -- Kicks you before Saving, which prevents you from being detected in any game. ___Default:___ false ---- @field AntiIdle boolean -- Prevents the 20-minute-Idle Kick. ___Default:___ true ---- @field Anonymous boolean -- * **RISKY:** Cleans the file of any info related to your account like: Name, UserId. This is useful for some games that might store that info in GUIs or other Instances. Might potentially mess up parts of strings that contain characters that match your Name or parts of numbers that match your UserId. ___Default:___ false ---- @field ShowStatus boolean -- ___Default:___ true ---- @field mode string -- Change this to invalid mode like "invalid" if you only want ExtraInstances. "optimized" mode is **NOT** supported with *@Object* option. ___Default:___ `"optimized"` ---- @field noscripts boolean -- ___Aliases:___ `Decompile`. ___Default:___ false ---- @field scriptcache boolean -- ___Default:___ true ---- @field decomptype string -- * "custom" - for built-in custom decompiler. ___Default:___ Your executor's decompiler, if available. Otherwise uses "custom" if not. ---- @field timeout number -- If the decompilation run time exceeds this value it gets cancelled. Set to -1 to disable timeout (unreliable). ***Aliases***: `DecompileTimeout`. ___Default:___ 10 ---- @field DecompileJobless boolean -- Includes already decompiled code in the output. No new scripts are decompiled. ___Default:___ false ---- @field SaveBytecode boolean -- Includes bytecode in the output. Useful if you wish to be able to decompile it yourself later. ___Default:___ false ---- .DecompileIgnore {Instance | Instance.ClassName | [Instance.ClassName] = {Instance.Name}} -- * Ignores match & it's descendants by default. To Ignore only the instance itself set the value to `= false`. Examples: "Chat", - Matches any instance with "Chat" ClassName, Players = {"MyPlayerName"} - Matches "Players" Class AND "MyPlayerName" Name ONLY, `workspace` - matches Instance by reference, `[workspace] = false` - matches Instance by reference and only ignores the instance itself and not it's descendants. ___Default:___ {Chat, TextChatService} ---- .IgnoreList {Instance | Instance.ClassName | [Instance.ClassName] = {Instance.Name}} -- Structure is similar to **@DecompileIgnore** except `= false` meaning if you ignore one instance it will automatically ignore it's descendants. ___Default:___ {CoreGui, CorePackages} ---- .ExtraInstances {Instance} -- If used with any invalid mode (like "invalidmode") it will only save these instances. ___Default:___ {} ---- @field IgnoreProperties table -- Ignores properties by Name. ___Default:___ {} ---- @field SaveCacheInterval number -- The less the value the more often it saves, but that would mean less performance due to constantly saving. ___Default:___ 0x1600 * 10 ---- @field FilePath string -- Must only contain the name of the file, no file extension. ___Default:___ false ---- @field Object Instance -- * 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, FOR EXAMPLE - *game.Workspace***. `"optimized"` mode is **NOT** supported with this option. If IsModel is set to false then Object specified here will be saved as a place file. Only saves the instance itself, not the descendants. If you wish to save descendants too then use @ExtraInstances={Object}. ___Default:___ false ---- @field IsModel boolean -- If Object is specified then sets to true automatically, unless you set it to false. ___Default:___ false ---- @field NilInstances boolean -- Save instances that aren't Parented (Parented to nil). ___Default:___ false ---- .NilInstancesFixes {[Instance.ClassName] = function} -- * This can cause some Classes to be fixed even though they might not need the fix (better be safe than sorry though). For example, Bones inherit from Attachment if we dont define them in the NilInstancesFixes then this will catch them anyways. **TO AVOID THIS BEHAVIOR USE THIS EXAMPLE:** {ClassName_That_Doesnt_Need_Fix = false}. ___Default:___ {Animator = function, AdPortal = function, BaseWrap = function, Attachment = function} ---- @field IgnoreDefaultProperties boolean -- Ignores default properties during saving. ___Default:___ true ---- @field IgnoreNotArchivable boolean -- Ignores the Archivable property and saves Non-Archivable instances. ___Default:___ true ---- @field IgnorePropertiesOfNotScriptsOnScriptsMode boolean -- Ignores property of every instance that is not a script in "scripts" mode. ___Default:___ false ---- @field IgnoreSpecialProperties boolean -- Prevents calls to `gethiddenproperty` and uses fallback methods instead. This also helps with crashes. If your file is corrupted after saving, you can try turning this on. ___Default:___ false ---- @field IsolateLocalPlayer boolean -- Saves Children of LocalPlayer as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving. ___Default:___ false ---- @field IsolateStarterPlayer boolean -- If enabled, StarterPlayer will be cleared and the saved starter player will be placed into folders. ___Default:___ false ---- @field IsolateLocalPlayerCharacter boolean -- Saves Children of LocalPlayer.Character as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving. ___Default:___ false ---- @field RemovePlayerCharacters boolean -- Ignore player characters while saving. (Enables SaveNonCreatable automatically). ___Default:___ true ---- @field SaveNonCreatable boolean -- * Includes non-serializable instances as Folder objects (Name is misleading as this is mostly a fix for certain NilInstances and isn't always related to NotCreatable). ___Default:___ false ---- .NotCreatableFixes table -- * {"Player"} is the same as {Player = "Folder"}; Format like {SpawnLocation = "Part"} is only to be used when SpawnLocation inherits from "Part" AND "Part" is Creatable. ___Default:___ { "Player", "PlayerScripts", "PlayerGui" } ---- @field IsolatePlayers boolean -- * This option does save players, it's just they won't show up in Studio and can only be viewed through the place file code (in text editor). More info at https://github.com/luau/UniversalSynSaveInstance/issues/2. ___Default:___ false ---- @field IgnoreDefaultPlayerScripts boolean -- * **RISKY: Ignores Default PlayerScripts like PlayerModule & RbxCharacterSounds. Prevents crashes on certain Executors. ___Default:___ true ---- @field IgnoreSharedStrings boolean -- * **RISKY: FIXES CRASHES (TEMPORARY, TESTED ON ROEXEC ONLY). FEEL FREE TO DISABLE THIS TO SEE IF IT WORKS FOR YOU**. ___Default:___ true ---- @field SharedStringOverwrite boolean -- * **RISKY:** 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). ___Default:___ false ---- @field TreatUnionsAsParts boolean -- * **RISKY:** Converts all UnionOperations to Parts. Useful if your Executor isn't able to save (read) Unions, because otherwise they will be invisible. ___Default:___ false (except Solara) ---- @field AlternativeWritefile boolean -- * **RISKY:** Uses breaks down file content string into segments and writes them using appendfile. This might help with crashes when it starts writing to file. ___Default:___ false - ---- @interface OptionsAliases ---- @within SynSaveInstance ---- Aliases for the [SynSaveInstance.CustomOptions table]. ---- @field FilePath string -- FileName ---- @field IgnoreDefaultProperties string -- IgnoreDefaultProps ---- @field SaveNonCreatable string -- SaveNotCreatable ---- @field IsolatePlayers string -- SavePlayers ---- @field scriptcache string -- DecompileJobless ---- @field timeout string -- DecompileTimeout ---- @field IgnoreNotArchivable string -- IgnoreArchivable ---- @field RemovePlayerCharacters string -- INVERSE SavePlayerCharacters - ---[=[ - @function saveinstance - Saves instances with specified options. Example: - ```lua - local Params = { - RepoURL = "https://raw.githubusercontent.com/luau/SynSaveInstance/main/", - SSI = "saveinstance", - } - - local synsaveinstance = loadstring(game:HttpGet(Params.RepoURL .. Params.SSI .. ".luau", true), Params.SSI)() - - local CustomOptions = { SafeMode = true, timeout = 15, SaveBytecode = true } - - synsaveinstance(CustomOptions) - ``` - @within SynSaveInstance - @yields - @param Parameter_1 variant> -- Can either be [SynSaveInstance.CustomOptions table] or a filled with instances ({Instance}), (then it will be treated as ExtraInstances with an invalid mode and IsModel will be true). - @param Parameter_2 table -- [OPTIONAL] If present, then Parameter_2 will be assumed to be [SynSaveInstance.CustomOptions table]. And then if the Parameter_1 is an Instance, then it will be assumed to be [SynSaveInstance.CustomOptions table].Object. If Parameter_1 is a table filled with instances ({Instance}), then it will be assumed to be [SynSaveInstance.CustomOptions table].ExtraInstances and IsModel will be true). This exists for sake compatibility with `saveinstance(game, {})` -]=] - -local function synsaveinstance(CustomOptions, CustomOptions2) - -- if GLOBAL_ENV.__USSI then - -- warn("UniversalSynSaveInstance is already running") - -- return - -- end - -- GLOBAL_ENV.__USSI = true - - do - local setthreadidentity = global_container.setthreadidentity - if setthreadidentity then - pcall(setthreadidentity, 8) -- ? Arceus X Fix - end - end - - local totalstr, totalsize = "", 0 - local savebuffer, savebuffer_size = { '' }, 2 - - local StatusText - - local OPTIONS = { - mode = "optimized", - noscripts = false, - scriptcache = true, - decomptype = "", - timeout = 10, - --* New: - __DEBUG_MODE = false, - - -- Binary = false, -- true in syn newer versions (false in our case because no binary support yet), Description: Saves everything in Binary Mode (rbxl/rbxm). - --Callback = nil, -- Description: If set, the serialized data will be sent to the callback instead of to file. - --Clipboard/CopyToClipboard = false, -- Description: If set to true, the serialized data will be set to the clipboard, which can be later pasted into studio easily. Useful for saving models. - -- MaxThreads = 3 -- Description: The number of decompilation threads that can run at once. More threads means it can decompile for scripts at a time. - -- DisableCompression = false, --Description: Disables compression in the binary output - - DecompileJobless = false, - DecompileIgnore = { -- * Clean these up (merged Old Syn and New Syn) - service.Chat, - service.TextChatService, - ModuleScript = nil, - }, - IgnoreDefaultPlayerScripts = EXECUTOR_NAME ~= "Wave" and true, - SaveBytecode = false, - - IgnoreProperties = { "ScriptGuid", "UniqueId", "HistoryId" }, - - IgnoreList = { service.CoreGui, service.CorePackages }, - - ExtraInstances = {}, - NilInstances = false, - NilInstancesFixes = {}, - - SaveCacheInterval = 0x1600 * 10, - ShowStatus = true, - SafeMode = false, - AntiIdle = true, - Anonymous = false, - ReadMe = true, - FilePath = false, - Object = false, - IsModel = false, - - IgnoreDefaultProperties = true, - IgnoreNotArchivable = true, - IgnorePropertiesOfNotScriptsOnScriptsMode = false, - IgnoreSpecialProperties = ArrayToDictionary({ "Fluxus", "Delta", "Solara" })[EXECUTOR_NAME] or false, -- ! Please submit more Executors that crash on gethiddenproperty (with this disabled basically) - - IsolateLocalPlayer = false, -- #service.StarterGui:GetChildren() == 0 - IsolateLocalPlayerCharacter = false, - IsolatePlayers = false, - IsolateStarterPlayer = false, - RemovePlayerCharacters = true, - - SaveNonCreatable = false, - NotCreatableFixes = { "Player", "PlayerScripts", "PlayerGui", "TouchTransmitter" }, - - -- ! Risky - - IgnoreSharedStrings = EXECUTOR_NAME ~= "Wave" and true, - SharedStringOverwrite = false, - TreatUnionsAsParts = EXECUTOR_NAME == "Solara", -- TODO Temporary true (once removed, remove Note from docs too) - AlternativeWritefile = ArrayToDictionary({ "Solara", "Celery" })[EXECUTOR_NAME] or false, -- TODO Temporary true (once removed, remove Note from docs too) - - OptionsAliases = { -- You can't really modify these as a user - FilePath = "FileName", - IgnoreDefaultProperties = "IgnoreDefaultProps", - SaveNonCreatable = "SaveNotCreatable", - IsolatePlayers = "SavePlayers", - scriptcache = "DecompileJobless", - timeout = "DecompileTimeout", - IgnoreNotArchivable = "IgnoreArchivable", - }, - } - - local function GetAlias(searchAlias) - for option, alias in OPTIONS.OptionsAliases do - if searchAlias == alias then - return option - end - end - - return "" - end - - do -- * Load Settings - local function construct_NilinstanceFix(Name, ClassName, Separate) - return function(instance, instancePropertyOverrides) - local Exists = if Separate then nil else OPTIONS.NilInstancesFixes[Name] - - local Fix - - local DoesntExist = not Exists - if DoesntExist then - Fix = Instance.new(ClassName) - if not Separate then - OPTIONS.NilInstancesFixes[Name] = Fix - end - -- Fix.Name = Name - - instancePropertyOverrides[Fix] = { __Children = { instance }, Properties = { Name = Name } } - else - Fix = Exists - end - - table.insert(instancePropertyOverrides[Fix].__Children, instance) - -- InstancePropertyOverrides[instance].Parent = AnimationController - if DoesntExist then - return Fix - end - end - end - - -- TODO: Merge BaseWrap & Attachment & AdPortal fix (put all under MeshPart container) - -- TODO?: - -- DebuggerWatch DebuggerWatch must be a child of ScriptDebugger - -- PluginAction Parent of PluginAction must be Plugin or PluginMenu that created it! - OPTIONS.NilInstancesFixes.Animator = construct_NilinstanceFix( - "Animator has to be placed under Humanoid or AnimationController", - "AnimationController" - ) - OPTIONS.NilInstancesFixes.AdPortal = construct_NilinstanceFix("AdPortal must be parented to a Part", "Part") - OPTIONS.NilInstancesFixes.Attachment = - construct_NilinstanceFix("Attachments must be parented to a BasePart or another Attachment", "Part") -- * Bones inherit from Attachments - OPTIONS.NilInstancesFixes.BaseWrap = - construct_NilinstanceFix("BaseWrap must be parented to a MeshPart", "MeshPart") - OPTIONS.NilInstancesFixes.PackageLink = - construct_NilinstanceFix("Package already has a PackageLink", "Folder", true) - - if CustomOptions2 and type(CustomOptions2) == "table" then - local tmp = CustomOptions - local Type = typeof(tmp) - CustomOptions = CustomOptions2 - if Type == "Instance" then - CustomOptions.Object = tmp - elseif Type == "table" and typeof(tmp[1]) == "Instance" then - CustomOptions.ExtraInstances = tmp - OPTIONS.IsModel = true - end - end - - local Type = typeof(CustomOptions) - - if Type == "table" then - if typeof(CustomOptions[1]) == "Instance" then - OPTIONS.mode = "invalidmode" - OPTIONS.ExtraInstances = CustomOptions - OPTIONS.IsModel = true - CustomOptions = {} - else - for key, value in CustomOptions do - if OPTIONS[key] == nil then - local Option = GetAlias(key) - - if Option then - OPTIONS[Option] = value - end - else - OPTIONS[key] = value - end - end - local Decompile = CustomOptions.Decompile - if Decompile ~= nil then - OPTIONS.noscripts = not Decompile - end - local SavePlayerCharacters = CustomOptions.SavePlayerCharacters - if SavePlayerCharacters ~= nil then - OPTIONS.RemovePlayerCharacters = not SavePlayerCharacters - end - local RemovePlayers = CustomOptions.RemovePlayers - if RemovePlayers ~= nil then - OPTIONS.IsolatePlayers = not RemovePlayers - end - end - elseif Type == "Instance" then - OPTIONS.mode = "invalidmode" - OPTIONS.Object = CustomOptions - CustomOptions = {} - else - CustomOptions = {} - end - end - - if OPTIONS.IgnoreDefaultPlayerScripts then - -- TODO This is a bad workaround, find a better automatic way - local DecompileIgnore = OPTIONS.DecompileIgnore - - local Path = service.StarterPlayer:FindFirstChild("StarterPlayerScripts") - local Exclude = { ModuleScript = { "PlayerModule" }, LocalScript = { "RbxCharacterSounds" } } - if Path then - for _, className in Exclude do - for _, name in className do - local Found = Path:FindFirstChild(name) - if Found then - table.insert(DecompileIgnore, Found) - end - end - end - end - end - - local function construct_TimeoutHandler(timeout, f, timeout_ret) - return function(script) -- TODO Ideally use ... (vararg) instead of `script` in case this is reused for something other than `decompile` & `getscriptbytecode` - if timeout < 0 then - return pcall(f, script) - end - - local thread = coroutine.running() - local timeoutThread, isCancelled - - timeoutThread = task.delay(timeout, function() - isCancelled = true -- TODO task.cancel - coroutine.resume(thread, nil, timeout_ret) - end) - - task.spawn(function() - local ok, result = pcall(f, script) - - if isCancelled then - return - end - - task.cancel(timeoutThread) - - while coroutine.status(thread) ~= "suspended" do - task.wait() - end - - coroutine.resume(thread, ok, result) - end) - - return coroutine.yield() - end - end - - local InstancePropertyOverrides = {} - - local DecompileIgnore, IgnoreList, IgnoreProperties, NotCreatableFixes = - ArrayToDictionary(OPTIONS.DecompileIgnore, true), - ArrayToDictionary(OPTIONS.IgnoreList, true), - ArrayToDictionary(OPTIONS.IgnoreProperties), - ArrayToDictionary(OPTIONS.NotCreatableFixes, true, "Folder") - - local __DEBUG_MODE = OPTIONS.__DEBUG_MODE - - if __DEBUG_MODE and type(__DEBUG_MODE) ~= "function" then - __DEBUG_MODE = warn - end - - local FilePath = OPTIONS.FilePath - local SaveCacheInterval = OPTIONS.SaveCacheInterval - local ToSaveInstance = OPTIONS.Object - local IsModel = OPTIONS.IsModel - - if ToSaveInstance and CustomOptions.IsModel == nil then - IsModel = true - end - - local IgnoreDefaultProperties = OPTIONS.IgnoreDefaultProperties - local IgnoreNotArchivable = not OPTIONS.IgnoreNotArchivable - local IgnorePropertiesOfNotScriptsOnScriptsMode = OPTIONS.IgnorePropertiesOfNotScriptsOnScriptsMode - - local old_gethiddenproperty - if OPTIONS.IgnoreSpecialProperties and gethiddenproperty then - old_gethiddenproperty = gethiddenproperty - gethiddenproperty = nil - end - - local IsolateLocalPlayer = OPTIONS.IsolateLocalPlayer - local IsolateLocalPlayerCharacter = OPTIONS.IsolateLocalPlayerCharacter - local IsolateStarterPlayer = OPTIONS.IsolateStarterPlayer - local IsolatePlayers = OPTIONS.IsolatePlayers - - local SaveNonCreatable = OPTIONS.SaveNonCreatable - local TreatUnionsAsParts = OPTIONS.TreatUnionsAsParts - - local DecompileJobless = OPTIONS.DecompileJobless - local ScriptCache = OPTIONS.scriptcache and getscriptbytecode - - local Timeout = OPTIONS.timeout - - local IgnoreSharedStrings = OPTIONS.IgnoreSharedStrings - local SharedStringOverwrite = OPTIONS.SharedStringOverwrite - local NilInstances = OPTIONS.NilInstances - - if NilInstances and enablenilinstances then -- ? Solara fix - enablenilinstances() - end - - local getbytecode - if getscriptbytecode then - getbytecode = construct_TimeoutHandler(3, getscriptbytecode) -- ? Solara fix - end - - local SaveBytecode - if OPTIONS.SaveBytecode and getscriptbytecode then - SaveBytecode = function(script) - local s, bytecode = getbytecode(script) - - if s then - if bytecode and bytecode ~= "" then - return "-- Bytecode (Base64):\n-- " .. base64encode(bytecode) .. "\n\n" - end - end - end - end - - local ldeccache = GLOBAL_ENV.scriptcache - - local DecompileIgnoring, ToSaveList, ldecompile, placename, elapse_t, SaveNonCreatableWillBeEnabled, RecoveredScripts - - if ScriptCache and not ldeccache then - ldeccache = {} - GLOBAL_ENV.scriptcache = ldeccache - end - - if ToSaveInstance == game then - OPTIONS.mode = "full" - ToSaveInstance = nil - IsModel = nil - end - - local function isLuaSourceContainer(instance) - return instance:IsA("LuaSourceContainer") - end - - do - local mode = string.lower(OPTIONS.mode) - local tmp = table.clone(OPTIONS.ExtraInstances) - - local PlaceName = game.PlaceId - - pcall(function() - PlaceName ..= " " .. service.MarketplaceService:GetProductInfo(PlaceName).Name - end) - - local function sanitizeFileName(str) - return string.sub(string.gsub(string.gsub(string.gsub(str, "[^%w _]", ""), " +", " "), " +$", ""), 1, 240) - end - - if IsModel then - if mode == "optimized" then -- ! NOT supported with Model file mode - mode = "full" - end - - for _, key in - { - "IsolateLocalPlayer", - "IsolateLocalPlayerCharacter", - "IsolatePlayers", - "IsolateStarterPlayer", - "NilInstances", - } - do - if CustomOptions[key] == nil and CustomOptions[GetAlias(key)] == nil then - OPTIONS[key] = false - end - end - - placename = ( - FilePath or sanitizeFileName("model " .. PlaceName .. " " .. (ToSaveInstance or tmp[1]):GetFullName()) - ) .. ".rbxmx" - else - placename = (FilePath or sanitizeFileName("place " .. PlaceName)) .. ".rbxlx" - end - - if mode ~= "scripts" then - IgnorePropertiesOfNotScriptsOnScriptsMode = nil - end - - local TempRoot = ToSaveInstance or game - - if mode == "full" then - local Children = TempRoot:GetChildren() - if 0 < #Children then - table.move(Children, 1, #Children, #tmp + 1, tmp) - end - elseif mode == "optimized" then -- ! Incompatible with .rbxmx (Model file) mode - -- if IsolatePlayers then - -- table.insert(_list_0, "Players") - -- end - for _, serviceName in - { - "Workspace", - "Players", - "Lighting", - "MaterialService", - "ReplicatedFirst", - "ReplicatedStorage", - - "ServerScriptService", -- ? Why - "ServerStorage", -- ? Why - - "StarterGui", - "StarterPack", - "StarterPlayer", - "Teams", - "SoundService", - "TextChatService", - "Chat", - - -- "InsertService", - "JointsService", - - "LocalizationService", -- For LocalizationTables - -- "TestService", - -- "VoiceChatService", - } - do - table.insert(tmp, service[serviceName]) - end - elseif mode == "scripts" then - -- TODO: Only save paths that lead to scripts (nothing else) - -- Currently saves paths along with children of each tree - local unique = {} - for _, instance in TempRoot:GetDescendants() do - if isLuaSourceContainer(instance) then - local Parent = instance.Parent - while Parent and Parent ~= TempRoot do - instance = instance.Parent - Parent = instance.Parent - end - if Parent then - unique[instance] = true - end - end - end - for instance in unique do - table.insert(tmp, instance) - end - end - ToSaveList = tmp - end - - local function get_size_format() - local Size - - -- local totalsize = #totalstr - - for i, unit in - { - "B", - "KB", - "MB", - "GB", - "TB", - } - do - if totalsize < 0x400 ^ i then - Size = math.floor(totalsize / (0x400 ^ (i - 1)) * 10) / 10 .. " " .. unit - break - end - end - - return Size - end - - local function wait_for_render() - service.RunService.RenderStepped:Wait() - end - - local Loading - local function run_with_loading(text, keepStatus, waitForRender, taskFunction, ...) - local previousStatus - - if StatusText then - if keepStatus then - previousStatus = StatusText.Text - end - Loading = task.spawn(function() - local spinner_count = 0 - local chars = { "|", "/", "—", "\\" } - local chars_size = #chars - - local function getLoadingText() - spinner_count += 1 - - if chars_size < spinner_count then - spinner_count = 1 - end - - return chars[spinner_count] - end - - text ..= " " - - while true do - StatusText.Text = text .. getLoadingText() - task.wait(0.25) - end - end) - if waitForRender then - wait_for_render() - end - end - - local result = { taskFunction(...) } - - if Loading then - task.cancel(Loading) - Loading = nil - if previousStatus then - StatusText.Text = previousStatus - end - end - - return unpack(result) - end - - local get_proxied_linkedsource = construct_TimeoutHandler(30, function(asset) - return game:HttpGet("https://linkedsource.glitch.me/asset/" .. asset) - end) - - do - local Decompiler = OPTIONS.decomptype == "custom" and custom_decompiler - or global_container.decompile - or custom_decompiler - - -- if Decompiler == custom_decompiler then -- Cope - -- local key = "DecompileTimeout" - -- if CustomOptions[key] == nil then - -- local Option = GetAlias(key) - -- if CustomOptions[Option] == nil then - -- Timeout = 1 - -- end - -- end - - -- end - - if OPTIONS.noscripts then - ldecompile = function() - return "-- Decompiling is disabled" - end - elseif Decompiler then - local decomp = construct_TimeoutHandler(Timeout, Decompiler, "Decompiler timed out") - - ldecompile = function(script) - -- local name = scr.ClassName .. scr.Name - local hashed_bytecode - if ScriptCache then - local s, bytecode = getbytecode(script) -- TODO This is awful because we already do this in Custom Decomp (when we are using it, that is) - local cached - - if s then - if not bytecode or bytecode == "" then - return "-- The Script is Empty" - end - hashed_bytecode = sha384(bytecode) - cached = ldeccache[hashed_bytecode] - end - - if cached then - if __DEBUG_MODE then - __DEBUG_MODE("Found in Cache", script:GetFullName()) - end - return cached - elseif DecompileJobless then - return "-- Not found in already decompiled ScriptCache" - end - else - task.wait() -- TODO Maybe remove? - end - - local ok, result = run_with_loading("Decompiling " .. script.Name, true, nil, decomp, script) - if not result then - ok, result = false, "Empty Output" - end - - local output - if ok then - result = string.gsub(result, "\0", "\\0") -- ? Some decompilers sadly output \0 which prevents files from opening - output = result - else - output = "--[[ Failed to decompile. Reason:\n" .. (result or "") .. "\n]]" - end - - if ScriptCache and hashed_bytecode then -- TODO there might(?) be an edgecase where it manages to decompile (built-in) even though getscriptbytecode failed, and the output won't get cached - ldeccache[hashed_bytecode] = output -- ? Should we cache even if it timed out? - if __DEBUG_MODE then - __DEBUG_MODE("Cached", script:GetFullName()) - end - end - - return output - end - else - ldecompile = function() - return "-- Decompiling is NOT supported on your executor" - end - end - end - - local function getsafeproperty(instance, propertyName) - return instance[propertyName] - end - - local function unfilterResult(category, optional) - return category ~= "Class" and not optional - end - - local __BREAK = "__BREAK" .. service.HttpService:GenerateGUID(false) - - local function ReadProperty(instance, property, propertyName, special, category, optional) - local raw = __BREAK - - local InstanceOverride = InstancePropertyOverrides[instance] - if InstanceOverride then - local PropertyOverride = InstanceOverride.Properties[propertyName] - if PropertyOverride then - return PropertyOverride - end - end - - local CanRead = property.CanRead - - if CanRead == false then -- * Skips because we've checked this property before - return __BREAK - end - - local function filterResult(result) -- ? raw == nil thanks to SerializedDefaultAttributes; "can't get value" - due to WriteOnly tag; "Invalid value for enum " - "StreamingPauseMode" (old games probably) Roexec - return result == nil - or result == "can't get value" - or type(result) == "string" - and (string_find(result, "Unable to get property " .. propertyName) or category == "Enum" and string_find( - result, - "Invalid value for enum " - )) - end - - if special then - if gethiddenproperty then - local ok, result = pcall(gethiddenproperty, instance, propertyName) - - if ok then - raw = result - end - - if filterResult(raw) then - -- * Skip next time we encounter this too perhaps (unless there's a chance for it to be readable on other instance, somehow) - - if result ~= nil or unfilterResult(category, optional) then - if __DEBUG_MODE then - __DEBUG_MODE("Filtered", propertyName) - end - -- Property.Special = false - property.CanRead = false - end - - return __BREAK -- ? We skip it because even if we use "" it will just reset to default in most cases, unless it's a string tag for example (same as not being defined) - end - end - else - if CanRead then - raw = instance[propertyName] - else -- Assuming CanRead == nil (untested) - local ok, result = pcall(getsafeproperty, instance, propertyName) - - if ok then - raw = result - elseif gethiddenproperty then -- ! Be careful with this 'and gethiddenproperty' logic - ok, result = pcall(gethiddenproperty, instance, propertyName) - - if ok then - raw = result - - property.Special = true - end - end - - property.CanRead = ok - - if not ok or filterResult(raw) then - return __BREAK - end - end - end - - return raw - end - - local function ReturnItem(className, instance) - local ref = referents[instance] - if not ref then - ref = ref_size - referents[instance] = ref - ref_size += 1 - end - - return '' -- TODO: Ideally this shouldn't return as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is Enabled OR If all properties are default (reduces file size by at least 1.4%) - end - - local function ReturnProperty(tag, propertyName, value) - return "<" .. tag .. ' name="' .. propertyName .. '">' .. value .. "" - end - - local function ReturnValueAndTag(raw, valueType, descriptor) - local value, tag = (descriptor or XML_Descriptors[valueType])(raw) - - return value, tag == nil and valueType or tag - end - - local function InheritsFix(fixes, className, instance) - local Fix = fixes[className] - if Fix then - return Fix - elseif Fix == nil then - for class_name, fix in fixes do - if instance:IsA(class_name) then - return fix - end - end - end - end - - local function GetInheritedProps(className) - local prop_list = {} - local layer = ClassList[className] - while layer do - local layer_props = layer.Properties - table.move(layer_props, 1, #layer_props, #prop_list + 1, prop_list) - - -- for _, prop in layer.Properties do - -- prop_list[prop_count] = prop -- ? table.clone is needed for case where .Default is modified - -- prop_count += 1 - -- end - - layer = ClassList[layer.Superclass] - end - inherited_properties[className] = prop_list - return prop_list - end - - local function save_cache() - local savestr = table.concat(savebuffer) - totalstr ..= savestr -- TODO: Causes "not enough memory" error on some exec - - -- writefile(placename, totalstr) - -- appendfile(placename, savestr) -- * supposedly causes uneven amount of Tags (e.g. must be closed with but sometimes there's more of one than the other). While being under load, the function produces unexpected output? - totalsize += #savestr - - table.clear(savebuffer) - savebuffer_size = 1 - - if StatusText then - StatusText.Text = "Saving.. Size: " .. get_size_format() - end - -- ? Needed for at least 1fps (status text) - -- task.wait() - wait_for_render() - end - - local function save_specific(className, properties) - local Ref = Instance.new(className) -- ! Assuming anything passed here is Creatable - local Item = ReturnItem(Ref.ClassName, Ref) - - for propertyName, val in properties do - local Class, value, tag - - -- TODO: Improve all sort of overrides & exceptions in the code (code below is awful) - if "Source" == propertyName then - tag = "ProtectedString" - value = XML_Descriptors.__PROTECTEDSTRING(val) - Class = "Script" - elseif "Name" == propertyName then - Class = "Instance" - value, tag = ReturnValueAndTag(val, "string") -- * Doubt ValueType will change - end - - if Class then - Item ..= ReturnProperty(tag, propertyName, value) - end - end - Item ..= "" - return Item - end - - local function save_hierarchy(hierarchy, queuedHierarchy) - for _, instance in hierarchy do - if IgnoreNotArchivable and not instance.Archivable then - continue - end - - local SkipEntirely = IgnoreList[instance] - if SkipEntirely then - continue - end - - local ClassName = instance.ClassName - - local InstanceName = instance.Name - - do - local OnIgnoredList = IgnoreList[ClassName] - if OnIgnoredList and (OnIgnoredList == true or OnIgnoredList[InstanceName]) then - continue - end - end - - if not DecompileIgnoring then - DecompileIgnoring = DecompileIgnore[instance] - - if DecompileIgnoring == nil then - local DecompileIgnored = DecompileIgnore[ClassName] - if DecompileIgnored then - DecompileIgnoring = DecompileIgnored == true or DecompileIgnored[InstanceName] - end - end - - if DecompileIgnoring then - DecompileIgnoring = instance - elseif DecompileIgnoring == false then - DecompileIgnoring = 1 -- Ignore one instance - end - end - - local InstanceOverride, ClassNameOverride, ClassTagOverride - - do - local function replaceClassName(newClassName) - if InstanceName ~= ClassName then -- TODO Compare against default instance instead (TouchTransmitter is called TouchInterest by default) - InstanceOverride = InstancePropertyOverrides[instance] - if not InstanceOverride then - InstanceOverride = { Properties = { Name = "[" .. ClassName .. "] " .. InstanceName } } - InstancePropertyOverrides[instance] = InstanceOverride - end - end - ClassName = newClassName - end - - local Fix = NotCreatableFixes[ClassName] - - if Fix then - if SaveNonCreatable then - replaceClassName(Fix) - else - continue -- They won't show up in Studio anyway (Enable IsolatePlayers if you wish to bypass this) - end - else -- ! Assuming nothing that is a PartOperation or inherits from it is in NotCreatableFixes - if TreatUnionsAsParts and instance:IsA("PartOperation") then - replaceClassName("Part") - ClassNameOverride = "BasePart" -- * Mutual Superclass for PartOperation and Part; For properties only - elseif not ClassList[ClassName] then -- ? API Dump is outdated then - if __DEBUG_MODE then - __DEBUG_MODE("Class not Found", ClassName) - end - - ClassTagOverride = ClassName -- ? To at least retain .ClassName unlike the rest of the class-specific properties - ClassName = "Folder" -- ? replaceClassName is not needed because of the ClassTagOverride - end - end - end - - if not InstanceOverride then - InstanceOverride = InstancePropertyOverrides[instance] - end - local ChildrenOverride = InstanceOverride and InstanceOverride.__Children - - if ClassName == "" then -- * FilteredSelection - ClassName = "Folder" - end - - -- ? The reason we only save .Name (and few other props in save_specific) is because - -- ? we can be sure this is a custom container (ex. NilInstancesFixes) - -- ? However, in case of NotCreatableFixes, the Instance might have Tags, Attributes etc. that can potentially be saved (even though it's a Folder) - if ChildrenOverride then - savebuffer[savebuffer_size] = save_specific(ClassName, InstanceOverride.Properties) -- ! Assuming anything that has __Children will have .Properties - savebuffer_size += 1 - else - -- local Properties = - savebuffer[savebuffer_size] = ReturnItem(ClassTagOverride or ClassName, instance) -- TODO: Ideally this shouldn't return as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is ENABLED - savebuffer_size += 1 - if not (IgnorePropertiesOfNotScriptsOnScriptsMode and not isLuaSourceContainer(instance)) then - local default_instance, new_def_inst - - if IgnoreDefaultProperties then - default_instance = default_instances[ClassName] - if not default_instance then - local ClassTags = ClassList[ClassName].Tags - if not (ClassTags and ClassTags.NotCreatable) then -- __api_dump_class_not_creatable__ also indicates this - new_def_inst = Instance.new(ClassName) -- ! Assuming anything that doesn't have NotCreatable is possible to create (therefore no pcall) - - default_instance = {} - - default_instances[ClassName] = default_instance - elseif __DEBUG_MODE then - __DEBUG_MODE("Unable to create default Instance", ClassName) - end - end - end - local proplist = inherited_properties[ClassNameOverride or ClassName] - if not proplist then - proplist = GetInheritedProps(ClassNameOverride or ClassName) - inherited_properties[ClassNameOverride or ClassName] = proplist - end - for _, Property in proplist do - local PropertyName = Property.Name - - if IgnoreProperties[PropertyName] then - continue - end - - local ValueType = Property.ValueType - - if IgnoreSharedStrings and ValueType == "SharedString" then -- ? More info in Options - continue - end - - local Category, Optional, Special = Property.Category, Property.Optional, Property.Special - - local raw = ReadProperty(instance, Property, PropertyName, Special, Category, Optional) - - if raw == __BREAK then -- ! Assuming __BREAK is always returned when there's a failure to read a property - local ok, result = pcall(gethiddenproperty_fallback, instance, PropertyName) -- * This helps in reading: Vector3int16, OptionalCoordinateFrame DataTypes. It also acts as an almost entire fallback for gethiddenproperty in case it is missing - - if result == nil and unfilterResult(Category, Optional) then - ok = nil - end - - if ok then - raw = result - else - local Fallback = Property.Fallback - - if Fallback then - ok, result = pcall(Fallback, instance) - - if ok then - raw = result - else - if __DEBUG_MODE then - -- TODO Maybe remove the fix during runtime if it fails to avoid re-trying - __DEBUG_MODE("Fix Failed", PropertyName) - end - continue - end - else - continue - end - end - end - - if SharedStringOverwrite and ValueType == "BinaryString" then -- TODO: Convert this to table if more types are added - ValueType = "SharedString" - end - - -- Special = Property.Special -- ? Read TODO below (must be updated if it's used frequently afterwards) - - if - default_instance - and not Property.Special -- TODO: .Special is checked more than once (because it might be updated during ReadProperty) - and not (PropertyName == "Source" and isLuaSourceContainer(instance)) - then -- ? Could be not just "Source" in the future - if new_def_inst then - default_instance[PropertyName] = getsafeproperty(new_def_inst, PropertyName) - end - if default_instance[PropertyName] == raw then - continue - end - -- local ok, IsModified = pcall(IsPropertyModified, instance, PropertyName) -- ? Not yet enabled lol (580) - end - - -- Serialization start - - local tag, value - if Category == "Class" then - tag = "Ref" - if raw then - if SaveNonCreatableWillBeEnabled then - local Fix = NotCreatableFixes[raw.ClassName] - if - Fix - and ( - PropertyName == "PlayerToHideFrom" - or ValueType ~= "Instance" and ValueType ~= Fix - ) - then - -- * To avoid errors - continue - end - end - - value = referents[raw] - if not value then - value = ref_size - referents[raw] = value - ref_size += 1 - end - 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 = XML_Descriptors.__ENUM(raw) - else - local Descriptor = XML_Descriptors[ValueType] - - if Descriptor then - value, tag = ReturnValueAndTag(raw, ValueType, Descriptor) - elseif "ProtectedString" == ValueType then -- TODO: Try fitting this inside Descriptors - tag = ValueType - - if PropertyName == "Source" then - if DecompileIgnoring then -- ? Should this really prevent extraction of the original source if present ? - if DecompileIgnoring == 1 then - DecompileIgnoring = nil - end - value = "-- Ignored" - else - local should_decompile = true - local LinkedSource - local LinkedSource_Url = instance.LinkedSource -- ! Assuming every Class that has ProtectedString Source property also has a LinkedSource property - local hasLinkedSource = LinkedSource_Url ~= "" - if hasLinkedSource then - local Path = instance:GetFullName() - if RecoveredScripts then - table.insert(RecoveredScripts, Path) - else - RecoveredScripts = { Path } - end - - do -- TODO Temporary fix for crashes (getscriptbytecode calls on scripts with LinkedSource), originally was inside the "ok" block below - should_decompile = nil - value = "" - end - - LinkedSource = string.match(LinkedSource_Url, "%w+$") -- TODO: No sure if this pattern matches all possible cases. Example is: 'rbxassetid://0&hash=cd73dd2fe5e5013137231c227da3167e' - if LinkedSource then - local cached = ldeccache[LinkedSource] - - if cached then - value = cached - should_decompile = nil - elseif DecompileJobless then - value = "-- Not found in already decompiled ScriptCache" - should_decompile = nil - end - - local function filterResults(str) - local o, r = - pcall(service.HttpService.JSONDecode, service.HttpService, str) - if o and r.errors then - return false - end - return true - end - - local asset = (string.find(LinkedSource, "%a") and "hash" or "id") - .. "=" - .. LinkedSource - - local source - local ok = pcall(function() - -- Credits @halffalse - source = game:HttpGet( - "https://assetdelivery.roproxy.com/v1/asset/?" .. asset - ) - end) - - if ok then - ok = filterResults(source) - end - - if not ok then - ok, source = run_with_loading( - "Getting " .. instance.Name .. " LinkedSource", - true, - nil, - get_proxied_linkedsource, - asset - ) - end - - if ok then - ok = filterResults(source) - end - - if ok then - ldeccache[LinkedSource] = source - - value = source - - -- should_decompile = nil - end - else --if __DEBUG_MODE then -- * We print this anyway because very important - warn( - "FAILED TO EXTRACT ORIGINAL SCRIPT SOURCE (OPEN A GITHUB ISSUE): ", - instance:GetFullName(), - LinkedSource_Url - ) - end - end - - if should_decompile then - local isLocalScript = instance:IsA("LocalScript") - if - isLocalScript and instance.RunContext == Enum.RunContext.Server - or not isLocalScript - and instance:IsA("Script") - and instance.RunContext ~= Enum.RunContext.Client - then - value = "-- Server Scripts are IMPOSSIBLE to save" --TODO: Could be not just server scripts in the future - else - value = ldecompile(instance) - if SaveBytecode then - local output = SaveBytecode(instance) - if output then - value = output .. value - end - end - end - end - - value = "-- Saved by UniversalSynSaveInstance https://discord.gg/wx4ThpAsmw\n\n" - .. (hasLinkedSource and "-- Original Source: https://assetdelivery.roblox.com/v1/asset/?id=" .. (LinkedSource or LinkedSource_Url) .. "\n\n" or "") - .. value - end - end - value = XML_Descriptors.__PROTECTEDSTRING(value) - else - --OptionalCoordinateFrame and so on, we make it dynamic - - if Optional then - Descriptor = XML_Descriptors[Optional] - - if Descriptor then - if raw == nil then - -- * It can be empty, because it's optional - -- ? Though why even save it if it's empty considering it's optional - continue - -- value, tag = "", ValueType - else - value, tag = ReturnValueAndTag(raw, ValueType, Descriptor) - end - end - end - end - end - - if tag then - savebuffer[savebuffer_size] = ReturnProperty(tag, PropertyName, value) - savebuffer_size += 1 - else --if __DEBUG_MODE then -- * We print this anyway because very important - warn("UNSUPPORTED TYPE (OPEN A GITHUB ISSUE): ", ValueType, ClassName, PropertyName) - end - end - end - savebuffer[savebuffer_size] = "" - savebuffer_size += 1 - - if SaveCacheInterval < savebuffer_size then - save_cache() - end - end - - if SkipEntirely ~= false then -- ? We save instance without it's descendants in this case (== false) - local Children = ChildrenOverride or queuedHierarchy or instance:GetChildren() - - if #Children ~= 0 then - save_hierarchy(Children) - end - end - - if DecompileIgnoring and DecompileIgnoring == instance then - DecompileIgnoring = nil - end - - savebuffer[savebuffer_size] = "" - savebuffer_size += 1 - end - end - - local function save_extra(name, hierarchy, customClassName, source) - savebuffer[savebuffer_size] = save_specific((customClassName or "Folder"), { Name = name, Source = source }) - savebuffer_size += 1 - if hierarchy then - save_hierarchy(hierarchy) - end - savebuffer[savebuffer_size] = "" - savebuffer_size += 1 - end - - local function save_game() - writefile(placename, "") - - if IsModel then - savebuffer[savebuffer_size] = 'true' - savebuffer_size += 1 - end - --[[ - -- ? Roblox encodes the following additional attributes. These are not required. Moreover, any defined schemas are ignored, and not required for a file to be valid: xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" - Also http can be converted to https but not sure if Roblox would decide to detect that - -- ? nullnil - is a legacy concept that is no longer used. - ]] - - -- TODO Find a better solution for this - SaveNonCreatableWillBeEnabled = SaveNonCreatable - or (IsolateLocalPlayer or IsolateLocalPlayerCharacter) and IsolateLocalPlayer - or IsolatePlayers - or NilInstances and global_container.getnilinstances -- ! Make sure this accurately reflects everything below - if ToSaveInstance then - save_hierarchy({ ToSaveInstance }, ToSaveList) - else - save_hierarchy(ToSaveList) - end - - if IsolateLocalPlayer or IsolateLocalPlayerCharacter then - local Players = service.Players - local LocalPlayer = Players.LocalPlayer - if IsolateLocalPlayer then - SaveNonCreatable = true - save_extra("LocalPlayer", LocalPlayer:GetChildren()) - end - if IsolateLocalPlayerCharacter then - local LocalPlayerCharacter = LocalPlayer.Character - if LocalPlayerCharacter then - save_extra("LocalPlayer Character", LocalPlayerCharacter:GetChildren()) - end - end - end - - if IsolateStarterPlayer then - -- SaveNonCreatable = true -- TODO: Enable if StarterPlayerScripts or StarterCharacterScripts stop showing up in isolated folder in Studio - save_extra("StarterPlayer", service.StarterPlayer:GetChildren()) - end - - if IsolatePlayers then - SaveNonCreatable = true - save_extra("Players", service.Players:GetChildren()) - end - - if NilInstances and global_container.getnilinstances then - local nil_instances, nil_instances_size = {}, 1 - - local NilInstancesFixes = OPTIONS.NilInstancesFixes - - for _, instance in global_container.getnilinstances() do - if instance == game then - instance = nil - -- break - else - local ClassName = instance.ClassName - - local Fix = InheritsFix(NilInstancesFixes, ClassName, instance) - - if Fix then - instance = Fix(instance, InstancePropertyOverrides) - -- continue - end - - local Class = ClassList[ClassName] - if Class then - local ClassTags = Class.Tags - if ClassTags and ClassTags.Service then -- For CSGDictionaryService, NonReplicatedCSGDictionaryService, LogService, ProximityPromptService, TestService & more - -- instance.Parent = game - instance = nil - -- continue - end - end - end - if instance then - nil_instances[nil_instances_size] = instance - nil_instances_size += 1 - end - end - SaveNonCreatable = true - save_extra("Nil Instances", nil_instances) - end - - if OPTIONS.ReadMe then - save_extra( - "README", - nil, - "Script", - "--[[\n" - .. (RecoveredScripts and "\t\tIMPORTANT: Original Source of these Scripts was Recovered: " .. service.HttpService:JSONEncode( - RecoveredScripts - ) .. "\n" or "") - .. [[ - Thank you for using UniversalSynSaveInstance https://discord.gg/wx4ThpAsmw. - - If you didn't save in Binary - we recommended to save the game right away to take advantage of the binary format & to preserve values of certain properties if you used IgnoreDefaultProperties setting (as they might change in the future). - You can do that by going to FILE -> Save to File As -> Make sure File Name ends with .rbxl -> Save - - If your player cannot spawn into the game, please move the scripts in StarterPlayer somewhere else. Then run `game:GetService("Players").CharacterAutoLoads = true`. - And use "Play Here" to start game instead of "Play" to spawn your Character where your Camera currently is. - - If the chat system does not work, please use the explorer and delete everything inside the TextChatService/Chat service(s). - Or run `game:GetService("Chat"):ClearAllChildren()` - - If Union and MeshPart collisions don't work, run the script below in the Studio Command Bar: - - - local C = game:GetService("CoreGui") - local D = Enum.CollisionFidelity.Default - - for _, v in game:GetDescendants() do - if v:IsA("TriangleMeshPart") and not v:IsDescendantOf(C) then - v.CollisionFidelity = D - end - end - print("Done") - - If you can't move the Camera, run this script in the Studio Command Bar: - - workspace.CurrentCamera.CameraType = Enum.CameraType.Fixed - - Or Destroy the Camera. - - This file was generated with the following settings: - ]] - .. service.HttpService:JSONEncode(OPTIONS) - .. "\n\n\t\tElapsed time: " - .. os.clock() - elapse_t - .. " PlaceId: " - .. game.PlaceId - .. " Executor: " - .. (identify_executor and table.concat({ identify_executor() }, " ") or "Unknown") - .. "\n]]" - ) - end - do - local tmp = { "" } - for identifier, value in SharedStrings do - table.insert(tmp, '' .. value .. "") - end - - if 1 < #tmp then -- TODO: This sucks so much because we try to iterate a table just to check this (check above) - savebuffer[savebuffer_size] = table.concat(tmp) - savebuffer_size += 1 - savebuffer[savebuffer_size] = "" - savebuffer_size += 1 - end - end - - savebuffer[savebuffer_size] = "" - savebuffer_size += 1 - save_cache() - do - -- ! Assuming we only write to file once hence why we only filter once - -- TODO This might cause issues on non-unique Usernames (ex. "Cake" if game is about cakes then everything supposedly related to your name will be replaced with "Roblox"); Certain UserIds might also affect numbers, like if your UserId is 2481848 and there is some number that goes like "1.248184818837" then that the matched part will be replaced with 1, potentially making the number incorrect. - -- TODO So for now it's best to keep this disabled by default - -- TODO It's also not smart to filter entire file string at the end as this might also affect decompiled scripts content, which has no way of containing any user-related information. It would be better to use gsub in string Descriptor and such - if OPTIONS.Anonymous then - local LocalPlayer = service.Players.LocalPlayer - - totalstr = string.gsub(string.gsub(totalstr, LocalPlayer.UserId, "1"), LocalPlayer.Name, "Roblox") - end - - if OPTIONS.AlternativeWritefile and appendfile then - local SegmentLength = 4145728 -- Celery has an arbitrary savefile/appendfile size limit of ~4MB for reasons unknown. This is a workaround to save the file in segments. - local Length = math.ceil(totalsize / SegmentLength) - - for i = 1, Length do - local savestr = string.sub(totalstr, (i - 1) * SegmentLength + 1, i * SegmentLength) - - run_with_loading( - "Writing to File " .. math.round(i / Length * 100) .. "% (Depends on Exec)", - nil, - true, - appendfile, - placename, - savestr - ) - if i ~= Length then - task.wait() - end - end - else - run_with_loading( - "Writing " .. get_size_format() .. " to File (Depends on Exec)", - nil, - true, - writefile, - placename, - totalstr - ) - end - end - table.clear(SharedStrings) - end - - local Connections - do - local Players = service.Players - local LocalPlayer = Players.LocalPlayer - - if IgnoreList.Model ~= true then - Connections = {} - local function ignoreCharacter(player) - table.insert( - Connections, - player.CharacterAdded:Connect(function(character) - IgnoreList[character] = true - end) - ) - - local Character = player.Character - if Character then - IgnoreList[Character] = true - end - end - - if OPTIONS.RemovePlayerCharacters then - table.insert( - Connections, - Players.PlayerAdded:Connect(function(player) - ignoreCharacter(player) - end) - ) - for _, player in Players:GetPlayers() do - ignoreCharacter(player) - end - else - IgnoreNotArchivable = false -- TODO Bad solution (Characters are NotArchivable); Also make sure the next solution is compatible with IsolateLocalPlayerCharacter - if IsolateLocalPlayerCharacter then - ignoreCharacter(LocalPlayer) - end - end - end - if IsolateLocalPlayer and IgnoreList.Player ~= true then - IgnoreList[LocalPlayer] = true - end - end - - if IsolateStarterPlayer then - IgnoreList.StarterPlayer = false - end - - if IsolatePlayers then - IgnoreList.Players = false - end - - if OPTIONS.ShowStatus then - do - local Exists = GLOBAL_ENV._statustext - if Exists then - Exists:Destroy() - end - end - - local StatusGui = Instance.new("ScreenGui") - - GLOBAL_ENV._statustext = StatusGui - - StatusGui.DisplayOrder = 2_000_000_000 - pcall(function() -- ? Compatibility with level 2 - StatusGui.OnTopOfCoreBlur = true - end) - - StatusText = Instance.new("TextLabel") - - StatusText.Text = "Saving..." - - StatusText.BackgroundTransparency = 1 - StatusText.Font = Enum.Font.Code - StatusText.AnchorPoint = Vector2.new(1) - StatusText.Position = UDim2.new(1) - StatusText.Size = UDim2.new(0.3, 0, 0, 20) - - StatusText.TextColor3 = Color3.new(1, 1, 1) - StatusText.TextScaled = true - StatusText.TextStrokeTransparency = 0.7 - StatusText.TextXAlignment = Enum.TextXAlignment.Right - StatusText.TextYAlignment = Enum.TextYAlignment.Top - - StatusText.Parent = StatusGui - - local function randomString() - local length = math.random(10, 20) - local randomarray = table.create(length) - for i = 1, length do - randomarray[i] = string.char(math.random(32, 126)) - end - return table.concat(randomarray) - end - - if global_container.gethui then - StatusGui.Name = randomString() - StatusGui.Parent = global_container.gethui() - elseif global_container.protectgui then - StatusGui.Name = randomString() - global_container.protectgui(StatusGui) - StatusGui.Parent = service.CoreGui - else - local RobloxGui = service.CoreGui:FindFirstChild("RobloxGui") - if RobloxGui then - StatusGui.Parent = RobloxGui - else - StatusGui.Name = randomString() - StatusGui.Parent = service.CoreGui - end - end - end - - do - local SafeMode = OPTIONS.SafeMode - if SafeMode then - service.Players.LocalPlayer:Kick("\n[SAFEMODE] Saving in Progress..\nPlease do NOT leave") - service.RunService:Set3dRenderingEnabled(false) - wait_for_render() - task.delay(10, service.GuiService.ClearError, service.GuiService) - end - - local anti_idle - if OPTIONS.AntiIdle then - anti_idle = service.Players.LocalPlayer.Idled:Connect(function() - service.VirtualInputManager:SendMouseWheelEvent( - service.UserInputService:GetMouseLocation().X, - service.UserInputService:GetMouseLocation().Y, - true, - nil - ) - end) - end - - elapse_t = os.clock() - - local ok, err = xpcall(save_game, function(err) - return debug.traceback(err) - end) - - if SafeMode then - service.RunService:Set3dRenderingEnabled(true) - end - - if old_gethiddenproperty then - gethiddenproperty = old_gethiddenproperty - end - - if anti_idle then - anti_idle:Disconnect() - end - if Connections then - for _, connection in Connections do - connection:Disconnect() - end - end - -- GLOBAL_ENV.__USSI = nil - if StatusText then - task.spawn(function() - elapse_t = os.clock() - elapse_t - local Log10 = math.log10(elapse_t) - local ExtraTime = 10 - if ok then - StatusText.Text = string.format("Saved! Time %.3f seconds; Size %s", elapse_t, get_size_format()) - StatusText.TextColor3 = Color3.new(0, 1) - task.wait(Log10 * 2 + ExtraTime) - else - if Loading then - task.cancel(Loading) - Loading = nil - end - StatusText.Text = "Failed! Check F9 console for more info" - StatusText.TextColor3 = Color3.new(1) - warn("Error found while saving:") - warn(err) - task.wait(Log10 + ExtraTime) - end - StatusText:Destroy() - end) - end - end -end - -return synsaveinstance diff --git a/tests/test2_saveinstance.luau b/test2_saveinstance.luau similarity index 100% rename from tests/test2_saveinstance.luau rename to test2_saveinstance.luau diff --git a/tests/test_saveinstance.luau b/test_saveinstance.luau similarity index 100% rename from tests/test_saveinstance.luau rename to test_saveinstance.luau