Players and Networking
PlayerService
Everything about a player in one place. Names and badges, membership, device and screen, settings, policies, character state, avatar, friends, groups and ownership, cached and retried.
Getting it
local PlayerService = Engine:GetService("PlayerService")Overview
Information about a player is spread across a dozen Roblox services, many of them slow web calls that fail now and then. PlayerService puts it behind one API:
- Identity: user ids, usernames, display names, formatted name tags with Roblox's verified, Premium and Plus marks, developer and video-star roles.
- Client info: device class, platform, input methods, screen size and safe area, graphics, audio, camera and accessibility settings, locale, FPS and ping. Each client measures this itself and sends it to the server.
- Session and policy: join data, session time, country, and Roblox's
PolicyServicerules (ads, paid random items, trading...). - Character and gameplay: health, movement, seat, team, camera settings.
- Avatar and social: thumbnails, worn items, friends, groups, badges, game passes, subscriptions.
Web lookups are cached (per kind, see configuration) and retried with backoff. Methods ending in Async return a Promise; the synchronous versions answer from what is already known and return nil when it is not.
Anywhere a method takes a target, you can pass a Player or a user id.
local PlayerService = Engine:GetService("PlayerService")
PlayerService.PlayerReady:Connect(function(player, info)
print(PlayerService:FormatName(player), "joined on", info.Platform.DeviceClass)
if PlayerService:IsOnMobile(player) then
giveMobileControls(player)
end
if not PlayerService:IsAllowed(player, "PaidRandomItems") then
hideLootBoxes(player)
end
end)Events
| Event | Fires when |
|---|---|
PlayerReady: Signal<(player, clientInfo)> | A player's client info arrives for the first time. The best moment to adapt the game to their device. |
ClientInfoReceived: Signal<(player, clientInfo)> | Every time client info arrives. |
ClientInfoChanged: Signal<(player, clientInfo)> | Client info arrives and something changed (rotated the phone, plugged in a gamepad...). |
CharacterReady: Signal<(player, character)> | A character has spawned and loaded. |
Identity
| Method | Description |
|---|---|
:GetUserId(target), :GetPlayer(target), :IsOnline(target) | Basics. GetPlayer returns nil for players not in this server. |
:GetUsername(target, decorate?), :GetDisplayName(target, decorate?) | Names. With decorate, Roblox's badge marks are appended. |
:FormatName(target, style?) | A name in a style: "DisplayName", "Username", "Both" (Display (@user)) or "BothIfDifferent". |
:GetNameTag(target, config?), :GetNameTagAsync(target, config?) | Everything needed to show a name: plain and decorated text, badges, membership, roles. |
:HasVerifiedBadge(target) | Roblox's verified checkmark. |
:GetMembership(target) | "Plus", "Premium" or "None". |
:IsPlus(target), :IsPremium(target), :IsSubscriber(target) | |
:GetAccountAge(target), :GetAccountCreated(target) | Days old, and creation time. |
:IsDeveloper(target), :IsDeveloperAsync(target) | The experience owner, or at least DeveloperMinGroupRank in the owning group. |
:IsVideoStar(target), :IsVideoStarAsync(target) | Member of Roblox's Video Stars program. |
:GetUsernameAsync(userId), :GetUserIdAsync(username) | Lookups for players who are not in the server. |
local tag = PlayerService:GetNameTag(player)
nameLabel.Text = tag.Text -- e.g. "Builderman (@builderman) " plus Roblox's marksBadge glyphs come from Roblox's own private-use characters. Engine checks at startup whether the font can draw them (ProbeGlyphs) and falls back to text such as (verified) when it cannot.
Device and client
These need the client info the player's client sends. The first report arrives about a second after joining (PlayerReady); until then they return nil.
| Method | Description |
|---|---|
:GetClientInfo(player) | All of it: { UserId, Platform, Screen, Preferences?, Performance?, CollectedAt, ReceivedAt, Stale }. |
:RefreshClientInfoAsync(player, timeout?) | Asks the client for a fresh report. |
:GetPlatformInfo(player) | Platform, OS, device class, client version, input. |
:GetDevice(player) | "Desktop", "Phone", "Tablet", "Console", "VR", "TV", "Hybrid" or "Unknown". |
:GetPlatform(player), :GetOS(player) | |
:GetDetectionConfidence(player), :GetDetectionSignals(player) | How sure the device guess is ("Strong", "Heuristic" or "Guess") and why. |
:GetInput(player) | Touch, keyboard, mouse, gamepad (count and brand), VR, preferred and last input. |
:GetScreen(player) | Viewport size, scale, orientation, size class, safe-area insets, fullscreen, touch controls. ScreenWidth and ScreenHeight are the viewport size, because scripts cannot read the device's resolution. |
:GetPreferences(player) | Graphics, audio, camera, movement, accessibility, VR and locale settings. |
:GetGraphics(player), :GetAccessibility(player), :GetClientLocale(player) | Parts of the above. |
:GetTimezoneOffset(player) | Minutes from UTC. |
:GetPerformance(player), :GetFps(player) | FPS, frame time, ping, memory, quality and device tier from PerformanceService. |
:IsOnMobile(player), :IsOnDesktop(player), :IsOnConsole(player), :IsInVR(player) | Quick checks. |
:ReadSetting(name) | Client only: read one of the local player's settings directly. |
Roblox does not let scripts ask which platform a player is on, so device detection works it out from the client build, the screen and input signals; GetDetectionSignals lists what it used. Accessibility settings such as ReducedMotion and PreferredTransparency let you respect what players chose.
Session and policy
| Method | Description |
|---|---|
:GetSessionInfo(player) | Join time, session length, ping, locale, membership, account age, join data. |
:GetSessionTime(player), :GetPing(player) | |
:GetJoinData(player) | Where they came from: source place, referrer, party members, teleport and launch data. |
:GetLocale(player) | Their Roblox locale. |
:GetCountryAsync(player) | Country or region code from LocalizationService. Server only. |
:GetAgeVerificationAsync(player) | What can be inferred about age verification, with confidence and signals. Server only. |
:GetPolicy(player), :GetPolicyAsync(player) | Roblox's PolicyService result. Prefetched for every player on join by default. |
:IsAllowed(player, policy), :IsAllowedAsync(player, policy) | Whether something is allowed for this player. |
:IsSubjectToChinaPolicies(player) |
Policy names (also in PlayerService.Policies): "Ads", "PaidRandomItems", "ContentSharing", "CommerceProducts", "Subscriptions", "PaidItemTrading", "PhotoToAvatar", "EndlessContentLoad", "EndlessContentAutoplay".
Character and gameplay
| Method | Description |
|---|---|
:GetCharacterInfo(player) | Alive, health, rig type, humanoid state, floor material, speeds, position, look direction, moving, seated, accessories. |
:GetGameplayInfo(player) | Team, camera and movement modes, zoom, display distances, respawn location. |
:IsAlive(player), :GetPosition(player) | |
:GetCharacterAsync(player, timeout?) | Resolves with the character once it exists. |
:GetAlivePlayers() | Every player with a living character. |
Avatar
| Method | Description |
|---|---|
:GetThumbnailAsync(target, type?, size?) | { Image, IsReady, Type, Size }. Type is an Enum.ThumbnailType name such as "HeadShot" or "AvatarBust". |
:GetThumbnailsAsync(target, types?, size?) | Several thumbnail types at once. |
:GetAppearanceAsync(target) | Rig type, worn assets, body colours, scales, emotes. |
:GetHumanoidDescriptionAsync(target) | The HumanoidDescription. |
Friends, groups and ownership
| Method | Description |
|---|---|
:GetFriendsAsync(target), :GetFriendCountAsync(target) | Friend list (up to MaxFriendsFetched). |
:GetOnlineFriendsAsync(player) | Online friends with where they are. |
:IsFriendsWithAsync(player, userId) | |
:GetFriendsWhoPlayedAsync(player) | Friends who have played this experience (Roblox's GetFriendsWhoPlayedAsync). |
:GetGroupsAsync(target) | Groups with rank, role and primary flag. |
:GetGroupRolesAsync(target, groupId) | Every public role the user holds in the group, highest first: { GroupId, IsMember, Roles = { { Id, Name, Rank } } }. |
:HasGroupRoleAsync(target, groupId, roleIdOrName) | Whether the user holds that role. |
:GetGroupRankAsync(target, groupId), :GetGroupRoleAsync(target, groupId) | The rank and name of the user's highest public role. |
:IsInGroupAsync(target, groupId) | |
:HasBadgeAsync(target, badgeId), :GetBadgeInfoAsync(badgeId) | |
:OwnsGamePassAsync(target, gamePassId) | Cached for OwnershipCacheSeconds. |
:OwnsAssetAsync(player, assetId) | |
:GetSubscriptionAsync(player, subscriptionId) | { IsSubscribed, IsRenewing?, ExpirationDate? }. |
:InvalidateOwnership(target) | Forget cached ownership, for example right after a purchase. |
A user can hold several roles in one group. GetGroupRankAsync and GetGroupRoleAsync only tell you about the highest one, so check for a specific role, such as your moderators, with HasGroupRoleAsync or GetGroupRolesAsync.
Profiles
GetProfileAsync
PlayerService:GetProfileAsync(target, config: { Sections?, ThumbnailTypes?, ThumbnailSize?, Force? }?): Promise<Profile>Collects many of the above in one call: sections Identity, Session, Client, Character, Gameplay, Avatar and Social (default from configuration). Sections that fail are listed in Errors instead of failing the whole profile, and the result is cached; Force rebuilds it, :InvalidateProfile(target) forgets it. Ideal for a player card or an admin panel.
Properties
| Property | Description |
|---|---|
LocalPlayer | Read-only. Players.LocalPlayer on the client. |
NameStyle | Default style for names. |
HideSubscriptionWhenVerified | Show only the verified mark on verified subscribers. |
TelemetryEnabled | Whether clients send their info at all. |
CacheSeconds | Default cache time of web lookups. |
Privacy
Client info is collected by your game's client and sent to your game server only. Engine does not send it anywhere else. Turn it off with TelemetryEnabled = false, or leave out settings and performance with CollectPreferences and CollectPerformance.
Configuration
DefaultConfiguration/Services/PlayerService46 settings| Setting | Default | What it does |
|---|---|---|
| CacheSeconds | 300 | Default TTL of a resolved web lookup, used by anything without its own below. |
| NegativeCacheSeconds | 20 | Seconds a failed lookup is remembered before it is asked again. |
| IdentityCacheSeconds | 1800 | TTL of name/userId lookups. |
| OwnershipCacheSeconds | 30 | TTL of gamepass/badge/asset ownership lookups. |
| SocialCacheSeconds | 120 | TTL of friend and group membership lookups. |
| RetryAttempts | 3 | Retries of a failed web call. |
| RetryBaseSeconds | 0.5 | First retry delay; doubles each attempt up to RetryMaxSeconds. |
| RetryMaxSeconds | 8 | Cap on the retry delay. |
| RetryJitter | 0.25 | Fraction of each retry delay randomised; 0 disables it. |
| RequestTimeoutSeconds | 15 | Seconds a single web call may take before it counts as failed. |
| MaxFriendsFetched | 200 | Ceiling on friends walked through FriendPages. |
| MaxGroupsFetched | 100 | Ceiling on groups walked through GroupService. |
| WarnOnLookupFailure | true | Warn when a web lookup fails for good. |
| PrefetchPolicy | true | Look up every player's PolicyService info on join so the sync policy checks can answer. |
| DefaultThumbnailType | "HeadShot" | Enum.ThumbnailType name used when none is passed. |
| DefaultThumbnailSize | 420 | Enum.ThumbnailSize number used when none is passed. |
| ThumbnailFallback | "" | Image used while a thumbnail is still rendering or failed. |
| NameStyle | "BothIfDifferent" | Default for :FormatName and a NameTag's Text: "DisplayName" | "Username" | "Both" | "BothIfDifferent". |
| BothFormat | "%s (@%s)" | Format of the "Both" style: display name, then username. |
| UsernamePrefix | "@" | Prefix put before a username. |
| BadgeSeparator | " " | Separator between a name and its badges. |
| HideSubscriptionWhenVerified | false | Draw only the verified checkmark on a verified subscriber instead of both marks. |
| Badges | { Verified = { Glyph = "\u{E000}", Fallback = "(verified)", Label = "Verified", Order = 1, }, Plus = { Glyph = "\u{E003}", Fallback = "(plus)", Label = "Roblox Plus", Order = 2, }, Premium = { Glyph = "\u{E001}", Fallback = "(premium)", Label = "Roblox Premium", Order = 3, }, } | Marks Roblox itself puts next to a name. |
| ProbeGlyphs | true | Probe every Glyph through TextService at boot and fall back when the font lacks it. |
| DeveloperMinGroupRank | 255 | Rank in the owning group at or above which a player counts as a developer (255 is the owner). |
| VideoStarGroupId | 4199740 | Roblox's own Video Stars group; membership is the video-star role. |
| TelemetryEnabled | true | Whether clients publish their device/settings/screen info to the server at all. |
| TelemetryJoinDelaySeconds | 1 | Seconds a client waits after joining before its first publish. |
| TelemetryRefreshSeconds | 20 | Seconds between a client re-measuring the fields that move and republishing if any changed. |
| TelemetryHeartbeatSeconds | 60 | Seconds between unconditional republishes, so fps/memory/ping are never older than this. |
| FpsSampleSeconds | 2 | Seconds of frames the fps figure is averaged over. |
| TelemetryStaleSeconds | 120 | Client info older than this is reported Stale by :GetClientInfo. |
| TelemetryRequestTimeoutSeconds | 10 | Seconds :RefreshClientInfoAsync waits on a client before rejecting. |
| TelemetryRatePerSec | 2 | Per-player rate limit on the client-to-server publish. |
| TelemetryBurst | 6 | Burst the publish rate limiter allows. |
| CollectPerformance | true | Include a PerformanceService sample (fps, frame time, memory, ping) in every publish. |
| CollectPreferences | true | Include the player's graphics/audio/camera/movement settings in every publish. |
| AppleGlyphCodepoint | 0xF8FF | Apple's logo glyph in the system font; separates iOS from Android. |
| InvalidGlyphCodepoint | 0xFFFF | Codepoint guaranteed to have no glyph anywhere. |
| TabletMinViewportDiagonal | 1200 | Viewport diagonal at or above which a touch device counts as a tablet rather than a phone. |
| PhoneMinAspectRatio | 1.85 | Viewport aspect ratio at or above which a touch device counts as a phone whatever its diagonal. |
| HybridDeviceClass | "Hybrid" | DeviceClass reported for a machine with both touch and a keyboard. |
| RedetectOnInputChange | true | Re-run platform detection when a gamepad is plugged in, VR is entered or the last input type flips. |
| ProfileSections | { "Identity", "Session", "Client", "Character", "Avatar", "Social" } | Sections :GetProfileAsync assembles when none are asked for. |
| ProfileTimeoutSeconds | 20 | Hard ceiling on one :GetProfileAsync. |
| ProfileCacheSeconds | 60 | Seconds a fully assembled profile is kept; 0 disables it. |
Types
export type Target = Player | number
export type NameStyle = "DisplayName" | "Username" | "Both" | "BothIfDifferent"
export type Membership = "Plus" | "Premium" | "None"
export type Role = "Developer" | "VideoStar"
export type NameTag
export type ClientInfo
export type PlatformInfo
export type DeviceClass
export type InputInfo
export type ScreenInfo
export type PreferencesInfo
export type SessionInfo
export type JoinData
export type PolicyInfo
export type Policy
export type CharacterInfo
export type GameplayInfo
export type Thumbnail
export type AppearanceInfo
export type FriendInfo
export type GroupInfo
export type GroupRole = { Id: number, Name: string, Rank: number }
export type GroupMembership = { GroupId: number, IsMember: boolean, Roles: { GroupRole } }
export type BadgeInfo
export type SubscriptionInfo
export type Profile
export type ProfileConfig