Skip to content
Engine

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.

ServiceShared

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 PolicyService rules (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

EventFires 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

MethodDescription
: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 marks

Badge 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.

MethodDescription
: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

MethodDescription
: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

MethodDescription
: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

MethodDescription
: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

MethodDescription
: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

PropertyDescription
LocalPlayerRead-only. Players.LocalPlayer on the client.
NameStyleDefault style for names.
HideSubscriptionWhenVerifiedShow only the verified mark on verified subscribers.
TelemetryEnabledWhether clients send their info at all.
CacheSecondsDefault 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

Defaults from DefaultConfiguration/Services/PlayerService46 settings
SettingDefaultWhat it does
CacheSeconds300Default TTL of a resolved web lookup, used by anything without its own below.
NegativeCacheSeconds20Seconds a failed lookup is remembered before it is asked again.
IdentityCacheSeconds1800TTL of name/userId lookups.
OwnershipCacheSeconds30TTL of gamepass/badge/asset ownership lookups.
SocialCacheSeconds120TTL of friend and group membership lookups.
RetryAttempts3Retries of a failed web call.
RetryBaseSeconds0.5First retry delay; doubles each attempt up to RetryMaxSeconds.
RetryMaxSeconds8Cap on the retry delay.
RetryJitter0.25Fraction of each retry delay randomised; 0 disables it.
RequestTimeoutSeconds15Seconds a single web call may take before it counts as failed.
MaxFriendsFetched200Ceiling on friends walked through FriendPages.
MaxGroupsFetched100Ceiling on groups walked through GroupService.
WarnOnLookupFailuretrueWarn when a web lookup fails for good.
PrefetchPolicytrueLook 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.
DefaultThumbnailSize420Enum.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.
HideSubscriptionWhenVerifiedfalseDraw 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.
ProbeGlyphstrueProbe every Glyph through TextService at boot and fall back when the font lacks it.
DeveloperMinGroupRank255Rank in the owning group at or above which a player counts as a developer (255 is the owner).
VideoStarGroupId4199740Roblox's own Video Stars group; membership is the video-star role.
TelemetryEnabledtrueWhether clients publish their device/settings/screen info to the server at all.
TelemetryJoinDelaySeconds1Seconds a client waits after joining before its first publish.
TelemetryRefreshSeconds20Seconds between a client re-measuring the fields that move and republishing if any changed.
TelemetryHeartbeatSeconds60Seconds between unconditional republishes, so fps/memory/ping are never older than this.
FpsSampleSeconds2Seconds of frames the fps figure is averaged over.
TelemetryStaleSeconds120Client info older than this is reported Stale by :GetClientInfo.
TelemetryRequestTimeoutSeconds10Seconds :RefreshClientInfoAsync waits on a client before rejecting.
TelemetryRatePerSec2Per-player rate limit on the client-to-server publish.
TelemetryBurst6Burst the publish rate limiter allows.
CollectPerformancetrueInclude a PerformanceService sample (fps, frame time, memory, ping) in every publish.
CollectPreferencestrueInclude the player's graphics/audio/camera/movement settings in every publish.
AppleGlyphCodepoint0xF8FFApple's logo glyph in the system font; separates iOS from Android.
InvalidGlyphCodepoint0xFFFFCodepoint guaranteed to have no glyph anywhere.
TabletMinViewportDiagonal1200Viewport diagonal at or above which a touch device counts as a tablet rather than a phone.
PhoneMinAspectRatio1.85Viewport 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.
RedetectOnInputChangetrueRe-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.
ProfileTimeoutSeconds20Hard ceiling on one :GetProfileAsync.
ProfileCacheSeconds60Seconds 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