Anyway to make this script better?

I made a working door script, but I feel like it can be more optimized, organized, and all around better.

local RunService = game:GetService("RunService") ;
local CollectionService = game:GetService("CollectionService") ;
local TweenService = game:GetService("TweenService") ;

local PromptUsed = script.Remotes.PromptUsed

local function TweenDoor(Side, Tweeninfo, Cframe)
	assert(Side:IsA("Model"), string.format("Side: %s is not a model.", Side.Name))
	assert(Cframe ~= nil, "Cframe: CFrame has no value.")
	
	TweenService:Create( 
		Side.PrimaryPart, 
		Tweeninfo, 
		{CFrame = Side.Primary.CFrame * Cframe}
	):Play() ;
end ;

local function Weld(Side: Model)
	for _, Parts in pairs(Side:GetChildren()) do 
		if Parts:IsA("BasePart") and (Parts ~= Side.PrimaryPart) then 
			local Weld = Instance.new("WeldConstraint") ;
			Weld.Part0 = Parts ;
			Weld.Part1 = Side.PrimaryPart ;
			Weld.Parent = Parts ;
		end ;
	end ;
end ;

local function CheckRank(Player, GroupID, Prompt)
	assert(Player ~= nil, string.format("Player: %s is not a player.", Player.Name))
	assert(type(GroupID) == "number", "GroupID: GroupID is not a number.")
	
	local DoorID = Prompt.Parent:GetAttribute("Rank") ;

	if Player:GetRankInGroup(GroupID) >= DoorID then 
		return true ;
	else
		return false ;
	end ;
end ;

local Door = {} ;
Door.__index = Door ;

function Door.Init()
	local self = setmetatable({}, Door) ;
	
	self.TweenInfo = TweenInfo.new(
		1, 
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.Out
	) ;
	
	self.GroupID = 12903725 ;
	
	if RunService:IsClient() then
		self.Player = game.Players.LocalPlayer ;
		
		self:PromptFired() ;
	else
		self:WeldPrimary() ;
		
		PromptUsed.OnServerEvent:Connect(function(Player, Type)
			self:Open(Type) ; 
		end)
	end ;
	
	return self ;
end ;

function Door:WeldPrimary()
	for _, Prompt in pairs(CollectionService:GetTagged("DoorDetection")) do 
		local Type = Prompt.Parent ;
		
		if Type.Name == "Door" then 
			
			Weld(Type.SideL) ;
		end
		
		
		if Type.Name == "DoubleDoor" then 
			Weld(Type.SideL) ;
			
			Weld(Type.SideR) ;
		end ;
	end ;
end ;

function Door:PromptFired()
	for _, Prompt in pairs(CollectionService:GetTagged("DoorDetection")) do 
		Prompt.ProximityPrompt.Triggered:Connect(function()
			local Rank = CheckRank(self.Player, self.GroupID, Prompt) ;
			
			if Rank then 
				PromptUsed:FireServer(Prompt.Parent)
			else
				game:GetService("StarterGui"):SetCore("SendNotification", {
					Title = "Warning", 
					Text = "Not ranked high enough", 
					Icon = "rbxassetid://8877073449",
					Duration = 2
				}) ;
			end ;
		end) ;
	end ;
end ;

function Door:Open(Type)
	assert(Type:IsA("Model"), string.format("Side: %s is not a model.", Type.Name))
	
	local CurrentState = Type:GetAttribute("Opened") ;

	if Type.Name == "Door" and not CurrentState then 
		Type:SetAttribute("Opened", true) ;
			
		TweenDoor(Type.SideL, self.TweenInfo, CFrame.new(-3.5, 0, 0)) ;

	elseif Type.Name == "Door" and CurrentState then
		Type:SetAttribute("Opened", false) ;
			
		TweenDoor(Type.SideL, self.TweenInfo, CFrame.new(3.5, 0, 0)) ;
	end ;

	if Type.Name == "DoubleDoor" and not CurrentState then 
		Type:SetAttribute("Opened", true) ;
			
		TweenDoor(Type.SideL, self.TweenInfo, CFrame.new(-3.5, 0, 0)) ;

		TweenDoor(Type.SideR, self.TweenInfo, CFrame.new(3.5, 0, 0)) ;
			
	elseif Type.Name == "DoubleDoor" and CurrentState then
		Type:SetAttribute("Opened", false) ;
			
		TweenDoor(Type.SideL, self.TweenInfo, CFrame.new(3.5, 0, 0)) ;

		TweenDoor(Type.SideR, self.TweenInfo, CFrame.new(-3.5, 0, 0)) ;

	end ;
end ;

return Door ;

Well, that’s a very basic code, you’ll probably figure out how to improve it as your game evolves. However, here are a few things you can change already:

  • Check the Player’s rank on the server too, otherwise exploiters will be able to open your doors unauthorizedly
  • Some properties of your class like GroupID, Player and TweenInfo shouldn’t be properties as they are static, they should be top-level constants
  • Putting some parts of the object-oriented code outside of the methods doesn’t really make sense in this context

Alright, I will change those things, thank you!