Tabletop Simulator

Tabletop Simulator

Not enough ratings
TTS Scripting Odds & Ends
By Buckwheat
Lesser-known tips, tricks, bugs, undocumented features, common pitfalls, API errors, and other things that might trip up an (in)experienced scripter.
   
Award
Favorite
Favorited
Unfavorite
Introduction
This is a collection of lesser-known facts about TTS scripting in no particular order.

Please suggest additions and corrections in the comments, including the current TTS version.

XML Lag (v12.2.3)
Redrawing large numbers of XML elements simultaneously is costly in performance, and will lag multiplayer games if the host has less-than-stellar Internet connection.
Obtain XML Input Field Value (v12.2.3)
There is no way to obtain the text typed into XML input fields other than the automatic arguments passed to their onValueChanged and onEndEdit functions. To make such text available outside these functions, they must be stored in variables.
onPlayerDisconnect Timing (v12.2.3)
onPlayerDisconnect() fires at least one frame before the disconnecting player ceases being referenced. In other words, calling Player.getPlayers() on the same frame as onPlayerDisconnect() returns a table including the disconnecting player.
Multiplayer Race Conditions (v12.2.3)
Even with a flag variable intended to prevent duplicate clicks from activating functions twice, it is possible for two inputs to arrive simultaneously and trigger the function twice before the flag is toggled. One workaround is a pub/sub queue that strictly processes one input at a time, optionally discarding duplicates.
Simultaneous Transform Function Order (v12.2.3)
Transform functions called on the same object on the same frame happen in a random order. Furthermore, in multiplayer, "same frame" means the client's frame, not the host's.
call() Argument (v12.2.3)
call() can only pass one argument to the called function, and this argument must be a table (or nil).
API Error: onObjectDrop() (2020-03-26)
Two arguments are automatically passed to onObjectDrop(): player color and object reference. The API lists these in reverse order.
Component Scripting Desync (v12.2.3)
Clients who have not fully loaded in an object when any Component set() functions are called on it will not necessarily see the same change to the object the host sees, because they are viewing a different object hierarchy at the time. This can cause scripting errors visible only to the client, not the host.
getVar() and getTable() Scope (v12.2.3)
getVar() and getTable() can only get global variables, that is, values in _G, of the object. "local" to the Global scope is not sufficient.
Undocumented Classes and Functions (2020-03-26)
Vector, Color, logString: https://steamhost.cn/steamcommunity_com/games/TabletopSimulator/announcements/detail/3535809451399020344

filterObjectEnterContainer, onPlayerChangeTeam, onObjectHover, onObjectNumberTyped, onObjectStateChange, onObjectCollisionEnter, onObjectCollisionExit, player.setUITheme, object.addContextMenuItem, object.clearContextMenu, addContextMenuItem, clearContextMenu, addHotkey, showHotkeyConfig, clearHotkeys, container.remainder, object.getGMNotes, object.registerCollisions: https://steamhost.cn/steamcommunity_com/games/TabletopSimulator/announcements/detail/1701727756584296505

