ReplicatedPseudoInstance¶
Summary
A ReplicatedPseudoInstance is an Instance which, when instantiated on the server, has built-in replication. While the parent is set to Workspace
, ReplicatedStorage
, or descendants of these, or set to Players
, it will be Replicated to all players (including those who join the server later). If it is parented to an individual player or a descendant of an individual Player, it will replicate solely to that Player.
If a property changes on the server, clients will be sent the new property's value and update their local version of the instance with the new property.
Events which are fired on the client (by replicating instances originating from the server) are automatically fired on the server via a RemoteEvent.
ReplicatedPseudoInstances which are Destroyed on the server have their destruction replicated.
Inherits from PseudoInstance
Warning
In order for replication to work, the ReplicatedPseudoInstance
library must be loaded on the client.
Warning
All events fired on a client must have LocalPlayer
as the first parameter. On the server, it will use the automatically-passed PlayerFrom parameter (which is built-in to RemoteEvents)
Example
To make a library inherit from ReplicatedPseudoInstance, simply pass it in as the third parameter to PseudoInstance:Register. ChoiceDialog is a good example of a ReplicatedPseudoInstance, but here is a re-implementation of the ClickDetector object (but also passes along what face of an Object you clicked.)
-- ClickDetector Class -- @author Validark local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local RunService = game:GetService("RunService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Resources = require(ReplicatedStorage:WaitForChild("Resources")) local Debug = Resources:LoadLibrary("Debug") local Enumeration = Resources:LoadLibrary("Enumeration") local SortedArray = Resources:LoadLibrary("SortedArray") local PseudoInstance = Resources:LoadLibrary("PseudoInstance") local ReplicatedPseudoInstance = Resources:LoadLibrary("ReplicatedPseudoInstance") local Parts = setmetatable({}, {__mode = "k"}) local LocalPlayer if RunService:IsClient() then repeat LocalPlayer = Players.LocalPlayer until LocalPlayer or not wait() local Mouse = Players.LocalPlayer:GetMouse() local Targets = {} local MouseDowns = {} local CurrentMoval = 0 local MaximumInteger = 2 ^ 53 local function MouseMoved() local Target = Mouse.Target local ActiveIcon while Target do local Detectors = Parts[Target] if Detectors then for i = 1, #Detectors do local Detector = Detectors[i] if Detector.MaxActivationDistance >= LocalPlayer:DistanceFromCharacter(Target.Position) then if not Targets[Detector] then Detector.MouseHoverEnter:Fire(LocalPlayer) end Targets[Detector] = CurrentMoval ActiveIcon = true Mouse.Icon = Detector.CursorIcon end end end Target = Target.Parent end for Detector, Moval in next, Targets do if Moval ~= CurrentMoval then Targets[Detector] = nil MouseDowns[Detector] = nil if not ActiveIcon then Mouse.Icon = "" end Detector.MouseHoverLeave:Fire(LocalPlayer) end end CurrentMoval = (CurrentMoval + 1) % MaximumInteger end Mouse.Move:Connect(MouseMoved) local Connection = Workspace.CurrentCamera:GetPropertyChangedSignal("CFrame"):Connect(MouseMoved) Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function() Connection:Disconnect() Connection = Workspace.CurrentCamera:GetPropertyChangedSignal("CFrame"):Connect(MouseMoved) end) for i = 1, 2 do local Event = i == 1 and "MouseClick" or "RightMouseClick" Mouse["Button" .. i .. "Down"]:Connect(function() local Target = Mouse.Target while Target do local Detectors = Parts[Target] if Detectors then for a = 1, #Detectors do local Detector = Detectors[a] if Detector.MaxActivationDistance >= LocalPlayer:DistanceFromCharacter(Target.Position) then MouseDowns[Detector] = i end end end Target = Target.Parent end end) Mouse["Button" .. i .. "Up"]:Connect(function() for Detector, Down in next, MouseDowns do if Down == i then Detector[Event]:Fire(LocalPlayer, Mouse.TargetSurface) end end end) end end local function CompareClickDetectors(a, b) return a.__id < b.__id end return PseudoInstance:Register("ClickDetector", { Internals = {}; Storage = {}; Properties = { MaxActivationDistance = Enumeration.ValueType.Number; CursorIcon = Enumeration.ValueType.String; Parent = function(self, PVInstance) if PVInstance == nil then return true elseif typeof(PVInstance) == "Instance" and PVInstance:IsA("PVInstance") then self.Janitor:LinkToInstance(PVInstance) if LocalPlayer then local OldParent = self.Parent if OldParent ~= PVInstance then if OldParent then local Data = Parts[PVInstance] if #Data == 1 and Data[1] == self then Parts[PVInstance] = nil else Data:RemoveElement(self) end end local Data = Parts[PVInstance] if Data then Data:Insert(self) else Parts[PVInstance] = SortedArray.new({self}, CompareClickDetectors) end end end return true else Debug.Error("The Parent of a " .. self.ClassName .. " must either be nil or a PVInstance") end end; }; Events = {"MouseClick", "MouseHoverEnter", "MouseHoverLeave", "RightMouseClick"}; -- If these values are indexed from a ClickDetector, it will return a `Signal` Methods = {}; Init = function(self, Id) self.MaxActivationDistance = 32 self.CursorIcon = "rbxassetid://1727841997" -- 1727849582 self:superinit(Id) end; }, ReplicatedPseudoInstance)