The Folly of Client-side Position Propogation

In 2018, Roblox removed the ability to toggle the "FilteringEnabled" feature for games, which controlled whether or not incoming client data was treated as canon by the server, forcing incoming client data to always be treated as non canon.

Once, FilteringEnabled wasn't, and all client data was canon. All Roblox games then were programmed with the knowledge that anything occurring on the client was propogated to the server and then to all clients. Once FilteringEnabled was forced and all client data decanonized, those games broke.

Since FilteringEnabled, everything that occurs must either spawn from the server or be created locally upon instruction from the server. The only exception is a client's character's position.

The hatred for the FilteringEnabled change is in the past, and only the bitterness of having disfunctional old games remains, but I am neither a fan nor foe of FilteringEnabled; I am happy to have a working paradigm and would have been happy with canon client data too; but the strength of the current server-centric paradigm is weakend by that one exception to the rule: having the client propogate the character's c-frame.

The current expectation is that the server intercepts and legitimizes all desires of the client to act on the game-world. Not only is that expectation subverted, but control over that interaction with the game-world is completely absent.

The biggest worry with client-replicated c-frame values is that exploiters could be taking advantage of the system to teleport around the game, which can mean every mob to be farmed is being soaked up by the exploiter, or the rare loot is being instantly taken, or the hacker is mass-killing other players, or the hacker is moving objects welded to the player (seats auto-weld to the humanoid root part and thus whatever is welded to the seat is welded to the player) or in the worst cases, the hacker is reaching an exploitable location like a dev-room and wreaking havoc.

A corrective measure would be to identify canonically-impossible changes in position and rectify the player's c-frame, inducing rubber-banding on the player. One could also check the ping between server and player and identify if the movement was probably caused by lag, and if not, ban or kick the player as rectification.

The implementation of this idea would be to hook onto an event associated with a changing c-frame, maybe :GetPropertyChangedSignal("CFrame"); but GetPropertyChangedSignal does not fire for physics-related changes, and worse yet, there is no event associated with those physics-changes. The best solution the Roblox LUA documentation provides is to connect to the RunService.Stepped event, which means every physics frame, you would check the players' positions. Physics frames, however, do not run consistently, and once the server is encumbered, may take large amounts of time to be processed: enough time that the player's position has been propogated from his client, and is then rubber-banded several seconds after play-time to rectify the large disposition.

If you thought to do the check on the client, to forego any extra latency connecting to RunService.Stepped on the server, you are making for yourself a timebomb: an exploiter can simply unbind or delete the script given to his character and move on without the check occurring. But alternatively, to do the check on the server means spawning an asynchronous function that, were the server to become overloaded, might run last of all functions, enabling an exploiter to continuously trigger an intensive remote-event on the server and then alter his position, overcoming the position rectification.

The inability to selectively replicate player c-frames horribly limits the anti-exploitation measures that developers can take within their games and serves no benefit other than to provide support for one feature of a legacy system which has otherwise been entirely rubbed out. Roblox would not need to buy Byfron and develop "Hyperion" if the company gave this tiny degree of freedom to game programmers.

The solution would be easy to implement: allow the developer to bind a callback to the server's reception of player c-frame data, one-time, exactly like how MarketplaceService.ProcessReceipt works. The callback function receives as a first parameter the player instance which sent the data, and as the second parameter the c-frame he wants to move to; the function is then expected to return the c-frame the player will be set to. The player's attempts to change his position happen instantly locally; then if the desired c-frame does not equal the actual c-frame the player will take, the player's position is updated for all clients, including the player. Otherwise, the player's position is propogated to all clients but the player's client. This method keeps the existing delay between the player's movement and the replication of that movement to other players; and, the existing codebase that handles a player's position does not have to be altered, like an event would mandate.

I pray this update occurs; I would provide the code myself with access to the engine's repository, so easy would the update be. But Roblox does not have a reputation for making meaningful updates, and especially not updates which give more power to developers. These days, Roblox is more concerned with effacing its identity: killing user advertisements and sunsetting the Robux. My wishes are thus, a far cry.