page_offset, getPage, setPage, setHighlight, clearHighlight: No official documentation[discordapp.com]
Atom Autocomplete Misspells Vector (2020-03-26)
Vector is the correct spelling of the Vector class. Atom autocompletes it to vector, which is incorrect.
Object Reference Validity (v12.2.3)
The correct way to check if an object reference's object still exists is to compare it to nil. "not obj" does not work, and returns true even after the object ceases to exist.
Rotation Snapping Upon Object Pickup (v12.2.3)
Objects will snap to the nearest rotation increment according to your rotation increment setting when picked up. The sole exception is Dice, which keep their rotation when picked up.
Objects As Table Keys (v12.2.3)
It is often more efficient to use object references as table keys, rather than values, so they can be checked for existence without traversing the whole table.
Sparse Array Serialization (v12.2.3)
The built-in JSON.encode() function will create 1999 "null"s in the serialized JSON, for example, if you serialize a table with a single key-value pair with the key being 2000. Avoid this inefficiency by using a different serializing algorithm or by storing your information in a different way.
Global XML UI Scaling (v12.2.3)
The size of Global UI elements depends only on the height of the game window, not its width.
Additional XML Documentation (2020-03-26)
Additional attributes for XML can be found here[www.digital-legacy.co.za].
script_state Eligibility (v12.2.3)
An object needs a script to persist a script_state. -- is a script.
Dealing the Last Card of a Deck (v12.2.3)
After removing the second last card, a Deck is destroyed and its last Card is spawned in its place. At this point, trying to deal() or takeObject() from the deck's object reference will error. Possible workarounds are using deck.remainder, dealing the last two cards on the same frame, and/or detecting the single leftover Card some other way.
Bug: All Assetbundles Are Chips (v12.2.3)
All Assetbundles will turn into a chip upon being dropped onto any Chip stack. The exception is Assetbundles imported as Infinite bags, which become a large number of chips. The workaround is to make sure the color tint of the Assetbundle differs from that of the chip, even if by a single hex digit.
Assetbundle Naming Collisions (v12.2.3)
Only one Assetbundle with any given name should exist in a scene. If two different Assetbundles are imported, both of which share an Assetbundle name, they might fail to import correctly. For this reason, it is prudent to include the name of your project when naming your Assetbundles for export, e.g. chess_mats instead of mats.
Uneditable Default Poker Table Snap Points (v12.2.3)
The snap points attached to the default Poker table are inaccessible from scripting and can only be changed manually using the Snaps tool. Any changes made will revert the next time the save is loaded. These snap points are not equally spaced apart.
Bug: Hand Zone Transforms Change Upon Loading (v12.2.3)
Hand zones can move up and down and/or change scale slightly every time a scene is saved and loaded. This can cause hand zones to no longer hide cards properly, and cards to fly out from hands upon loading. The re-scaling problem seems more severe on hand zones rotated such that they do not align with the cardinal directions. The best workaround is to setHandTransforms() in onLoad(). Manual correction of hand zones once every few saves may also be sufficient.
Object Reference to Default Tables (v12.2.3)
It is possible to obtain an object reference to Tables from the Objects > Tables menu, by finding it with a scripting zone or Physics.cast. Tables have tag Surface. Resizing the default table, however, will cause player avatars to no longer be correctly placed above their hand zones.
Multiplayer Object Loading Desync (v12.2.3)
If the frame an object appears, in addition to every consecutive subsequent frame in which the object is in motion, are all dropped by a client, this client will not know of the object's existence. Causing any Transform variable of the object to change, i.e. moving, rotating, or scaling it, will let the client know of the object, as long as they do not drop all the frames in which the object transforms too.
Box Select Locked Objects (v12.2.3)
Hold Alt to box-select multiple locked objects. This can save time compared to selecting each one individually.
Turns Scripting Unreliability (v12.2.3)
Turns scripting is unreliable to the point that it is impractical to describe exactly what things are wrong with it. Modders typically implement a custom turns system independent from the built-in one. (credit to Bone White)
Hotseat Mode Unsupported (v12.2.3)
Hotseat mode is not suitable for complex scripting, and is not usable for unit testing. (credit to Bone White)
Bug: loading_custom Unreliable (v12.2.3)
loading_custom becomes false before a newly-spawned custom asset finishes loading. Source[github.com]. Source[github.com]. (credit to benjamin_dobell)
Bug: Lua UI Label Alpha Is Multiplied by Background Alpha (v12.2.3)
A Lua button or Input with, for example, 0.5 background alpha and 1 text color alpha will have text with 0.5 alpha, rather than fully opaque text.
Lua Button Draw Order (v12.2.3)
When Lua buttons overlap, they are displayed with the newest-created one in front, regardless of physical position in 3D space.
getSnapPoints() and setSnapPoints() Scale (v12.4.3)
Updated (12.4.3): getSnapPoints() and setSnapPoints() now both in local scale. (thanks Bone White)

getSnapPoints() returns the snap points' displacement from the object's origin in world units, while setSnapPoints() expects them in object units. This means obj.setSnapPoints(obj.getSnapPoints()) only maintains the placement of snap points on obj if obj is scaled to {1, 1, 1}.
Empty Table Serialization (v12.4.3)
JSON.encode({}) returns an empty string. JSON.decode("") returns nil. This can be problematic if you assume your variable is always a table.