I’m thinking I would have to create some sort of loop and a table but I’m not 100% sure.
My situation:
Loading a specific StarterCharacter based on a StringValue.
I have a set of values in StarterPlayerScripts that control a number of configurations. One being that of a string value, the players “ShipType”. The ShipType is their Selected StarterCharacter in the game, their Spaceship.
In Replicated Storage I have a folder full of different ShipTypes each consisting of different properties and configuration values.
To simplify things I’ll use a ShipTemplate to show an example of the hierarchy below.
PlayerValues - Selected ShipType
Replicated Storage - Folder of different Ships
My overall goal is to load a specific Ship based on what the Player’s ShipType is.
How would I go about doing this? It’s easy to do it for just one default ship as I can just clone a specific object, but I’m having difficulty selecting an object based on another. I don’t quite understand loops or tables well enough just yet.
-- Called when the character is added
local function LoadCharacter(player)
player:LoadCharacter()
local character = player.Character
print(character.Name, "has spawned")
for i, v in pairs(character:GetChildren()) do
if v:IsA("BaseScript") then
v:Destroy()
end
end
local rootPart = character:WaitForChild("HumanoidRootPart")
-- Wait until rootPart is part of the workspace
while not rootPart:IsDescendantOf(workspace) do
wait()
end
-- Gives control of the ship to the player
rootPart:SetNetworkOwner(game.Players:GetPlayerFromCharacter(character))
character:WaitForChild("Humanoid").Died:Connect(function()
print(character.Name, "has died")
local Player = game.Players:GetPlayerFromCharacter(character)
LoadCharacter(Player)
end)
end
-- Called when a player is added to the game
local function onPlayerAdded(player)
-- Player Descendants
local PlayerValues = player:WaitForChild("PlayerScripts"):WaitForChild("PlayerValues")
local ShipType = PlayerValues.ShipType.Value
local Ship = game.ReplicatedStorage:WaitForChild("Ships"):FindFirstChild(ShipType)
player.Character = Ship:WaitForChild("StarterCharacter"):Clone()
LoadCharacter(player)
end
-- Connect onPlayerAdded() to the PlayerAdded event.
game.Players.PlayerAdded:Connect(onPlayerAdded)
I tried that out based on that advice but no luck. Here is a small snippet of code in a script that’s in ServerScriptService
local function onPlayerAdded(player)
-- Player Descendants
local PlayerValues = player.PlayerScripts:WaitForChild("PlayerValues")
local ShipType = PlayerValues.ShipType.Value
local Ship = game.ReplicatedStorage:WaitForChild("Ships"):FindFirstChild(ShipType)
player.Character = Ship.StarterCharacter:Clone()
LoadCharacter(player)
end
-- Connect onPlayerAdded() to the PlayerAdded event.
game.Players.PlayerAdded:Connect(onPlayerAdded)
I’ve found that I received this error. I don’t understand though, as the entity, PlayerScripts exists. However, it could be an issue with referencing the player as a whole. (I’ve added my entire player handling script in the original post.)
Makes sense, though, a backpack doesn’t load for my player. Do I need a character for that in the first place? If so I’m not sure if I could use that if I don’t have the character loaded.
Or is there a certain ROBLOX default script for this? I removed all scripts pertaining to the roblox default character.
Localscripts in backpack only run after your character spawns. You can use PlayerScripts or PlayerGui to store the local script to load on start (even without a character.)
StarterCharacter is not necessarily for this purpose. StarterCharacter is intended to define a character model which is used in place of default characters. What you’re seeking to apply is a custom character loader. PlayerScripts is also inaccessible to the server.
Due to that I tried going to remote functions, here is my current code. Server → Client
Server script in Server Script Service.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RespawnFunction = Instance.new("RemoteFunction")
RespawnFunction.Name = "RespawnFunction"
RespawnFunction.Parent = ReplicatedStorage
Players.CharacterAutoLoads = true
local function onPlayerAdded(player)
RespawnFunction:InvokeClient(player)
player:LoadCharacter()
end
Players.PlayerAdded:Connect(onPlayerAdded)
Local Script in Player Scripts
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = Players.LocalPlayer
local RespawnFunction = ReplicatedStorage:WaitForChild("RespawnFunction")
-- Respawn Event Function
local function Respawned()
local Character = player.Character
print(Character.Name, "has spawned")
for i, v in pairs(Character:GetChildren()) do
if v:IsA("BaseScript") then
v:Destroy()
local PlayerValues = player:WaitForChild("BackPack"):WaitForChild("PlayerValues")
local ShipType = PlayerValues.ShipType.Value
local Ship = game.ReplicatedStorage:WaitForChild("Ships"):FindFirstChild(ShipType)
player.Character = Ship:WaitForChild("CustomCharacter"):Clone()
end
end
end
RespawnFunction.OnClientInvoke = Respawned
I’m not sure what’s the issue here, all the syntax and variables seem right.
Once a player is added it should fire the function. However, the player’s character isn’t loading at all.
You are never going to want to allow the client to invoke a remote in this manner. What you’re looking for seems to be a RemoteEvent, not a RemoteFunction. With a RemoteFunction, if the server is invoking the client, the server will expect a return value and the thread will yield until one is received. Exploiters can overwrite the invocation callback for a RemoteFunction and permanently hang the server. This current code will permanently hang as it is, because a return value is not being sent.
Furthermore, for replication and security purposes, you should not be having the client set their own character. The server should be setting the character up, passing ownership and informing the client that their character is ready so that the client may hook up their own functionality as necessary.