I would just have it so is scans all players, not caring about there platform, because, if you do it from the client, is a exploiter unplugs there mouse and keyboard, and has a touch screen, then it would said be a mobile device.
And, I don’t think there is any way to find out a players platform on the server.
I just wrote up a system for this which I believe works fairly well.
--LOCAL
local replicated = game:GetService("ReplicatedStorage")
local remote = replicated:WaitForChild("RemoteEvent")
local userInput = game:GetService("UserInputService")
local hasMouse = userInput.MouseEnabled --Mouse.
local hasKeyboard = userInput.KeyboardEnabled --Keyboard.
local hasTouchscreen = userInput.TouchEnabled --Touchscreen.
local hasGamepad = userInput.GamepadEnabled --Gamepad.
remote:FireServer(hasMouse, hasKeyboard, hasTouchscreen, hasGamepad)
--SERVER
local replicated = game:GetService("ReplicatedStorage")
local remote = replicated.RemoteEvent
local players = game:GetService("Players")
local playerControls = {}
local function onPlayerAdded(player)
task.delay(10, function() --Change 10 if necessary.
if not playerControls[player] then
player:Kick("No data.")
end
end)
end
local function onPlayerRemoving(player)
playerControls[player] = nil
end
local function onRemoteFired(player, mouse, keyboard, touchscreen, gamepad)
if type(mouse) ~= "boolean" or type(keyboard) ~= "boolean" or type(touchscreen) ~= "boolean" or type(gamepad) ~= "boolean" then
player:Kick("Malformed data.")
end
playerControls[player] = {["mouse"] = mouse, ["keyboard"] = keyboard, ["touchscreen"] = touchscreen, ["gamepad"] = gamepad}
print(player.Name, mouse, keyboard, touchscreen, gamepad)
end
players.PlayerAdded:Connect(onPlayerAdded)
players.PlayerRemoving:Connect(onPlayerRemoving)
remote.OnServerEvent:Connect(onRemoteFired)
Sanity checks are performed on the server to ensure data sent from the client is valid, if any data is invalid the player belonging to that client is kicked. Additionally, if no data is received for a player within 10 seconds of that player joining then the player is kicked.
The server script goes inside ServerScriptService, the local script goes inside ReplicatedFirst which prioritises its replication to the client over other instances resulting in its much sooner execution while the game is still loading. These scripts also make use of a single RemoteEvent instance which is placed inside Replicated Storage.
The result of this implementation is a dictionary of dictionaries which can be easily indexed to find controller/device information about any particular client.