Help
Engine Changelog
What changed in each version of Engine, newest first, and what to change in your game when a version asks for it.
How version numbers work is explained in Updates and Versions. With automatic updates, every new server runs a version as soon as it is published; with manual updates, your game moves to it when you update from the plugin. Changes to the plugin and the shell it installs are in the Plugin Changelog.
1.2.0
- ChangedBreaking
profile:Set(path, value)andprofile:Update(path, transform)on DataService profiles no longer create missing tables on the path, and no longer replace a value on the path that is not a table. They error with the part of the path that is the problem ("Stats does not exist", "Coins is not a table") and change nothing. The last key may still be new:profile:Set("Stats.Kills", 0)works whenStatsexists, so create a table first withprofile:Set("Stats", {}).profile:Remove(path)on something that does not exist returnsniland changes nothing.Patch.Set(root, keys, value)follows the same rule. EconomySystem, InventorySystem and MonetizationService add the tables of their own record themselves, so they keep working on a store you bind. EconomySystem and InventorySystem report a value of your own where they keep their record (a string atEconomy, for example) as an error instead of overwriting it. See Reading and writing data and Updating from 1.1.1. - ChangedBreaking
MonetizationService no longer binds itself to the first profile store a player session starts in. It keeps its records in a profile only after
MonetizationService:BindProfileStore(store), or whenProfileStoreNamein its configuration names the store. Without either, records live in memory for the session and product receipts are deduplicated on that server only, with a warning. See Where purchases are stored. - Added
DataService:CreateExtendedStore(name, scope?), withGetExtendedStoreandFindExtendedStore: a store for entries that need more room than the 4 MB one DataStore key holds, such as the build on a plot. Every entry has one master key withData, the small essential data you choose, and named sections for the big data. Each section is saved in keys of its own, split over several keys when it is bigger than one. A failed or interrupted save never leaves half-written data, and every section is checked when it is read. Every call costs more DataStore requests than aStore, profile store orDocument, so use it only for entries that do not fit in one key. New settings:ExtendedStoreChunkSize,ExtendedStorePendingTimeoutandExtendedStoreParallelRequests. See Extended stores. - Added
InventorySystem, on the server and the client: items, containers, equipment, crafting and world pickups, built around Roblox Tools. Every player gets the containers in
PlayerContainers(by default one 30-slotBackpack), saved in a DataService profile and mirrored to their client. Items with a Tool are Tools in the player's Backpack, given back after every respawn. Equipment puts Accessories on the character and adds up stats. Recipes take ingredients, kept tools and currency costs, with unlocks and crafting stations. Chests and world pickups hold items outside a player, and items can be sold, or bought in item shops, through EconomySystem. The client sees its own containers and anything opened to it, and sends requests that the server checks. Inventories live in the system's own profile store,Engine.Inventory, unless you callInventorySystem:BindProfileStore(store). - Added
ModerationSystem handles the violations Roblox reports in your experience. When it loads, it binds a violation processor with
ModerationService:BindViolationProcessor. Every report firesModerationSystem.RobloxViolation(violation, player?), is written to the audit log asRobloxViolation, and records aViolationsanction worthRobloxViolationPoints, so your escalation ladders act on Roblox's reports the same way they act on chat violations. SetModerationSystem.OnRobloxViolationto pick the consequence yourself, and readModerationSystem.IsViolationProcessorBoundto see whether the processor is bound. New settings:RobloxViolations,RobloxViolationPriority,RobloxViolationKind,RobloxViolationPoints,AcknowledgeRobloxViolationsandDefaultViolationReason. If your game binds its own violation processor, set the priority orAcknowledgeRobloxViolations = falseso both see the report. See Roblox violations. - Added
ModerationSystem:MockViolationAsync(report)sends a test report throughModerationService:MockViolationCallbackAsync, so you can try your violation handling in Studio. - Added
GroupRolesonModerationSystem:DefineRoleand on the roles in theRolessetting:{ [groupId] = { roleId or role name, ... } }gives the role to anyone holding one of those group roles. Groups can give a member several roles now, and a rank no longer says where a role sits, so this is the reliable way to map group staff.GroupRankskeeps working. See DefineRole. - Fixed
ModerationSystem:FilterTextAsync(text, from, { For = player })returned an empty string. It usedTextFilterResult:GetChatForUserAsync, which Roblox deprecated and which now always answers with an empty string. It usesGetNonChatStringForUserAsyncnow. - Changed
ModerationSystem no longer reads
Players.BanningEnabled: scripts cannot read it, so the check never worked. WithUseRobloxBanson, a ban always callsPlayers:BanAsync. When banning is off in Game Settings, that call fails and firesModerationSystem.RobloxBanFailedas before, and the moderation record still enforces the ban. - Added
MonetizationService:GetRobuxTransferEligibilityAsync(sender, receiverUserId), which resolves with"Eligible","SenderNotEligible","ReceiverNotEligible"or"Unknown", andMonetizationService:CanTransferRobuxAsync(sender, receiverUserId). Both useMarketplaceService:CanUsersTransferRobuxAsync, cached per sender and receiver forTransferEligibilityCacheSeconds. See Robux transfers. - Changed
MonetizationService:PromptRobuxTransferAsyncfirst asks Roblox whether both users may transfer Robux. When one of them may not, it rejects with"SenderNotEligible"or"ReceiverNotEligible"without opening a prompt, and fires the newMonetizationService.TransferRefused(sender, receiverUserId, reason). When Roblox cannot tell, the prompt opens as before. Turn the check off withCheckTransferEligibility = false. - Added
PlayerService:GetGroupRolesAsync(target, groupId), every public role the user holds in the group, highest first ({ GroupId, IsMember, Roles = { { Id, Name, Rank } } }), andPlayerService:HasGroupRoleAsync(target, groupId, roleIdOrName). See Friends, groups and ownership. - Changed
PlayerService:GetGroupRankAsync,:GetGroupRoleAsyncand:IsInGroupAsyncuseGroupService:GetRolesInGroupAsyncinstead of the deprecatedPlayer:GetRankInGroupandPlayer:GetRoleInGroup. Rank and role are those of the user's highest public role, as before, and membership comes from Roblox's ownIsMember. A user can hold several roles in a group;GetGroupRolesAsynclists them all. - Changed
PlayerService:GetHumanoidDescriptionAsyncandPlayerService:OwnsAssetAsyncusePlayers:GetHumanoidDescriptionFromUserIdAsyncandMarketplaceService:PlayerOwnsAssetAsyncinstead of the deprecated calls. - Changed
PlayerService's platform and screen info no longer probe
UserInputService:GetPlatform,VRService.VRDeviceNameandGuiService:GetScreenResolution. Scripts cannot call them, so those probes only ever failed.ScreenWidthandScreenHeightare the viewport size andVRDeviceisnil, as they already were in practice, and the detection confidence is never"Reported". - Fixed
DestructionService: a part that one client breaks locally (without
authoritative) only kept its collision on the server in games that reportWorkspace.AuthorityMode = Automatic. Scripts cannot readAuthorityMode, so the server always turned the part'sCanCollide,CanQueryandCanTouchoff, and server raycasts and NPCs went through a wall that was broken on one screen only. The server now looks at the reporting player's character: when that client moves its own character (classic games), the server part keeps colliding; when the server moves it (Server Authority, or a game that keeps characters owned by the server), the part is released as before. See Client breaks and Server Authority. - Fixed
DestructionService could not break anything whose textures together are wider or taller than 1024 pixels, and a single 1024 by 1024 texture was already too much. The shard texture atlas is now packed into one EditableImage of at most 1024 by 1024 pixels, and textures are scaled down when they do not fit.
- Changed
DestructionService shards look like the part they came from. A MeshPart's
SurfaceAppearancecarries over, with its color map,Colortint,AlphaModeand its normal, metalness, roughness and emissive maps. A transparent texture or decal shows the part's color underneath instead of white, decals keep theirColor3andTransparencyand stack byZIndex, pixelated textures stay pixelated, a textured MeshPart keeps itsTransparency, and the cut faces show the texture of the part they were cut from instead of the whole atlas squeezed together. A texture the game may not load no longer fails the break: those shards use the part's color, and one warning names the asset. See What the shards look like. - Added
GeometryService:SliceData(data, plane, capRegions?)takes an optional list of UV rectangles. A cut face whose edge lies in one of them gets its UVs inside that rectangle. See Working with mesh data. - Added
ResampleModeon EffectService decal definitions:Decals = { { Texture = ..., ResampleMode = Enum.ResamplerMode.Pixelated } }. See DecalDef. - Added
The
ScreenInsetssetting in UIService's configuration, anEnum.ScreenInsetsname for every screen's ScreenGui (empty keeps followingScreenIgnoreGuiInset), andScreenInsetson a layer definition (:DefineLayeror theLayerssetting), so screens and layers can respectDeviceSafeInsets. See Screens and dialogs. - Changed
PasswordSystem:Parseand:Verifyreject a PHC string with an empty parameter (m=19456,,t=2) instead of skipping it, and a UIService theme token path with an empty segment ("Colors..Text") no longer resolves.
Updating from 1.1.1
Two changes can ask something of your game.
Paths in profiles. profile:Set and profile:Update no longer create missing tables. A write to a path whose table does not exist yet now errors and names the missing part, such as Engine: DataService Profile:Set - Stats does not exist. Patch.Set follows the same rule.
profile:Set("Stats.Kills", 0) -- created Stats when it was missingif profile:Get("Stats") == nil then
profile:Set("Stats", {})
end
profile:Set("Stats.Kills", 0)Tables every profile needs belong in the profile store's template, so profile:Reconcile() adds them to profiles that were saved without them, and you never have to check.
MonetizationService's profile store. If you sell developer products and relied on MonetizationService picking your player profile store by itself, bind the store yourself before players join:
MonetizationService:BindProfileStore(PlayerData)Or set ProfileStoreName in the MonetizationService configuration. Records already saved in that store are used again as soon as it is bound. Without a store, receipts are only deduplicated on each server, and Engine warns you.
| Update model | When to change your game |
|---|---|
| Automatic updates | Right away. Every server started since the release runs 1.2.0. |
| Manual updates | When you click Update to v1.2.0 in the plugin. Until then your game stays on its current version and keeps working as it does. |
1.1.1
- Fixed
Engine:GetSystem("ModerationSystem")no longer errors with "ShouldDeliverCallback is a callback member of TextChannel; you can only set the callback value, get is not available" in a game that uses TextChatService. ModerationSystem now setsShouldDeliverCallbackon every text channel without reading it first. Roblox never lets a script read a callback, so aShouldDeliverCallbackyour game sets on a channel itself is not kept next to ModerationSystem's: whichever is set last wins. Put your own message checks inModerationSystem:AddChatFilter, or setChatModerationtofalsein the ModerationSystem configuration to keep your own callback. See Chat moderation. - Fixed
ModerationSystem:UnmuteAsync,ModerationSystem:UnbanAsyncandModerationSystem:LiftAsyncresolve with the sanctions they lifted, also when the player is in the server. For a player in the server they resolved with an empty list, although the lift itself worked. - Fixed
Reading
NetworkService.OnServerInvokeorNetworkService.OnClientInvokeno longer errors with "OnServerInvoke is a callback member of RemoteFunction; you can only set the callback value, get is not available". Both return the function you set, ornilwhen none is set, on the server and on the client.
1.1.0
- ChangedBreaking
Engine:GetService(name),Engine:GetSystem(name)andEngine:GetPackage(name)return the service, system or package itself, on the server and on the client. The:Api()step is gone. The first call for a name loads the module, and prints its load time whenLogServiceLoadsis on in the General configuration; later calls return the same table.
The plugin was updated the same day. See the Plugin Changelog.
Updating from 1.0.0
Remove :Api() after every GetService, GetSystem and GetPackage call, in Scripts and LocalScripts alike:
local DataService = Engine:GetService("DataService"):Api()
local EconomySystem = Engine:GetSystem("EconomySystem"):Api()
local Promise = Engine:GetPackage("Promise"):Api()local DataService = Engine:GetService("DataService")
local EconomySystem = Engine:GetSystem("EconomySystem")
local Promise = Engine:GetPackage("Promise")Studio's Find All / Replace All (Ctrl+Shift+F) changes every script at once: replace ):Api() with ). A script that stores what a getter returns and calls :Api() on it later needs the same change by hand.
| Update model | When to change your scripts |
|---|---|
| Automatic updates | Right away. Every server started since the release runs 1.1.0, and code that still calls :Api() errors there. |
| Manual updates | When you click Update to v1.1.0 in the plugin. Until then your game stays on 1.0.0 and the old code keeps working. |
1.0.0
The first release of Engine.
Services
17 services: AudioService, ComputeService, CryptographyService, DataService, DestructionService, EffectService, GeometryService, JunkService, MonetizationService, NetworkService, PerformanceService, PlayerService, SchedulerService, SpatialService, StatisticsService, TimeService and UIService.
Systems
4 systems: EconomySystem, LeaderboardSystem, ModerationSystem and PasswordSystem.
Packages
24 packages: BehaviorTree, Bin, Collections, Compress, Graph, Guard, Hash, Id, MathKit, Noise, Octree, Patch, Pool, Promise, Rand, RateLimiter, Serde, Signal, Spline, Spring, State, StateMachine, TableKit and Timer.
Configuration and updates
- Per-module configuration with defaults, and automatic replication of client-safe configuration.
- Automatic updates: a small shell loads the newest published engine when each server starts.
- Manual updates: a fixed copy of the engine stored in your place, updated from the plugin.
The Engine plugin was released the same day. See the Plugin Changelog.