TheNexusAvenger / Nexus-VR-Character-Model

Maps Roblox characters to the VR inputs of players.
MIT License
65 stars 12 forks source link

My fix for character appearance problems #34

Closed karl-police closed 6 months ago

karl-police commented 6 months ago

I once fixed an issue, but without modifying the module directly. I don't have VR so I had others test it.

That's why I didn't integrate this into the LocalScript, here's what I did.

Basically when the Character Appearance changes, I am removing the Character from everyone's table, and add it back. This causes NexusVR to just refresh it. Which is a nice cool thing I found out to resolve this issue.

Since NexusVR doesn't refresh replaced body parts, like "Head", "UpperTorso", etc. e.g. when it gets replaced through applying a HumanoidDescription

Server Script

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local NexusVRCharacterModel = ReplicatedStorage:WaitForChild("NexusVRCharacterModel") :: ModuleScript
local RateLimiter = require(NexusVRCharacterModel:WaitForChild("State"):WaitForChild("RateLimiter"))

local StarterPlayer = game:GetService("StarterPlayer")

local clientScriptTemplate = script:WaitForChild("NexusVR_AppearanceFix")

-- Setup the remote
local AppearanceFix_Remote = Instance.new("RemoteEvent")
AppearanceFix_Remote.Name = "NexusVR_Fix_AppearanceChanged"
AppearanceFix_Remote.Parent = ReplicatedStorage

-- Give it to all current Players
for _, ply in ipairs(game.Players:GetPlayers()) do
    task.spawn(function()
        --Create and store a ScreenGui with the script.
        --This prevents the script disappearing on respawn.
        local ScreenGui = Instance.new("ScreenGui")
        ScreenGui.ResetOnSpawn = false
        ScreenGui.Name = "NexusVRCharacterModelClientLoader_Fix"
        clientScriptTemplate:Clone().Parent = ScreenGui

        ScreenGui.Parent = ply:WaitForChild("PlayerGui")
    end)
end
clientScriptTemplate:Clone().Parent = game.StarterPlayer:WaitForChild("StarterPlayerScripts")

--Client should send replication at 30hz.
--A buffer is added in case this rate is exceeded
--briefly, such as an unstable connection.
local REPLICATION_RATE_LIMIT = 35

local UpdateRateLimiter = RateLimiter.new(REPLICATION_RATE_LIMIT)

AppearanceFix_Remote.OnServerEvent:Connect(function(Player)
    --Ignore if the rate limit was reached.
    if UpdateRateLimiter:RateLimitReached(Player) then return end

    -- Replicate to other players
    for _, OtherPlayer in game.Players:GetPlayers() do
        if Player ~= OtherPlayer then
            AppearanceFix_Remote:FireClient(OtherPlayer, Player)
        end
    end
end)

Under the Server script

-- This script should help with NexusVR, whenever the character's appearance has been modified.
-- The issue is only related to player limbs that have been changed.
-- NexusVR doesn't seem to re-connect them.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local VRService = game:GetService("VRService")

local NexusVRCharacterModel = ReplicatedStorage:WaitForChild("NexusVRCharacterModel") :: ModuleScript

local CharacterService = ( require(NexusVRCharacterModel:WaitForChild("State"):WaitForChild("CharacterService")) :: any ).GetInstance()

local AppearanceFix_Remote: RemoteEvent = ReplicatedStorage:WaitForChild("NexusVR_Fix_AppearanceChanged")

local function refreshNexusVR_CharacterParts(Player)
    -- This is to refresh the VR Character...
    CharacterService.Characters[Player].Character = nil
    CharacterService.Characters[Player].VRCharacter = nil
    CharacterService.Characters[Player] = nil
end

AppearanceFix_Remote.OnClientEvent:Connect(function(Player)
    if not (CharacterService.Characters[Player]) then
        return
    end

    --[[
        NexusVR is like client-side based, and data gets replicated to other clients.
        And other clients need this data refreshed
        This makes it so that, that what happened to the VR Player, also happens to other clients
        To refresh the character.
    ]]

    refreshNexusVR_CharacterParts(Player)
end)

-- Don't continue further from here, if no VR.
if not (VRService.VREnabled) then
    return
end

local LocalPlayer = game.Players.LocalPlayer

local appearanceChangedCon = nil :: RBXScriptConnection

LocalPlayer.CharacterAdded:Connect(function(char)
    local hum = char:WaitForChild("Humanoid") :: Humanoid

    if not (hum) then
        warn(`NexusVR_AppearanceFix Humanoid was not found for {char}`)
    end

    -- Reset connection if it already exists
    if (appearanceChangedCon) then
        appearanceChangedCon:Disconnect()
        appearanceChangedCon = nil
    end

    appearanceChangedCon = hum.ChildAdded:Connect(function(child)
        -- If a new HumanoidDescription appeared, then something changed on the avatar.
        -- We should re-ensure that everything is still connected to NexusVR.
        if (child:IsA("HumanoidDescription")) then
            refreshNexusVR_CharacterParts(LocalPlayer)

            -- Fire to server, to fire it to other clients.
            AppearanceFix_Remote:FireServer()
        end
    end)
end)
TheNexusAvenger commented 6 months ago

Resolved with #35. Closing once the next release rolls out.

TheNexusAvenger commented 6 months ago

Changes from #35 have been released with V.2.9.0.