⚡ SCRIPT EXECUTION - modedp/script-ware-documentation GitHub Wiki
All of Script-Ware's platforms are supercharged by its incredible script execution engine, developed by multiple veteran developers over a long course of time. It is one of the best Lua engines available on the market and it is absolutely optimised and featured to provide a smooth running experience.
Script-Ware allows for fully unrestricted and secure script execution on the client.
We'll be evidently diving into every part of what you can achieve using our awesome execution engine, but the following awesome features are what Script-Ware comes with:
- We have implemented 150+ viable (proprietary) custom implementations to our script environment, allowing for an incredible amount of scalability.
- This means that our environment has been extended to offer compatibility for extremely popular scripts alongside providing a capability to develop extraordinarily advanced scripts.
- Furthermore, Script-Ware has extremely special features that allow for insane script efficiency, such as the bitstream library
- Script-Ware is extremely regulated and organised to handle stress and to prevent unexpected crashes during it's execution process.
- Everything is documented (goes without saying)! We've taken pride in ensuring you are absolutely aware of what every single thing means in your product.
Let's begin the documentation for our script engine.
All scripts are provided with a script identity which details their permissions - what they can and cannot do. These are set to restrict generic scripts to performing malicious activity by abusing APIs.
Unlike generic scripts, Script-Ware runs scripts at a script identity level of 8 (which was previously 6 or 7, hence the term "Level 7" - Script-Ware is always a step ahead 😎). Scripts running at this level allow for complete access to every API and have scot-free access to every permission.
You can test this by quickly running the following script in Script-Ware: printidentity()
If you run this same script within an in-game script/localscript - it will usually have an identity of 2.
With Script-Ware's elevated script identity, you can receive elevated access to perform things such like being able to place GUI's within game:GetService("CoreGui") - a place where the client will typically never have access to thus making it far safer to use.
However, even better is our gethui function, of which provides you a GUI container that is even harder to access than CoreGui - completely sliding past any and all anti-cheats.
Every single platform we have supported will have an elevated script identity to eight. Utilise this.
With this information understood, we can start diving into the vast functionality Script-Ware offers.
This section will display all the documentation for every Script-Ware implemented, custom, function.
Although the vast majority of these functions are on every Script-Ware platform, some anomalies may lie where-by a function is not actively supported by a certain Script-Ware platform. Be advised, we know! We will eventually implement said functions across all platforms.
However, some of these functions cannot be created on macOS due to restrictions and differences between both operating systems.
void naturally refers to a function that does not return anything.
[type] refers to an optional parameter, one that does not need to have anything passed to it.
any refers to it taking any type of datatype within its argument.
Multiple declarations for a function indicate that they are the same functions (typically for compatibility)
These functions are typically special to Script-Ware in the sense that they provide information about Script-Ware or load scripts via Script-Ware.
string getexecutorname()
string identifyexecutor()
Simply returns "ScriptWare" to signal the identity of the executor that is running the script.
void import(int scriptnumber)
Executes the script from the script catalogue as if you were running it from the executor. To get a script's id, refer to the script link: https://dashboard.script-ware.com/catalogue?scriptID=18, in this case 18 is the script number and can be executed by being passed to import.
bool isourclosure(function f)
bool isexecutorclosure(function f)
Returns true of the function passed is a Script-Ware function, returns false otherwise.
<void> executescript(union<string, number> script, <table?> settings)
Executes "script" as if it was being run from the Script-Ware editor. If "script" is a number, it will import the script id from the catalogue.
void outputprint(int type, string message)
Prints "message" to the Script-Ware Output Page using type.
They will appear as "SCRIPT INFO", "SCRIPT WARN" & "SCRIPT ERROR" in the output page respectively.
These functions allow you to read into environments (and instances).
✅ All Script-Ware platforms support these functions!
union loadstring(string chunk, [string chunk_name])
Attempts to load "chunk", returns a valid function is successful. You can set an optional chunk name via "chunk_name".
number getidentity()
number getthreadidentity()
number getthreadcontext()
Returns the identity for the current thread.
Instance getscriptfromthread(thread t)
Returns the script from thread even if the script does "script = nil"
Get Hidden UI
instance gethui()
Returns a holder made for GUI's to be parented in. It hides children from FindFirstChild attacks and does not fire game.DescendantAdded.
table getgenv()
Returns the Script-Ware environment
table getrenv()
Returns the global game environment
table getreg()
Returns the Lua registry.
table getgc(bool include_tables)
Returns the garbage collector table.
table getsenv(LocalScript/ModuleScript script)
Returns the environment of "script".
table<instance> getinstances()
Returns every single instance in the client.
table<instance> getnilinstances()
Returns every instance in the game that is parented to nil.
table<Connections> getconnections(Connections signal)
Get a list of active connections to the provided signal.
Get Hidden Property
variant gethiddenproperty(instance object, string property)
Returns the hidden "property" from "object". Not all internal types are pushable to Lua, we currently support "BinaryString, SharedString and SystemAddress". If SystemAddress is pushed it returns a table like so: "{Address = x, Port = x}"
Set Hidden Property
void sethiddenproperty(Instance object, string property, variant value)
Set's "object"'s hidden "property" value to "value".
Set Hidden
bool sethidden(Instance, string property, bool value)
Sets whether a property is hidden or not using value. Returns old visibility status.
Is Hidden Property
bool ishiddenproperty(Instance object, string property)
Returns true if property is hidden, false is the property isn't hidden, nil if the property doesn't exist.
table getproperties(Instance object)
Returns a array of property names on said object.
Get Hidden Properties
table gethiddenproperties(Instance object)
Returns a array of hidden property names on said object.
string getscriptbytecode(Instance script)
string dumpstring(Instance script)
Gets the Luau bytecode of a script, useless if you don't know what you're doing. Using the Bitstream library is recommended if you plan to do any parsing, it has built in LEB128.
string getscripthash(Instance script)
Gets the hashed bytecode of the specified "script" and returns it. This is useful for comparing game scripts to see if the developers have changed their scripts.
table getloadedmodules(bool filter)
Returns all ModuleScripts loaded in the game. If "filter" is false it will not filter our core items.
Functions related to metatable manipulation. ✅ All Script-Ware platforms support these functions!
table getrawmetatable(union obj)
Returns the metatable value of "obj", bypassing the __metatable field.
void setrawmetatable(union obj, table newmetatable)
Set's the metatable of "obj" to "newmetatable".
void setreadonly(table tab, bool state)
Set's the read-only state of "tab" to "state" - setting whether it can be written to or not.
bool isreadonly(table tab)
Returns the read-only state of "tab".
bool iswritable(table tab)
Returns if "tab" is writable (not read-only).
void makewritable(table tab)
Makes "tab" writable (sets the read-only status to false).
void makereadonly(table tab)
Makes tab read-only (sets the read-only status to true).
Referring to closure manipulation.
✅ All Script-Ware platforms support these functions!
function getscriptclosure(Instance script)
Returns a new copy of the script's closure.
function newcclosure(function f)
Returns a wrapped CClosure of "f".
function hookfunction(function old, function hook)
Hooks the function specified in "old" - replacing it with "hook".
bool checkcaller()
Returns if the current function is being called by Script-Ware or not.
instance<union> getcallingscript()
Returns the script that called the function. Even gets scripts that call "script = nil"
NOTE: Script-Ware created functions will always return nil.
bool islclosure(function f)
Returns whether "f" is a Lua closure.
bool iscclosure(function f)
Returns whether "f" is a CClosure
bool checkclosure(function f)
Returns whether "f" is a closure created by Script-Ware.
Instance Saving Functions
void setrbxclipboard(string data)
Sets the studio clipboard to data allowing the data to be pasted inside of studio.
The data must be in a valid xml/binary model format or it will not work.
Example:
saveinstance(object,"clipboard.rbxm")
setrbxclipboard(readfile("clipboard.rbxm"))
variant getpcd(instance obj)
Returns content of the PhysicalConfigData of "obj".
NOTE: It is not recommended you use this, just do gethiddenproperty(obj, "PhysicalConfigData") instead. This function exists for compatibility and ease of use, however, it is deprecated.
void saveinstance(tuple<Instance,Array> obj, string filename, table options)
Allows you to serialize instances into model/place files allowing you to inspect them in studio.
If "obj" is an instance it serializes into a model file, if it is "game" it will be written to a place file. (as in .rblx if its in binary mode)
"filename" is the name of the file it will write to.
If "obj" is a table then it will serialize the instances of the numerical indices of the table into a model file.
Functions that refer to network ownership manipulation. ✅ All Script-Ware platforms support these functions!
bool isnetworkowner(instance part)
Returns true if you are the current network owner of "part".
void setsimulationradius(int simulationradius)
Sets your physical simulation radius to "simulationradius"
int getsimulationradius()
Returns the current set physical simulation radius of the LocalPlayer.
These functions are related to inputting mouse activity and key activity into the window process through Lua. ✅ All Script-Ware platforms support these functions!
Is Active
function isrbxactive(): boolean
Returns true is the main window is in focus.
This must return true for any of the other functions to work.
This doesn't mean you must call it, but the main window must be in focus for the other functions to work.
function keypress(key: number)
Performs a keypress with the provided key - you can read on keys available here.
function keyrelease(key: number)
Performs a key release with the provided key.
function mouse1click()
Stimulates a full left mouse click (press and release).
function mouse1press()
Stimulates a left mouse press (button is pressed but not released).
function mouse1release()
Stimulates a left mouse release (button is released after being pressed).
function mouse2click()
Stimulates a full right mouse click (press and release).
function mouse2press()
Stimulates a right mouse press (button is pressed but not released).
function mouse2release()
Stimulates a right mouse release (button is released after being pressed).
function mousescroll(pixels: number)
Scrolls the mouse wheel by "pixels".
function mousemoverel(x: number, y: number)
Moves the mouse relative to the current mouse position by coordinates "x" and "y".
function mousemoveabs(x: number, y: number)
Moves the mouse to the coordinates "x" and "y" from the top left of the main window.
local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera
local m = player:GetMouse()
while wait(1) do
local pos = camera:WorldToScreenPoint(player.Character.Head.Position)
mousemoverel(pos.X - m.X, pos.Y - m.Y) --moves the camera to the position of the LocalPlayers head.
end
These functions allow for the manipulation and analysis of files in respective locations.
Files can only be written to the workspace folder.
Don't be afraid of potentially malicious scripts that could install malware or flood your folders. Only the workspace folder can have files programatically created, deleted and written to.
function readdialog(title: string, filter: string): tuple<bool, string>
Result of dialog call
Opens a read file dialog with title and filter.
See for docs on filter: https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamea
This allows for the user to select a file anywhere on their computer. If the user selects a file true is return with the file data. If the user doesn't select a file then false is return a long with nil.
Note: Both writedialog and readdialog are async. They will not hang the process.
function writedialog(title: string, filter: string, data: string): bool
Makes a write file dialog with title and filter.
When a user selects a file data is written to selected file and true is returned.
Use cases: Saving lists of data(ex. sound logs, chat logs, remote logs, etc.), Saving images directly out of the game.
function readfile(path: string): string
Reads the content of the file located in "path" and returns it. This function provides descriptive errors to notify you if there has been a problem during this process.
function writefile(path: string, content: string, usecontentfolder: bool?): string
Writes a file in "path" with the data specified in "content". If "usecontentfolder" is set to true it will write to the content folder rather than workspace.
function appendfile(path: string, content: string)
Appends "content" to the file located in "path".
function loadfile(path: string): variant
Loads content of file located in "path" as a chunk and returns a valid function if successful.
function dofile(path: string)
Attempts to load and execute the file located in "path".
function listfiles(folder: string): table<string>
Returns a table of all the files located in "folder" (a path to the folder).
function isfolder(path: string): bool
Returns true if "path" leads to a folder.
function isfile(path: string): bool
Returns true if "path" leads to a file.
function makefolder(path: string)
Creates a new folder at "path".
function delfolder(path: string)
Deletes the folder located at "path". Errors if no folder was located.
function delfile(path: string)
Deletes the file located at "path". Errors if no folder was located.
Alas! There is no more need to utilise chunky and often easily (accidentally) deleted folders within workspace to store user data to then utilise it later on. Using Script-Ware's novel profile functions, this makes this far cleaner.
You are able to create multiple profiles under one setting, making it especially nice to organise certain areas of your script.
This should be cross software (provided other products follow this guideline).
Settings are written to the App Data folder, under a new "ExecutorProfiles" folder.
Setting is the parent for the profiles, it is the folder that holds the profile.
Profile is the actual data holder, it is a file (file.profile) of which is under Setting
function writeprofile(setting: string, profile: string, data: string)
Creates a new folder named "setting", and within the setting a new "profile" is made named profile containing "data" as its content. If the setting already exists, the profile is just made within it.
function getprofile(setting: string, profile: string)
function readprofile(setting: string, profile: string)
Reads the "content" of the existing "profile" and returns the content.
function: deletestring(setting: string)
Deletes an entire setting, including profiles within the setting.
function deleteprofile(setting: string, profile: string)
Deletes the specified profile located within the specified setting.
function getsettingprofiles(setting: string): table
Returns all profiles under specified setting in a table.
function issetting(setting: string): bool
Returns whether or not the specified setting exists.
function isprofile(setting: string, profile: string): bool
Returns whether the profile exists within the specified setting
local DefaultSettings = {
MaxHealth = 100,
Health = 50,
}
local function EncodeJson(x)
return game:GetService("HttpService"):JSONEncode(x)
end
local function DecodeJson(x)
return game:GetService("HttpService"):JSONDecode(x)
end
local Settings
if not isprofile("MyEpicSettings", "Profile1") then
writeprofile("MyEpicSettings", "Profile1", EncodeJson(DefaultSettings))
Settings = DefaultSettings
else
Settings = DecodeJson(getprofile("MyEpicSettings", "Profile1"))
end
print("Health", Settings.Health)
print("Max Health", Settings.MaxHealth)
Script-Ware allows for the user to generate a console of which can be printed to, coloured, used to log, error & more. This can be useful for safely debugging a script.
function consolecreate()
function rconsolecreate()
Opens a console without any text inside of it. A default console title will be set.
function consoleprint(message: string, colour: string?)
function rconsoleprint(message: string, colour: string?)
Prints "message" to the console and takes an optional colour argument. If none is given, the default will be white.
NOTES:
"u(colour)" will underline the message.
"b(colour)" will bold the message.
function consolesettitle(title: string)
function rconsolesettitle(title: string)
Set the console's title to "title".
function consoleinput(): string
function rconsoleinput(): string
Allows the user to enter something in the console, and returns what they have entered.
function consoleclear()
function rconsoleclear()
Clears the console out entirely (all prints are gone).
function rconsoledestroy()
function consoledestroy()
Puts the console away, when re-shown - the previous data will remain.
These are functions that do not fit into a specific category.
function getcallbackvalue(inst: Instance, callbackname: string)
Gets the callback function of the given callback on the given instance.
Example:
local RemoteFunc = Instance.new("RemoteFunction")
RemoteFunc.OnInvoke = function() print("hi") end
print(RemoteFunc.OnInvoke) --> fails
print(getcallbackvalue(RemoteFunc, "OnInvoke")) --> function 0xDEADBEEF
function setclipboard(text: string)
function toclipboard(text: string)
Sets the users studio clipboard to "text".
function getcustomasset(filename: string, preventcache: bool?): string
Makes a custom content-id from filename allowing you to load your own content from the workspace folder. if "preventcache" is enabled, it will force the content to not cache allowing it to be reloaded.
-- Custom Asset Image Displayer "Creates a simple GUI which loads images from a URL inputted."
local function loadCustomAsset(url,filename,reusable)
local data = game:HttpGet(url)
writefile(filename,data)
return getcustomasset(filename,reusable)
end
local gui = Instance.new("ScreenGui")
local label = Instance.new("ImageLabel",gui)
label.Size = UDim2.new(0,500,0,500)
local textBox = Instance.new("TextBox",label)
textBox.Size = UDim2.new(1,0,0,25)
textBox.Position = UDim2.new(0,0,1,-25)
textBox.FocusLost:Connect(function(enter)
if enter then
label.Image = loadCustomAsset(textBox.Text,"displayImage.png",true)
textBox.Text = ""
end
end)
gui.Parent = game:GetService("Players").LocalPlayer.PlayerGui
function cloneref(data: userdata): userdata
This takes a userdata and copies it.
function messagebox(text: string, caption: string, flags: int): int
Creates a message box that appears from the game, it returns the user's input. This is ran async.
It returns an integer of which reflects the following events:
function compress_string(text: string, algorithm: string): string
Compresses "text" using the compression "algorithm" specified in the next argument - returns the compressed string.
function decompress_string(text: string, algorithm: string, lz4size: int?): string
Decompresses "text" using the supported compression "algorithm" specified (being the ones above), this also optionally takes the "lz4size". Returns the decompressed string.
function lz4compress(text: string): string
Compresses "text" using only the lz4 compression algorithm - returns the result.
function lz4decompress(text: string, size: int): string
Decompresses "text" using only the lz4 compression algorithm, takes the size as the second parameter - returns the result.
function setfflag(fflag: string, value: string)
Sets FFlag "flag" to "value".
function setidentity(identity: number)
Sets the current thread's identity to "identity".
function queue_on_teleport(script: string)
Executes "script" upon successful teleport.
function setnamecallmethod(method: string)
Sets current namecall method to new namecall "method". Assumes the function is called in a metamethod hook.
function getnamecallmethod(): string
Returns the current namecall method. Assumes the function is called in a metamethod hook.
function setparentinternal(object: Instance, newparent: Instance)
Internally sets the parent property of "object" to "newparent". This does not trigger any events or anything, this includes it not being to any children lists. If you want that behavior use this along with addchildinternal.
function addchildinternal(parent: Instance, child: Instance)
Adds a child to an instance without firing any signals (thwarts event-based detection)
function removechildinternal(parent: Instance, child: Instance)
Remove a child in an instance without firing any signals. NOTE: You must have a reference to the child in the parent in order to delete it.
function fireproximityprompt(proximity: Instance)
Triggers the Proximity Prompt that was passed to "proximity". This is replicated to the server!
function fireclickdetector(detector: Instance, distance: number?, flag: string?)
Fires a Click Detector with the given distance provided, if none is provided, the distanced clicked is defaulted to 0. The different flags to fire the event with are as follows:
If no flag is provided, the function defaults to "MouseClick".
local ClickDetector = game:GetService("Workspace").MyPart.ClickDetector
fireclickdetector(ClickDetector, 50, "RightMouseClick")
-- Fires the Click detector at a 50 stud range and fires the ClickDetector.RightMouseClick:Connect() signal
NOTE: The click detector will not fire if "ClickDetector.MaxActivationDistance" is greater than the distance provided.
function firetouchinterest(o1: Instance, o2: Instance, begin: number?)
If "begin" is 0, it begins the touch; if 1, it ends the touch.
function firesignal(event: RBXScriptSignal)
Fires all signals attached to "event"
function setproximitypromptduration(proximity: obj, duration: number)
Sets the duration of the proximity prompt to duration.
function getproximitypromptduration(proximity: obj): number
Returns the current proximity prompt duration.
function game:GetObjects(url: string): table
A full custom implementation of GetObjects, this returns all objects whereas the common implementation (often just an alias for the LoadLocalAsset function) merely returns the first asset.
This is an extension to miscellaneous functions, functions that also do not fall under certain criteria.
✅ All Script-Ware platforms support these functions!
function http.request(options: table): table
function request(options: table): table
Creates an HTTP request using the conventionally made "options".
Script-Ware comes equipped with many of its own, viable, libraries which contain various functions that fit the library names.
These libraries are:
- Drawing Library
- Cache Library
- Bit Library
- Websocket Library
- Debug Library
- Crypt Library
We will discuss these libraries and what content they contain - alongside providing examples for them.
Invalidate Cache
void cache.invalidate(Instance obj)
Invalidates instance "obj" in the cache.
void cache.replace(Instance oldguy, Instance newhire)
Replaces the "oldguy" in the cache with the "newhire".
bool cache.iscached(Instance obj)
Returns true if instance "obj" is cached.
These functions are here for compatibility, since, these functions are naturally supported. See here.
✅ All Script-Ware platforms support these functions!
int bit.bor(int x, int y)
Returns bitwise or of "x" and "y".
int bit.band(int x, int y)
Returns bitwise and of "x" and "y".
int bit.banot(int x)
Returns bitwise not of "x".
int bit.bxor(int x, int y)
Returns bitwise xor of "x" and "y".
int bit.badd(int x, int y)
Performs binary addition on "x" and "y", returns the answer.
int bit.bdsub(int x, int y)
Performs binary subtraction on "x" and "y", returns the answer.
int bit.bdiv(int x, int y)
Performs binary divison on "x" and "y", returns the answer.
int bit.bmul(int x, int y)
Performs binary multiplication on "x" and "y", returns the answer.
int bit.bswap(int x)
Swaps the bytes of "x" and returns it.
int bit.blshift(int x, int y)
Returns the result of a logical bitwise left-shift of "x" by the bits provided by "y".
int bit.brshift(int x, int y)
Returns the result of a logical bitwise right-shift of "x" by the bits provided by "y".
int bit.btobit(int x, int y)
Returns "x" represented as a binary number.
int bit.btohex(int x, int y)
Returns "x" represented in hexadecimal.
Re-implementation of the Lua debug library, with added additions.
✅ All Script-Ware platforms support these functions!
function debug.getconstants(f: (function | number)): table
Returns the constants in function/level "f".
function debug.getconstant(f: (function | number), idx: number): table
Returns the constant "idx" from function/level "f".
function debug.setconstant(f: (function | number), idx: number, obj: any): table
Sets constant "idx" from function/level "idx" to "val". NOTE: The new object MUST be of the same type as the old.
function debug.getupvalues(f: (function | number)): table
Returns upvalues in function/level "f".
function debug.getupvalue(f: (function | number), idx: number): table
Returns upvalue "idx" in function/level "f".
function debug.setupvalue(f: (function | number), idx:number, obj: any): table
Sets upvalue "idx" in function/level "f" to "val".
function debug.getprotos(f: (function | number)): table
Returns protos in function/level "f".
function debug.getproto(f: (function | number), idx: number, active: boolean?): (function | table)
Returns proto "idx" in function/level "f".
function debug.setproto(variant f, int idx, function replacement)
Sets proto "f" at idx with "function" replacement at level/function "f".
function debug.getstack(level: number, idx: number): any
Returns register "idx" in the stack located at "level".
function debug.setstack(level: number, idx: number, obj: any)
Sets register "idx" in the stack located at "level" to "val".
function debug.getinfo(f: (function | number)): table
Returns information about function/level "f".
✅ All Script-Ware platforms support these functions!
userdata Drawing.new(string class)
userdata createrenderobj(string class)
Creates and returns a new draw object of type "class"
bool isrenderobj(userdata object)
Checks if "object" is a draw object.
Sets a draw object's "property" to given "value" NOTE: "setrenderproperty" is a draw object's "__newindex"
variant getrenderproperty(userdata drawobject, string property)
Returns a draw object's "property" NOTE: "getrenderproperty" is a draw object's "__index"
void cleardrawcache()
Clears all objects from the internal draw object list NOTE: this invalidates any references to pre-existing draw objects
circle = Drawing.new('Circle');
circle.Radius = 50;
circle.Color = Color3.fromRGB(255, 255, 255);
circle.Filled = false;
circle.NumSides = 32; -- Circles aren't drawn perfectly; more "sides" = more lag
circle.Position = Vector2.new(20, 20); -- pixels offset from top right
circle.Transparency = 0.9;
square = Drawing.new('Square');
square.Position = Vector2.new(20, 20);
square.Size = Vector2.new(20, 20); -- pixels offset from .Position
square.Thickness = 2;
square.Color = Color3.fromRGB(255, 255, 255);
square.Filled = true;
square.Transparency = 0.9;
line = Drawing.new('Line');
line.PointA = Vector2.new(20, 20); -- origin
line.PointB = Vector2.new(50, 50); -- destination
line.Color = Color3.new(.33, .66, .99);
line.Thickness = 1;
line.Transparency = 0.9;
text = Drawing.new('Text');
text.Text = '𝘚𝘤𝘳𝘪𝘱𝘵-𝘞𝘢𝘳𝘦';
text.Color = Color3.new(1, 1, 1); -- white text
text.OutlineColor = Color3.new(0, 0, 0); -- black outline
text.Center = true; -- center text
text.Outline = true;
text.Position = Vector2.new(100, 100);
text.Size = 20; -- font size
text.Font = Drawing.Fonts.Monospace; -- 'UI', 'System', 'Plex', 'Monospace'
text.Transparency = 0.9;
image = Drawing.new('Image'); -- todo: look into how images are actually rendered
image.Color = Color3.new(0, 0, 0);
image.Rounding = 3;
image.Size = Vector2.new(256, 256);
image.Position = Vector2.new(100, 100);
image.Transparency = 0.9;
quad = Drawing.new('Quad');
quad.Color = Color3.new(.1, .2, .3);
quad.Filled = false;
quad.Thickness = 2;
quad.Point1 = Vector2.new(100, 0);
quad.Point2 = Vector2.new(50, 50);
quad.Point3 = Vector2.new(0, 100);
quad.Point4 = Vector2.new(100, 100);
quad.Transparency = 0.69;
triangle = Drawing.new('Triangle');
triangle.PointA = Vector2.new(50, 0);
triangle.PointB = Vector2.new(0, 50);
triangle.PointC = Vector2.new(100, 50);
triangle.Thickness = 3;
triangle.Color = Color3.new(1, 0, 0);
triangle.Filled = true;
triangle.Transparency = 1.0;
-- don't forget to clean up after yourself!
for obj in next, {circle, square, line, text, image, quad, triangle} do
obj:Destroy(); -- or :Remove()
end
Script-Ware allows for high level encryption and string encoding through it's crypt library, featuring many methods of performing functions.
function crypt.encrypt(data: string, key: string, nonce: string?): string
"data" is encrypted and returned in Base64 encoding via AES-CTR encryption using the "key" and a "nonce" that is otherwise automatically generated. The key and IV are assumed to be encoded in Base64.
function crypt.decrypt(data: string, key: string, nonce: string?): string
"data" is decrypted using the "key" and a "nonce" that is otherwise used by the automatically generated previous nonce. The key and IV are assumed to be encoded in Base64.
function crypt.custom_encrypt(data: string, key: string, nonce: string, ciphermode: string): string
Encrypts "data" using the same arguments as in the normal encrypt function, but allows for you to specify your own cipher mode. The following are the compatible ones:
function crypt.custom_decrypt(data: string, key: string, nonce: string, ciphermode: string): string
Decrypts "data" using the same arguments as in the normal decrypt function, but allows for you to specify your own cipher mode. The same ones as shown above.
function crypt.generatekey(): string
Returns a 256 bit, Base64 encoded, encryption key. You can use this key for the encryption process.
function crypt.generatebytes(bytes: number?): string
Generates randomly generated, base64 encoded, "bytes" in length bytes. If the "bytes" is not passed, it generates 16 bytes. This is typically used for the nonce/IV.
Script-Ware V2 is the first to properly support asymmetric encryption, you can now secure stuff using two keys, a public (encryption) and a private (decryption) via the RSA algorithm.
OAEP padding is used alongside the encryption, meaning that encrypted results will appear completely different every time but can still be decrypted fine.
If the key is invalid, or the data being given for decryption is not in-line with the private key, an error will be thrown.
XML, PEM, DER & even JWK encryption key formats are accepted. Furthermore, encrypted data is returned base64 encoded. Decryption data is taken base64 encoded. Just like AES.
function crypt.asym_encrypt(plain_text: string, public_key: string): string
Returns encrypted "plain_text" using the public RSA key "public_key".
function crypt.asym_decrypt(cipher_text: string, private_key: string): string
Returns decrypted "cipher_text" using the private RSA key "private_key"
function crypt.asym_generatekey(): string
Returns a keypair in XML format which looks like this:
PUBLICKEY: <RSAPublicKey><Modulus>yLWaMsvl+0F4W4wzGqacD5WFANtH88Xs5M0bPizkjPdc4PopRtnMfTKX55JDB/EkXWxVZU3Xvcx3a41EQtJ4Bb8IT2mZALKAHJzFEJxRyL6C5IIWK1pia40aO6oLcZCpn8uS08sR/2rWzVF8u2UED08LvwSMsRaUFPwjrcj0aDTpdv8dI7YQOu2Qwqgjov0w3h5EPeoQIMXGlP8LbBJ6WcoI8s3vq4Nn2XvYRgcahQ6Pf24V35gi6joPKk09UERJDwAOsMLMFb1LClI0MG7N9O/k0INu7lNYH40YDlaztMvD/X6A7jRkinjwOoFeQeT20+zcwdGFyzfxXhHMlxa0qQ==</Modulus><Exponent>AQAB</Exponent></RSAPublicKey>
PRIVATEKEY: <RSAKeyValue>
<Modulus>yLWaMsvl+0F4W4wzGqacD5WFANtH88Xs5M0bPizkjPdc4PopRtnMfTKX55JDB/EkXWxVZU3Xvcx3a41EQtJ4Bb8IT2mZALKAHJzFEJxRyL6C5IIWK1pia40aO6oLcZCpn8uS08sR/2rWzVF8u2UED08LvwSMsRaUFPwjrcj0aDTpdv8dI7YQOu2Qwqgjov0w3h5EPeoQIMXGlP8LbBJ6WcoI8s3vq4Nn2XvYRgcahQ6Pf24V35gi6joPKk09UERJDwAOsMLMFb1LClI0MG7N9O/k0INu7lNYH40YDlaztMvD/X6A7jRkinjwOoFeQeT20+zcwdGFyzfxXhHMlxa0qQ==</Modulus>
<Exponent>AQAB</Exponent>
<D>YYIQksMKTQGmlltLbJQq10tdnWfq0dyDVTjsFk27D10Ra189dGVVejF9KQyeshoN/7Ek351sxZGzJrYvxoL3ulrvj50Orw8JPF1RQ9udw/gCIT+tw4waqoOXjgrzmk2cirxc7bYHXV4rUv5WqbfOvB076KYuL853TKaeMsyhKIpHvuGBiM5fYxVR1tUMGFqaZFktXvYUSBDUKSxIkWl5sEK+OYX7KuvO813GZMFuraWR29fevSqyAvnk1sgMTZxPui+oPqnnJvMiQErlLTK3MWRviYKHtkM4UHVBQ/wGDtlR0c4Y7Dq7CouPnZiqM7nrmbV9+wAcAUHq1OCylRQIAQ==</D>
<P>2ZBiSsD45Uermt6nH+oEbQPAyH1JY0Rskd6idFt76wpCPoldW5+BBX4gFd36e3LBi47WM5g6dLyMvflW1AHqoOQjJ2vs+vqs6KaV7qpN7Ebvc3e6suz9I9vh4SRx3kN2q5jl/wTLQyWfMp0Bbej2meE2xx5ndn484X9gjTBQ2ak=</P>
<Q>7CryRb1U23+z3sf50aS0RT2IE3FJ8u6fn1Qo+POkY3nqjkJUT4C5h9sr8+pQaC29rJXDMXHdQb9c3akcqLtdpGqlDQpU6et0RO8ZdOfUMEY142KPjpQNKwdHydnb4Fi5v5O80gIU4WG1vgYm7LOw1bwZihWSai3TF5kxyWS94wE=</Q>
<DP>Orju3tBxHamXluiL4WBPq4D80uCGI2X/i+u0m4Vp0Ann0WtQKH/H2kytqllM128k8F8wT6LB/Go77rbTCcwuoRiSqHxH7YIlk3ILO+z5tVF8JgVl/g9bUAd0U0l79ortB+nwAkIv/sKz0nxwO1NK5EPWfi4lrOTr182CqPkmiWk=</DP>
<DQ>Ks9+K3Yus7tqd6/4cTBSHVsSF7Qpm4f2F4PCCZ7g5cdr5W3QqrjC6QWp6gVHnhrsjyR2xbfGS+zMgMHvGAce6ncKhFqjGeQ8p2JQUwjTRgz0J8vuCgJ8gJhJQyXi3cA3q6q0R71o25JCx7eFxtKr85KtboKKkATOhzCV/dPtOAE=</DQ>
<InverseQ>tTHkUX+TPJ98y+RwsMWTKuXU0cswIyvBlbrvGi9ENt8pS18TspAfYWDt48Ajw4fJirnK3MqKuQumxiTVsdJK2A70H/rSpyax7VwWiKxO+8Zns2R5TpI1E9aEkcBnHBEQu9cbPXU09lRFZs7f4AReU3jkBZGXJueGMmBKtQH4Ku4=</InverseQ>
</RSAKeyValue>
Don't use this function for your encryption/decryption code!
You can then use this to perform encryption. These keys are not special, they are generic RSA keys in XML encoding - feel free to use your own generated keys in any supported encoding.
This function is strictly only to generate keys, don't pass this function to the encrypt or decrypt function. It won't work!
function crypt.base64encode(data: any): string
"data" is base64 encoded and returned.
function crypt.base64decode(data: any): string
"data" is base64 decoded and returned.
function crypt.hash(data: string, algorithm: string?): string
"data" is hashed via the algorithm. Defaulted to SHA256.
Wow, what a plethora of cool algorithms!
local generated_key = crypt.generatekey()
local generated_nonce = crypt.generatebytes()
local encrypted = crypt.encrypt("hello, world!", generated_key, generated_nonce)
print(encrypted) --encrypted "hello, world!"
local decrypted = crypt.decrypt(encrypted, generated_key, generated_nonce)
print(decrypted) --decrypted encrypted "hello, world!"
local RSA_Public = "<RSAPublicKey><Modulus>yLWaMsvl+0F4W4wzGqacD5WFANtH88Xs5M0bPizkjPdc4PopRtnMfTKX55JDB/EkXWxVZU3Xvcx3a41EQtJ4Bb8IT2mZALKAHJzFEJxRyL6C5IIWK1pia40aO6oLcZCpn8uS08sR/2rWzVF8u2UED08LvwSMsRaUFPwjrcj0aDTpdv8dI7YQOu2Qwqgjov0w3h5EPeoQIMXGlP8LbBJ6WcoI8s3vq4Nn2XvYRgcahQ6Pf24V35gi6joPKk09UERJDwAOsMLMFb1LClI0MG7N9O/k0INu7lNYH40YDlaztMvD/X6A7jRkinjwOoFeQeT20+zcwdGFyzfxXhHMlxa0qQ==</Modulus><Exponent>AQAB</Exponent></RSAPublicKey>"
local RSA_Private = "<RSAKeyValue><Modulus>yLWaMsvl+0F4W4wzGqacD5WFANtH88Xs5M0bPizkjPdc4PopRtnMfTKX55JDB/EkXWxVZU3Xvcx3a41EQtJ4Bb8IT2mZALKAHJzFEJxRyL6C5IIWK1pia40aO6oLcZCpn8uS08sR/2rWzVF8u2UED08LvwSMsRaUFPwjrcj0aDTpdv8dI7YQOu2Qwqgjov0w3h5EPeoQIMXGlP8LbBJ6WcoI8s3vq4Nn2XvYRgcahQ6Pf24V35gi6joPKk09UERJDwAOsMLMFb1LClI0MG7N9O/k0INu7lNYH40YDlaztMvD/X6A7jRkinjwOoFeQeT20+zcwdGFyzfxXhHMlxa0qQ==</Modulus><Exponent>AQAB</Exponent><D>YYIQksMKTQGmlltLbJQq10tdnWfq0dyDVTjsFk27D10Ra189dGVVejF9KQyeshoN/7Ek351sxZGzJrYvxoL3ulrvj50Orw8JPF1RQ9udw/gCIT+tw4waqoOXjgrzmk2cirxc7bYHXV4rUv5WqbfOvB076KYuL853TKaeMsyhKIpHvuGBiM5fYxVR1tUMGFqaZFktXvYUSBDUKSxIkWl5sEK+OYX7KuvO813GZMFuraWR29fevSqyAvnk1sgMTZxPui+oPqnnJvMiQErlLTK3MWRviYKHtkM4UHVBQ/wGDtlR0c4Y7Dq7CouPnZiqM7nrmbV9+wAcAUHq1OCylRQIAQ==</D><P>2ZBiSsD45Uermt6nH+oEbQPAyH1JY0Rskd6idFt76wpCPoldW5+BBX4gFd36e3LBi47WM5g6dLyMvflW1AHqoOQjJ2vs+vqs6KaV7qpN7Ebvc3e6suz9I9vh4SRx3kN2q5jl/wTLQyWfMp0Bbej2meE2xx5ndn484X9gjTBQ2ak=</P><Q>7CryRb1U23+z3sf50aS0RT2IE3FJ8u6fn1Qo+POkY3nqjkJUT4C5h9sr8+pQaC29rJXDMXHdQb9c3akcqLtdpGqlDQpU6et0RO8ZdOfUMEY142KPjpQNKwdHydnb4Fi5v5O80gIU4WG1vgYm7LOw1bwZihWSai3TF5kxyWS94wE=</Q><DP>Orju3tBxHamXluiL4WBPq4D80uCGI2X/i+u0m4Vp0Ann0WtQKH/H2kytqllM128k8F8wT6LB/Go77rbTCcwuoRiSqHxH7YIlk3ILO+z5tVF8JgVl/g9bUAd0U0l79ortB+nwAkIv/sKz0nxwO1NK5EPWfi4lrOTr182CqPkmiWk=</DP><DQ>Ks9+K3Yus7tqd6/4cTBSHVsSF7Qpm4f2F4PCCZ7g5cdr5W3QqrjC6QWp6gVHnhrsjyR2xbfGS+zMgMHvGAce6ncKhFqjGeQ8p2JQUwjTRgz0J8vuCgJ8gJhJQyXi3cA3q6q0R71o25JCx7eFxtKr85KtboKKkATOhzCV/dPtOAE=</DQ><InverseQ>tTHkUX+TPJ98y+RwsMWTKuXU0cswIyvBlbrvGi9ENt8pS18TspAfYWDt48Ajw4fJirnK3MqKuQumxiTVsdJK2A70H/rSpyax7VwWiKxO+8Zns2R5TpI1E9aEkcBnHBEQu9cbPXU09lRFZs7f4AReU3jkBZGXJueGMmBKtQH4Ku4=</InverseQ></RSAKeyValue>"
local encrypted = asym_encrypt("hello, world!", RSA_Public)
local decrypted(asym_decrypt(encrypted, RSA_Private))
-- two keys!
function WebSocket.connect(url: string): object
Attempts to connect to the "url" provided.
function WebSocket:Send(msg: string)
Sends the provided message "msg" to the server.
function WebSocket:Close()
Closes the opened connection.
signal WebSocket.OnMessage
This event is fired when a message is sent from the server.
signal WebSocket.OnClose
This event is fired when the WebSocket is closed, by either the "WebSocket:Close" method or by the server.
local socket = WebSocket.connect("ws://localhost:8126/foo")
socket.OnClose:Connect(function() print'closed' end)
socket.OnMessage:Connect(function(msg)
print(msg)
end)
socket:Send("Hello")
The Script-Ware Disassember is a new advancement in script analysis. This utility is useful for examining scripts that may have anti-decompile methods, or for learning purposes.
function disassemble(bytecode: string): string
The disassembler takes bytecode as a string returned from getscriptbytecode or your own.
local bytecode = getscriptbytecode(script_instance)
writefile("myBytecode.txt", disassemble(bytecode))
How to read the disassembler's output
Lua
for i = 1, 10 do
print(string.format("%i, Hi!", i))
end
print("High Five!")
Luau
[000] VARARGINIT 0 0 0
[001] LOADNUMBER 2 1
[002] LOADNUMBER 0 10
[003] LOADNUMBER 1 1
[004] FORPREP 0 9 ; jump to 13
[005] LOADMULTIK 3 1 ; multi-index(1): print
[007] LOADMULTIK 4 4 ; multi-index(2): string.format
[009] LOADK 5 5 ; K(Bx): %i, Hi!
[010] MOVE 6 2
[011] CALL 4 3 0
[012] CALL 3 0 1
[013] FORLOOP 0 -9 ; jump to 4
[014] LOADMULTIK 0 1 ; multi-index(1): print
[016] LOADK 1 6 ; K(Bx): High Five!
[017] CALL 0 2 1
[018] RETURN 0 1 0
In Lua and Luau, there is a fixed size stack that has 2 to 250 values. You can see that here in the maxstacksize field:
In this case, 7 values on the stack will ever be used at the same time.
As far as instructions are concerned, Lua stack is not a traditional push/pop style stack; instead, the number of values needed, and where every one of them are used is predetermined at compile time, and used as necessary by instructions.
Here's an example of how the stack is used to store and retrieve values:
The first instruction, LOADMULTIK, loads the function print into register 0, the second instruction loads the constant "High Five!" which is in constant index 6 into register 1, and then comes the CALL instruction.
The CALL instruction's arguments are 0, 2 and 1. What do these mean?
Like with many instructions, the first argument (this is called argument A) are used as a base register reference to then operate with offsets. In this case, the CALL instruction's base is 0, which is where the stored "print" function is (you can always expect the first argument to have the function being called.) But where does the 2nd and 3rd argument (called argument B and C, respectively) come from? Well that's easy: take argument B and subtract it by 1 (that's the number of arguments passed to the function) and take argument C and subtract it by 1 (that's the number of values returned.) You can indeed see that B - 1 == 1, which we only pass 1 argument "High Five!" and expect no returns (0 values returned.)
As one more explanation, take a CALL instruction with the base of 6, and it is calling with 3 arguments and 2 returns. You can expect the format to be "CALL 6 4 3", with the arguments being in 7, 8 and 9 (as an offset from 6.) A visualization would be "local a, b = func(c, d, e)".
For loops work like CALL instructions in how they work with a base location on the stack. You can see this in action in this example:
You can see that FORPREP's base is 0, where the values of the for loop are stored for usage. Base + 0 is the limit, base + 1 is the iteration, and base + 2 is the starting number.
But, you're probably wondering what the 2nd argument is. That is called the jump offset.
A jump offset is a number that is added with the current program counter (called PC) to specify a relative location/destination.
You can see off to the side the locations of instructions:
These are important for the VM to keep track of so instructions know their way around with relative locations. You can see the disassembler add the offset automatically, showing you where the instructions go "FORPREP 0 9 ; jump to 13" such the current PC (which is 4) is added to the offset (which is 9) to be 13. At position 13, you can see the FORLOOP instruction, which checks if the condition of the for loop has been met, else jump back to the beginning of the loop and run another iteration.
Now that you know the basics of how to read the disassembly, let's dive into more advanced topics.
Luau has a unique feature that we call multi-index table retreival (or compound globals.) This allows much faster grabbing of values nested in multiple tables compared to vanilla Lua.
Source: "print(a.b.c)"
Luau
[000] VARARGINIT 0 0 0
[001] LOADMULTIK 0 1 ; multi-index(1): print
[003] LOADMULTIK 1 5 ; multi-index(3): a.b.c
[005] CALL 0 2 1
[006] RETURN 0 1 0
Text
[1] getglobal 0 0 ; print
[2] getglobal 1 1 ; a
[3] gettable 1 1 258 ; "b"
[4] gettable 1 1 259 ; "c"
[5] call 0 2 1
[6] return 0 1
As you can see in the comparison, Luau combines one GETGLOBAL and up to two GETTABLE operations after that, into a single instruction. Which means Luau will not be able to combine past 2 levels deep into a table efficiently, thus concluding a.b.c.d.e.f will not generate 2 LOADMULTIK's; instead, it generates one LOADMULTIK and 3 (three) GETTABLEK instructions.
In Luau, fastcalls are a great optimization. ROBLOX keeps a hardcoded list of function pointers that are indexed directly by argument A, in which these functions are specially chosen and generated to work in this fashion (the fastcall version of math.sqrt is NOT the same as the normal function.) In order for fastcalls to work, the normal version of the call must exist in the code. The reason for this is because of something called dynamic deoptimization.
Fastcalls alone are a powerful tool, indeed; however, they are not compliant with the Lua 5.1 backwards compatibility promise. Since special functions exist simply for the sake of being able to be fastcalled, using functions like getfenv or setfenv on or in the environment causes the environment to dynamically deoptimized such that fastcalls will no longer work and run the slower, normal code instead.
Here's an example of this in action:
Luau
[000] VARARGINIT 0 0 0
[001] LOADMULTIK 0 1 ; multi-index(1): print
[003] LOADNUMBER 3 90
[004] MONOFCALL 22 3 2 ; jump to 6 ; fastcalling 'math.rad'
[005] LOADMULTIK 2 4 ; multi-index(2): math.rad
[007] CALL 2 2 0
[008] MONOFCALL 24 0 2 ; jump to 10 ; fastcalling 'math.sin'
[009] LOADMULTIK 1 6 ; multi-index(2): math.sin
[011] CALL 1 0 0
[012] CALL 0 0 1
[013] RETURN 0 1 0
Text
[01] getglobal 0 0 ; print
[02] getglobal 1 1 ; math
[03] gettable 1 1 258 ; "sin"
[04] getglobal 2 1 ; math
[05] gettable 2 2 259 ; "rad"
[06] loadk 3 4 ; 90
[07] call 2 2 0
[08] call 1 0 0
[09] call 0 0 1
[10] return 0 1
You can see the special MONOFCALL instructions jumping with argument C (being 2, for both.) This is a conditional jump that will happen only if the environment is optimized. If the jump does not happen, you can see that it will run the code necessary for a non-optimized call. Fastcalling instructions always point to and/or jump to their respective CALL instruction, because these instructions require values in the normal CALL. This means that fastcalls will not work if the unoptimized version of the code does not exist.
Script-Ware comes with the premium capability of accessing the content of in-game Local and Module scripts. It does this through decompiling the scripts data and providing you with a readable output.
You can optionally specify to decompile all available scripts when you save a game in its entirety through using SaveInstance.
Script-Ware's current decompiler is in Beta. There are more improvements we have planned to make, stay tuned!
string decompile(LocalScript/ModuleScript script)
Returns the decompiled "script".
If decompilation fails, it will return an error message
string disassemble(string bytecode)
This provides you diassembled bytecode which you can utilise to very nicely analyse script content. This bypasses every single anti-decompile procedure that exists.
This is an entirely new dynamic that scripters will have to get used to, but it is a powerful tool for script developers, game developers, and more.
In order to pass "bytecode", use the getscriptbytecode function of which returns the scripts Luau bytecode. This is shown in the below example.
writefile("My Script.txt", disassemble(getscriptbytecode(game.Lighting.EpicScript)))
This creates a file in the workspace folder with the diassembled content you can now read through.
Although we try our best to make Script-Ware the most versatile and stable script execution product on the market, it is inevitable that we will encounter problems, bugs, glitches, and what have you. If you encounter one, we are more than happy to fix it.
This goes for our scripting engine, our scripting environment, our interface, and more.
Script-Ware is the top product for resolving bugs and problems! We have an amazing track record of resolving something 48 hours after it has been reported, with some exceptions.
When Script-Ware crashes it will pop up with a notepad file that has some information on who to send the file to as well as what looks like complete garbage data (which is the crash data that we will use to diagnose this crash).
You should then head to https://dashboard.script-ware.com/report-crash to report this crash properly, the rest of the details are available there. You'll then receive an email letting you know that your problem is being looked into.
You leaving this file as-is and continuing will just mean your crash will never be resolved! Please make sure you report crashes you get so we can get crashes fixed.
Script-Ware has a new bug report form in the support section of the site! You can easily discuss your bug and give us screenshots and information so that we can resolve the problem for you.
This section is accessible here: https://dashboard.script-ware.com/report-bug
Please ensure you provide us with as much information as possible, including how to reproduce your bug so that we can fix it for you.
Script-Ware will absolutely accept any serious vulnerabilities that are discovered within its software. This goes for all of our platforms.
Depending on the severity of the vulnerability, you are also entitled to a cash reward for reporting the vulnerability. An example of a vulnerability we would pay cash for is a detection method on injection, a way to bypass or mess with our authentication, a way to grab our environment, and similar vulnerabilities.
Vulnerabilities like these are best privately reported to an official developer such as AzuLX or Modulus through Discord or other (private/direct) means. When reported and resolved you will be given a cash reward which can range upwards of hundreds of dollars.
Alternatively, you may send an email to this email address: [email protected] - explaining how to get in contact with you for a one-to-one discussion regarding your found vulnerability. Thank you.
Severe vulnerabilities like this can potentially harm our large userbase (as well as other, similar, products). You helping us means we can protect our userbase as well as provide you with a reward for helping us out.