Client Difference Log
API Changes
Added Property float CanvasGroup.GroupTransparency
Added Property MaterialVariant MaterialService.TerrainAsphalt {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainBasalt {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainBrick {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainCobblestone {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainConcrete {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainCrackedLava {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainGlacier {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainGrass {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainGround {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainIce {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainLeafyGrass {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainLimestone {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainMud {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainPavement {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainRock {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainSalt {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainSand {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainSandstone {RobloxSecurity} [NotScriptable]
Added Property MaterialVariant MaterialService.TerrainSnow {RobloxSecurity} [NotScriptable]
Added Property bool MaterialService.hasPropertyWarningInStudio {RobloxSecurity} [Hidden] [ReadOnly]
Added Property Enum.MaterialPattern MaterialVariant.MaterialPattern
Added Property bool VoiceChatService.GenerateDefaultChannel {PluginSecurity} [NotBrowsable] [Preliminary]
Added Function void MaterialService:ClearOverrideTerrainMaterial(Enum.Material material)
Added Function MaterialVariant MaterialService:GetOverrideTerrainMaterial(Enum.Material material)
Added Function void MaterialService:SetOverrideTerrainMaterial(MaterialVariant materialVariant)
Added Function void StudioService:DEPRECATED_SetDocumentDisplayName(string newName) {RobloxScriptSecurity}
Added Enum MaterialPattern
Added EnumItem MaterialPattern.Regular : 0
Added EnumItem MaterialPattern.Organic : 1
Added EnumItem PathWaypointAction.Custom : 2
Added EnumItem StudioStyleGuideColor.DropShadow : 32
Added Tag [NotBrowsable] to Class CanvasGroup
Added Tag [ReadOnly] to Property RigidConstraint.Broken
Changed the category of Property MaterialVariant.StudsPerTile
from: "Appearance"
to: "Appearance For Part"
Changed the serialization of Property RigidConstraint.Broken
from: [<š¾|š> Saves|Loads]
to: [<š¾> SaveOnly]
Changed the parameters of Event AssetImportService.UploadFinished
from: (bool succeeded)
to: (bool succeeded, Dictionary errorMap)
Changed the value of EnumItem StudioStyleGuideColor.Shadow from 32 to 33
Changed the value of EnumItem StudioStyleGuideColor.Light from 33 to 34
Changed the value of EnumItem StudioStyleGuideColor.Dark from 34 to 35
Changed the value of EnumItem StudioStyleGuideColor.Mid from 35 to 36
Changed the value of EnumItem StudioStyleGuideColor.MainText from 36 to 37
Changed the value of EnumItem StudioStyleGuideColor.SubText from 37 to 38
Changed the value of EnumItem StudioStyleGuideColor.TitlebarText from 38 to 39
Changed the value of EnumItem StudioStyleGuideColor.BrightText from 39 to 40
Changed the value of EnumItem StudioStyleGuideColor.DimmedText from 40 to 41
Changed the value of EnumItem StudioStyleGuideColor.LinkText from 41 to 42
Changed the value of EnumItem StudioStyleGuideColor.WarningText from 42 to 43
Changed the value of EnumItem StudioStyleGuideColor.ErrorText from 43 to 44
Changed the value of EnumItem StudioStyleGuideColor.InfoText from 44 to 45
Changed the value of EnumItem StudioStyleGuideColor.SensitiveText from 45 to 46
Changed the value of EnumItem StudioStyleGuideColor.ScriptSideWidget from 46 to 47
Changed the value of EnumItem StudioStyleGuideColor.ScriptBackground from 47 to 48
Changed the value of EnumItem StudioStyleGuideColor.ScriptText from 48 to 49
Changed the value of EnumItem StudioStyleGuideColor.ScriptSelectionText from 49 to 50
Changed the value of EnumItem StudioStyleGuideColor.ScriptSelectionBackground from 50 to 51
Changed the value of EnumItem StudioStyleGuideColor.ScriptFindSelectionBackground from 51 to 52
Changed the value of EnumItem StudioStyleGuideColor.ScriptMatchingWordSelectionBackground from 52 to 53
Changed the value of EnumItem StudioStyleGuideColor.ScriptOperator from 53 to 54
Changed the value of EnumItem StudioStyleGuideColor.ScriptNumber from 54 to 55
Changed the value of EnumItem StudioStyleGuideColor.ScriptString from 55 to 56
Changed the value of EnumItem StudioStyleGuideColor.ScriptComment from 56 to 57
Changed the value of EnumItem StudioStyleGuideColor.ScriptKeyword from 57 to 58
Changed the value of EnumItem StudioStyleGuideColor.ScriptBuiltInFunction from 58 to 59
Changed the value of EnumItem StudioStyleGuideColor.ScriptWarning from 59 to 60
Changed the value of EnumItem StudioStyleGuideColor.ScriptError from 60 to 61
Changed the value of EnumItem StudioStyleGuideColor.ScriptWhitespace from 61 to 62
Changed the value of EnumItem StudioStyleGuideColor.ScriptRuler from 62 to 63
Changed the value of EnumItem StudioStyleGuideColor.DocViewCodeBackground from 63 to 64
Changed the value of EnumItem StudioStyleGuideColor.DebuggerCurrentLine from 64 to 65
Changed the value of EnumItem StudioStyleGuideColor.DebuggerErrorLine from 65 to 66
Changed the value of EnumItem StudioStyleGuideColor.DiffFilePathText from 66 to 67
Changed the value of EnumItem StudioStyleGuideColor.DiffTextHunkInfo from 67 to 68
Changed the value of EnumItem StudioStyleGuideColor.DiffTextNoChange from 68 to 69
Changed the value of EnumItem StudioStyleGuideColor.DiffTextAddition from 69 to 70
Changed the value of EnumItem StudioStyleGuideColor.DiffTextDeletion from 70 to 71
Changed the value of EnumItem StudioStyleGuideColor.DiffTextSeparatorBackground from 71 to 72
Changed the value of EnumItem StudioStyleGuideColor.DiffTextNoChangeBackground from 72 to 73
Changed the value of EnumItem StudioStyleGuideColor.DiffTextAdditionBackground from 73 to 74
Changed the value of EnumItem StudioStyleGuideColor.DiffTextDeletionBackground from 74 to 75
Changed the value of EnumItem StudioStyleGuideColor.DiffLineNum from 75 to 76
Changed the value of EnumItem StudioStyleGuideColor.DiffLineNumSeparatorBackground from 76 to 77
Changed the value of EnumItem StudioStyleGuideColor.DiffLineNumNoChangeBackground from 77 to 78
Changed the value of EnumItem StudioStyleGuideColor.DiffLineNumAdditionBackground from 78 to 79
Changed the value of EnumItem StudioStyleGuideColor.DiffLineNumDeletionBackground from 79 to 80
Changed the value of EnumItem StudioStyleGuideColor.DiffFilePathBackground from 80 to 81
Changed the value of EnumItem StudioStyleGuideColor.DiffFilePathBorder from 81 to 82
Changed the value of EnumItem StudioStyleGuideColor.ChatIncomingBgColor from 82 to 83
Changed the value of EnumItem StudioStyleGuideColor.ChatIncomingTextColor from 83 to 84
Changed the value of EnumItem StudioStyleGuideColor.ChatOutgoingBgColor from 84 to 85
Changed the value of EnumItem StudioStyleGuideColor.ChatOutgoingTextColor from 85 to 86
Changed the value of EnumItem StudioStyleGuideColor.ChatModeratedMessageColor from 86 to 87
Changed the value of EnumItem StudioStyleGuideColor.Separator from 87 to 88
Changed the value of EnumItem StudioStyleGuideColor.ButtonBorder from 88 to 89
Changed the value of EnumItem StudioStyleGuideColor.ButtonText from 89 to 90
Changed the value of EnumItem StudioStyleGuideColor.InputFieldBorder from 90 to 91
Changed the value of EnumItem StudioStyleGuideColor.CheckedFieldBackground from 91 to 92
Changed the value of EnumItem StudioStyleGuideColor.CheckedFieldBorder from 92 to 93
Changed the value of EnumItem StudioStyleGuideColor.CheckedFieldIndicator from 93 to 94
Changed the value of EnumItem StudioStyleGuideColor.HeaderSection from 94 to 95
Changed the value of EnumItem StudioStyleGuideColor.Midlight from 95 to 96
Changed the value of EnumItem StudioStyleGuideColor.StatusBar from 96 to 97
Changed the value of EnumItem StudioStyleGuideColor.DialogButton from 97 to 98
Changed the value of EnumItem StudioStyleGuideColor.DialogButtonText from 98 to 99
Changed the value of EnumItem StudioStyleGuideColor.DialogButtonBorder from 99 to 100
Changed the value of EnumItem StudioStyleGuideColor.DialogMainButton from 100 to 101
Changed the value of EnumItem StudioStyleGuideColor.DialogMainButtonText from 101 to 102
Changed the value of EnumItem StudioStyleGuideColor.InfoBarWarningBackground from 102 to 103
Changed the value of EnumItem StudioStyleGuideColor.InfoBarWarningText from 103 to 104
Changed the value of EnumItem StudioStyleGuideColor.ScriptEditorCurrentLine from 104 to 105
Changed the value of EnumItem StudioStyleGuideColor.ScriptMethod from 105 to 106
Changed the value of EnumItem StudioStyleGuideColor.ScriptProperty from 106 to 107
Changed the value of EnumItem StudioStyleGuideColor.ScriptNil from 107 to 108
Changed the value of EnumItem StudioStyleGuideColor.ScriptBool from 108 to 109
Changed the value of EnumItem StudioStyleGuideColor.ScriptFunction from 109 to 110
Changed the value of EnumItem StudioStyleGuideColor.ScriptLocal from 110 to 111
Changed the value of EnumItem StudioStyleGuideColor.ScriptSelf from 111 to 112
Changed the value of EnumItem StudioStyleGuideColor.ScriptLuauKeyword from 112 to 113
Changed the value of EnumItem StudioStyleGuideColor.ScriptFunctionName from 113 to 114
Changed the value of EnumItem StudioStyleGuideColor.ScriptTodo from 114 to 115
Changed the value of EnumItem StudioStyleGuideColor.ScriptBracket from 115 to 116
Changed the value of EnumItem StudioStyleGuideColor.AttributeCog from 116 to 117
Removed Property CanvasGroup.Transparency
Removed Function StudioService:PromptForLocalSave
Removed Function StudioService:SetDocumentDisplayName
Removed Event StudioService.FirstPublishOfCloudPlace
(Click here for a syntax highlighted version!)
thank you!
just had to remove the cursor component from my roact app textboxes because you canāt access user input while textboxes are focused. great timing!
Fixed BillboardGui rendering in VR.
Implemented the new Roblox VR system with the following components :
- 3rd person and 1st person comfort camera module with vehicle support
- new in-game menu and player gui interaction
- support for Oculus Touch and Valve Index controllers
More VR stuff! Although, this is the third(?) time āSupport for ā¦ Valve Index controllersā has shown up.
For those who care: Vector2/3.zero/one/xAxis/...
, CFrame.identity
, and cframe.Rotation
are live and ready for use.
This new Luau debuggerās API is amazing
Let us use it in our own plugins when it finally ships (or free DebuggerManager)
coroutine.close is also a godsend
This seems great but how can it be used though?
Can someone provide an example use case?
Iām sure a lot of us are confused and wondering about the same thing
Fun note, with the addition of this function you can now write a pure Lua exit()
function:
function exit()
task.defer(coroutine.close, coroutine.running())
coroutine.yield()
end
Now to wait for interruptible coroutines, where u can yield running coroutines, with a timeout or from another thread with parallel lua.
Interruptible coroutines doesnāt make sense, there is only one running coroutine. The running coroutine canāt forcibly yield a normal coroutine (a coroutine which resumed another coroutine, and is still waiting). The running coroutine can obviously yield and return to what resumed it. Yielding with timeout is just one of the waiting functions (task.wait
, wait
)? Interrupting a coroutine from another thread makes no sense, threads and coroutine are very different concepts (and currently there is no way to share data between threads during unsynchronized execution).
local co = coroutine.create(function()
while true do
print(1)
task.wait()
end
end)
coroutine.resume(co) -- start printing 1 every frame
task.wait(2)
coroutine.close(co) -- stop printing every frame
-- and then one more error message appears after it stops printing
Is it intended that this generates an error? The error this gives is also not very helpful.
All other task.
methods should not generate any error; wait
does because it pushes an extra argument to stack before resumption which confuses the scheduler. We have a separate change in the later release that should address this.
@FieryEvent
In parralel lua, I would expect pausing an actor itself, rather than a specific thread (you of course canāt access threads cross actor). I can imagine that actually having some nice use cases.
There is also an alternative interpretation which would work for cooperative threading, though, I doubt it would really be justifiableā¦ The coroutine will just yield again after resumption, e.g. if the coroutine is in the middle of a timed wait, once the wait expires it will remain yielding. This really only makes sense because cases where you are expecting code to start running again on its own are also already the exact cases where you want this, aside from wanting to stop code mid-execution, which, isnāt really feasible in any form.
Assuming it existed, probably would only want this behaviour for yields not explicitly initiated via coroutine.yield
(in other words, only non-lua yields, otherwise you get confusing behaviour)
VR is an input method, the same applies to controller. If I join your game while using an Xbox controller, but I receive a message it was disabled, I wouldnāt enjoy my experience. The same applies to VR.
Thatās the point though. I donāt want VR being used in my experience and I donāt want to support VR and its components either. The developer should have a choice on which input methods they want to support (i.e. we already can restrict our experience to certain devices).
I donāt want people using VR in my experience because it looks awful in comparison to playing the intended way (through a computer, handheld device or gamepad) and thereās no support in the codebase for VR either explicitly because itās not supposed to be played on VR.
You should receive a message that tells you its disabled and that should prompt you to change the way youāre playing to the intended way. What you said is exactly what I want.
Would it be possible to incorporate this with an InvokeClient wrapper and have coroutine.close kill a potentially hung thread if the client overwrites OnClientInvoke to not return anything, or should we still not rely on client invocation and instead use two-way RemoteEvent communication?
Iām not too familiar with coroutines so donāt know how this could be achieved but I just thought about how it could be useful for preventing server hangs with client invocations. I tried mocking something up but not sure if this is the correct way to use it.
local function exitCo()
task.defer(coroutine.close, coroutine.running())
coroutine.yield()
end
local function safelyInvokeClient(remoteFunction, player, timeout, ...)
timeout = if type(timeout) == "number" and timeout >= 0 then timeout else 0
task.delay(timeout, exitCo)
local results = table.pack(remoteFunction:InvokeClient(player, ...))
return table.unpack(results)
end
task.delay(timeout, exitCo)
Alas, that wonāt do what you want. delay
will just create a new coroutine and the exitCo
call will stop that coroutine instead.
You can however write the safelyInvokeClient
call that you desire (done here with what I think is the minimal correct code that doesnāt allocate any lambdas so it should be very cheap to use. You could get slightly cheaper if you pooled delay and invocation coroutines to re-use them but then the code would get pretty complicated for not that much benefit):
local function spawnIfNotResumed(resumed, thread, ...)
if not resumed.value then
resumed.value = true
task.spawn(thread, ...)
end
end
local function invokeAndMaybeResume(resumed, thread, remoteFunction, player, ...)
spawnIfNotResumed(resumed, thread, true, remoteFunction:InvokeClient(player, ...))
end
-- Returns: true/false, followed by the return values if true (didn't time out)
local function safelyInvokeClient(remoteFunction, player, timeout, ...)
local thisThread = coroutine.running()
local resumed = {value = false}
task.delay(timeout, spawnIfNotResumed, resumed, thisThread, false)
task.spawn(invokeAndMaybeResume, resumed, thisThread, remoteFunction, player, ...)
return coroutine.yield()
end
@colbert2677
Personally Iād say that this sort of behaviour is probably good to avoid since it is sort of a thread hack (similar to resuming during a WaitForChild, or resuming during a wait call), and, Iām not really sure I even understand how the behaviour with the remote would work (e.g. with GC and such), that behaviour is made unclear. Not to say this wouldnāt be useful or anything, or that this sort of thing should never be done or will never show up or something.
When it comes to requesting data from the client, best practice is definitely still to write asynchronous code imo.
E.g. the best model to follow imo is that the server will ask the client to report some info, without any expectations that the client will ever actually comply with that request, and, then the client submits a request giving the server an update on the info, and the server does something with that.
Itās usually better to avoid cases where code will act any different because a client didnāt respond, since it leads to some unpredictability, and, can lead to exploits that are super difficult to understand (e.g. exploiters sending responses late, or duplicating responses in the case of events, or not sending responses at all, etc)
I think the best way to think about a client response to a server request is less like a response and more like an action the client is making on its own. The server is just asking the client to perform an action, and leaving it open for whether or not it does can be useful.
Thereās a few cases where synchronous code is more elegant (though in my experience those cases are always the client calling the server where you wouldnāt need said timeout).
For example, if the user is placing some object this is a very clean pattern:
local tempObject = placeObject(...)
PlaceObjectRemote:InvokeServer(...)
tempObject:Destroy()