I tried searching the dev forum for how to fix but its all about connections and task.spawn, things im not even using, or using them in ways that could cause memory leaks. I am so stumped I even tried chat gtp and it gave me the same result. The server has like 3000 memory usage when I test the game in studio.
Main module
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Modules = ReplicatedStorage:WaitForChild("Modules")
local PlayerStates = require(Modules:WaitForChild("FighterStates"))
local Objects = workspace:WaitForChild("Game"):WaitForChild("Objects")
local FighterModule = {}
FighterModule.StageLength = 50
function FighterModule.New(Model:Model,Config:{})
local RootPart = Model.PrimaryPart
local LifeTick = 0
Model:SetAttribute("state",PlayerStates.idle)
Model:SetAttribute("state_timer",0)
Model:SetAttribute("max_health",Config.max_health)
Model:SetAttribute("health",Config.max_health)
Model:SetAttribute("dir",1)
Model:SetAttribute("free",true)
Model:SetAttribute("custom_gravity",1)
Model:SetAttribute("custom_friction",1)
Model:SetAttribute("custom_speed",1)
Model:SetAttribute("custom_jumppower",1)
Model:SetAttribute("attack_type",2)
Model:SetAttribute("attack","")
Model:SetAttribute("cancel_type",0)
Model:SetAttribute("dir_point",0)
Model:SetAttribute("djumps",1)
Model:SetAttribute("djump_stored_hspeed",0)
Model:SetAttribute("air_dash_used",false)
Model:SetAttribute("air_dash_back_used",false)
local Inputs = {
Move = Vector2.zero,
A = false,
B = false,
C = false,
D = false,
Evasive = false,
}
local TechBuffer = 0
local InputBuffer = {0,Vector2.zero,0}
local Server = Config.InitializeServer(Model)
local Attacks = Server.Attacks
local function GetState()
return Model:GetAttribute("state"),Model:GetAttribute("state_timer")
end
local function GetFacingDir()
return Model:GetAttribute("dir")
end
local function SetState(state:number)
Model:SetAttribute("state",state)
Model:SetAttribute("state_timer",0)
if state ~= PlayerStates.attack and state ~= PlayerStates.attack_air and state ~= PlayerStates.attack_ground then
Model:SetAttribute("custom_gravity",1)
Model:SetAttribute("custom_friction",1)
Model:SetAttribute("attack_type",2)
end
TechBuffer = 0
end
local oldInputs = Inputs
local function BufferHandler()
if Inputs.A == true and oldInputs.A ~= true then
InputBuffer = {LifeTick,Inputs.Move,1}
elseif Inputs.B == true and oldInputs.B ~= true then
InputBuffer = {LifeTick,Inputs.Move,2}
elseif Inputs.C == true and oldInputs.C ~= true then
InputBuffer = {LifeTick,Inputs.Move,3}
elseif Inputs.D == true and oldInputs.D ~= true then
InputBuffer = {LifeTick,Inputs.Move,4}
elseif Inputs.Evasive == true and oldInputs.Evasive ~= true then
InputBuffer = {LifeTick,Inputs.Move,5}
end
end
local function ClearBuffer()
InputBuffer = {0,Inputs.Move,0}
end
local function DoAttack(attack)
Model:SetAttribute("attack_type",(typeof(attack.type) == "number" and attack.type) or 2)
Model:SetAttribute("attack",attack.Name)
Model:SetAttribute("cancel_type",0)
SetState(PlayerStates.attack)
attack.hit = false
attack.hit_other = false
attack.Use(LifeTick)
end
local function AirTech()
SetState(PlayerStates.air_tech)
RootPart.Velocity = Vector3.new(-Model:GetAttribute("dir"),1.25,0).Unit*Config.speeds.air_tech
end
local function Evasive()
local state,state_timer = GetState()
if state_timer >= 20 and TechBuffer <= 0 and state == PlayerStates.hurt_air then
TechBuffer = 6
elseif Model:GetAttribute("free") and ((state ~= PlayerStates.air_dash and state ~= PlayerStates.air_dash_back) or state_timer >= 15) then
if Model:GetAttribute("air_dash_back_used") ~= true and ((Model:GetAttribute("dir") == 1 and Inputs.Move.x < 0) or (Model:GetAttribute("dir") == -1 and Inputs.Move.x > 0)) then
Model:SetAttribute("air_dash_back_used",true)
SetState(PlayerStates.air_dash_back)
Model.PrimaryPart.Velocity = Vector3.new(Model:GetAttribute("dir")*-Config.speeds.air_dash_hspeed,Config.speeds.air_dash_vspeed,0)
elseif Model:GetAttribute("air_dash_used") ~= true then
Model:SetAttribute("air_dash_used",true)
SetState(PlayerStates.air_dash)
Model.PrimaryPart.Velocity = Vector3.new(Model:GetAttribute("dir")*Config.speeds.air_dash_hspeed,Config.speeds.air_dash_vspeed,0)
end
end
end
local function AirInputHandler(state:number,state_timer:number)
-- action inputs
if Inputs.Move.Y > 0.5 and oldInputs.Move.Y <= 0.5 and Model:GetAttribute("djumps") > 0 then
Model:SetAttribute("djumps",Model:GetAttribute("djumps")-1)
SetState(PlayerStates.djump_start)
Model:SetAttribute("djump_stored_hspeed",RootPart.Velocity.x)
elseif LifeTick - InputBuffer[1] < 6 then
local button = InputBuffer[3]
if Config.AttackHandler then
Config.AttackHandler(button,LifeTick)
else
local attack
if button == 1 then
attack = Attacks.Air_A
elseif button == 2 then
attack = Attacks.Air_B
elseif button == 3 then
attack = Attacks.Air_C
elseif button == 4 then
attack = Attacks.Air_D
elseif button == 5 then
Evasive()
end
if attack and typeof(attack.Use) == "function" and attack.CanBeUsed() then
DoAttack(attack)
end
end
ClearBuffer()
end
end
local function GroundedInputHandler(state:number,state_timer:number)
-- action inputs
if Inputs.Move.Y > 0.5 then -- no buffer required.
SetState(PlayerStates.jumpsquat)
if InputBuffer[3] ~= 0 then
InputBuffer[1] += Config.base_frames.jumpsquat
end
elseif LifeTick - InputBuffer[1] < 6 then
local button = InputBuffer[3]
if Config.AttackHandler then
Config.AttackHandler(button,LifeTick)
else
local attack
if button == 1 then
attack = Attacks.A
elseif button == 2 then
attack = Attacks.B
elseif button == 3 then
attack = Attacks.C
elseif button == 4 then
attack = Attacks.D
elseif button == 5 then
Evasive()
end
if attack and typeof(attack.Use) == "function" and attack.CanBeUsed() then
DoAttack(attack)
end
end
ClearBuffer()
end
-- non-action inputs
if (state == PlayerStates.idle or state == PlayerStates.crouch_start or state == PlayerStates.walk or state == PlayerStates.walk_back) and Inputs.Move.Magnitude > 0.5 then
local dir = GetFacingDir()
local new = state
if Inputs.Move.y < -0.5 and state ~= PlayerStates.crouch and new ~= PlayerStates.crouch_end then
new = PlayerStates.crouch_start
else--if state ~= PlayerStates.crouch and state ~= PlayerStates.crouch_start then
if (Inputs.Move.x > 0 and dir == 1) or (Inputs.Move.x < 0 and dir == -1) then
new = PlayerStates.walk
else
new = PlayerStates.walk_back
end
end
if new ~= state then
SetState(new)
end
elseif (state == PlayerStates.walk or state == PlayerStates.walk_back) and math.abs(Inputs.Move.x) <= 0.5 then
SetState(PlayerStates.idle)
elseif state == PlayerStates.crouch and Inputs.Move.y > -0.5 then
SetState(PlayerStates.crouch_end)
end
end
local function GroundedBehavior()
local begin_state,begin_timer = GetState()
if begin_state == PlayerStates.idle or begin_state == PlayerStates.crouch or begin_state == PlayerStates.crouch_start or begin_state == PlayerStates.crouch_end or begin_state == PlayerStates.crouch then
if begin_state == PlayerStates.crouch_end and begin_timer >= Config.base_frames.crouch_end then
SetState(PlayerStates.idle)
elseif (begin_state == PlayerStates.crouch_start and begin_timer >= Config.base_frames.crouch_start) then
SetState(PlayerStates.crouch)
end
RootPart.Velocity = Vector3.zero
elseif begin_state == PlayerStates.attack or begin_state == PlayerStates.attack_air then
SetState(PlayerStates.attack_ground)
elseif begin_state == PlayerStates.attack_end then
SetState(PlayerStates.idle)
elseif begin_state == PlayerStates.walk then
RootPart.Velocity = Vector3.new(math.sign(Model:GetAttribute("dir"))*Config.speeds.walk*Model:GetAttribute("custom_speed"),0,0)
elseif begin_state == PlayerStates.walk_back then
RootPart.Velocity = Vector3.new(-math.sign(Model:GetAttribute("dir"))*Config.speeds.walk_back*Model:GetAttribute("custom_speed"),0,0)
elseif begin_state == PlayerStates.jumpsquat then
RootPart.Velocity = Vector3.zero
if begin_timer >= Config.base_frames.jumpsquat then
SetState(PlayerStates.jump)
local influence = (Inputs.Move.x > 0.5 and 1) or (Inputs.Move.x < -0.5 and -1) or 0
RootPart.Velocity = Vector3.new(influence*Config.speeds.jump_influence,Config.speeds.jump*Model:GetAttribute("custom_jumppower"),0)
end
elseif begin_state == PlayerStates.hurt_ground then
elseif begin_state == PlayerStates.hurt_air then
if TechBuffer > 0 then
AirTech()
else
SetState(PlayerStates.hurt_land)
end
elseif begin_state == PlayerStates.hurt_land then
if begin_timer >= 40 or Inputs.Move.y > 0.5 then
SetState(PlayerStates.getup)
end
elseif begin_state == PlayerStates.getup then
RootPart.Velocity = Vector3.zero
if begin_timer >= Config.base_frames.getup then
SetState(PlayerStates.idle)
end
elseif begin_state == PlayerStates.land and begin_timer >= Config.base_frames.landing then
SetState(PlayerStates.idle)
elseif begin_state ~= PlayerStates.land and (begin_state ~= PlayerStates.air_tech or begin_timer > 2) and begin_state ~= PlayerStates.attack_ground then -- this line should be at the end. this prevent weird bugging when in an aerial state
RootPart.Velocity = Vector3.zero
SetState(PlayerStates.land)
end
local state,state_timer = GetState()
if state == PlayerStates.idle or state == PlayerStates.walk or state == PlayerStates.walk_back or state == PlayerStates.crouch or state == PlayerStates.crouch_end or state == PlayerStates.crouch_start then
GroundedInputHandler(state,state_timer)
end
end
local function AirborneBehavior()
local begin_state,begin_timer = GetState()
local can_influence = false
if begin_state == PlayerStates.jump then
can_influence = true
if begin_timer >= Config.base_frames.jump_end then
SetState(PlayerStates.fall)
end
elseif begin_state == PlayerStates.hurt_air then
if Inputs.Evasive then
Evasive()
end
elseif begin_state == PlayerStates.grasped then
RootPart.Velocity = Vector3.zero
elseif (begin_state == PlayerStates.air_dash or begin_state == PlayerStates.air_dash_back) then
if begin_timer >= Config.base_frames.air_dash_frames then
SetState(PlayerStates.fall)
end
elseif begin_state == PlayerStates.attack_end then
SetState(PlayerStates.idle)
elseif begin_state == PlayerStates.djump_start then
RootPart.Velocity = Vector3.zero
if begin_timer >= Config.base_frames.djump then
SetState(PlayerStates.djump)
local influence = (Inputs.Move.x > 0.5 and 1) or (Inputs.Move.x < -0.5 and -1) or 0
RootPart.Velocity = Vector3.new((influence*Config.speeds.jump_influence)+Model:GetAttribute("djump_stored_hspeed"),Config.speeds.djump*Model:GetAttribute("custom_jumppower"),0)
-- i forgor
end
elseif begin_state == PlayerStates.djump then
can_influence = true
if begin_timer >= Config.base_frames.djump_end then
SetState(PlayerStates.fall)
end
elseif begin_state == PlayerStates.attack_air then
can_influence = true
elseif begin_state ~= PlayerStates.fall and (begin_state == PlayerStates.air_tech and begin_timer < Config.base_frames.air_tech) == false then -- prevents weird behaviour from happening with grounded states
can_influence = true
SetState(PlayerStates.fall)
end
if can_influence and math.abs(Inputs.Move.x) > 0.25 then
local add = Inputs.Move.x*Config.speeds.air_accel/60
if (add > 0 and RootPart.Velocity.x + add < Config.speeds.air_speed) or (add < 0 and RootPart.Velocity.x + add > -Config.speeds.air_speed) then
RootPart.Velocity = Vector3.new(math.clamp(RootPart.Velocity.x+add,-Config.speeds.air_speed,Config.speeds.air_speed),RootPart.Velocity.y,0)
end
end
local state,state_timer = GetState()
if state == PlayerStates.fall or state == PlayerStates.jump or state == PlayerStates.djump or state == PlayerStates.air_dash or state == PlayerStates.air_dash_back then
AirInputHandler(state,state_timer)
end
end
local function UpdateAttack()
local state = GetState()
if state == PlayerStates.attack or state == PlayerStates.attack_ground or state == PlayerStates.attack_air then
local attack = Attacks[Model:GetAttribute("attack")]
if attack then
attack.Update(LifeTick)
end
end
end
local function Friction(friction:number,value:number)
if value > 0 then
value = math.clamp(value-friction,0,value)
elseif value < 0 then
value = math.clamp(value+friction,value,0)
end
return value
end
local function HandleCancelling()
if Model:GetAttribute("cancel_type") == 1 then
elseif Model:GetAttribute("cancel_type") == 2 then
if Inputs.Move.Y > 0.5 or (LifeTick - InputBuffer[1] < 6 and InputBuffer[2].y > 0.5) then
SetState(PlayerStates.jumpsquat)
end
end
end
local function Update(ip)
Inputs = ip
Server.Update(Inputs,InputBuffer,LifeTick)
BufferHandler()
if Model:GetAttribute("cancel_type") ~= 0 then Model:SetAttribute("cancel_type",0) end
local x = Vector3.new(math.sign(RootPart.Velocity.x)*Config.collision_width,0,0)
local y = (RootPart.Velocity.Y > 0 and Vector3.new(0,Config.hip_height,0)) or Vector3.new(0,-Config.hip_height,0)
if Model:GetAttribute("state") == PlayerStates.grasped then x = Vector3.zero y = Vector3.zero end
RootPart.CFrame += RootPart.Velocity/60
local collisionCheck_X = RootPart.Position + x
local collisionCheck_Y = RootPart.Position + y
local prev_free = Model:GetAttribute("free")
if collisionCheck_Y.y > 0 then
if not prev_free then
Model:SetAttribute("free",true)
Model:SetAttribute("djumps",Config.max_djumps)
Model:SetAttribute("air_dash_used",false)
Model:SetAttribute("air_dash_back_used",false)
end
local gravity = Config.gravity * Model:GetAttribute("custom_gravity") / 60
RootPart.Velocity += Vector3.new(0,gravity,0)
local f = Config.frictions.air_friction
local state,state_timer = GetState()
if state == PlayerStates.hurt_air then
f = Config.frictions.air_stun_friction
elseif state == PlayerStates.air_dash or state == PlayerStates.air_dash_back then
f = Config.frictions.air_dash_friction
end
RootPart.Velocity = Vector3.new(Friction(f*Model:GetAttribute("custom_friction"),RootPart.Velocity.x),RootPart.Velocity.y,0)
AirborneBehavior()
else
if prev_free then
Model:SetAttribute("free",false)
end
RootPart.CFrame = RootPart.CFrame.Rotation + Vector3.new(RootPart.Position.x,Config.hip_height,0)
RootPart.Velocity = Vector3.new(Friction(Config.frictions.ground_state_friction*Model:GetAttribute("custom_friction"),RootPart.Velocity.x),0,0)
GroundedBehavior()
end
UpdateAttack()
HandleCancelling()
if math.abs(collisionCheck_X.x*2) > FighterModule.StageLength then
RootPart.CFrame = RootPart.CFrame.Rotation + Vector3.new(math.clamp(RootPart.Position.x,-FighterModule.StageLength/2+Config.collision_width,FighterModule.StageLength/2-Config.collision_width),RootPart.Position.y,0)
end
local state,timer = GetState()
if state == PlayerStates.idle or state == PlayerStates.walk or state == PlayerStates.walk_back or state == PlayerStates.crouch or state == PlayerStates.fall or state == PlayerStates.jump or state == PlayerStates.crouch_end or state == PlayerStates.jumpsquat then
if RootPart.Position.x < Model:GetAttribute("dir_point") and Model:GetAttribute("dir") ~= 1 then
Model:SetAttribute("dir",1)
elseif RootPart.Position.x > Model:GetAttribute("dir_point") and Model:GetAttribute("dir") ~= -1 then
Model:SetAttribute("dir",-1)
end
end
if TechBuffer > 0 then
TechBuffer -= 1
if TechBuffer <= 0 and state == PlayerStates.hurt_air and state_timer >= 20 then
AirTech()
end
end
-- very end
if Server.PostUpdate then
Server.PostUpdate(Inputs,InputBuffer,LifeTick)
end
LifeTick += 1
oldInputs = Inputs
Model:SetAttribute("state_timer",Model:GetAttribute("state_timer")+1)
end
return Update,Model
end
return FighterModule
the server script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")
local FighterModule = require(ReplicatedStorage:WaitForChild("Modules"):WaitForChild("FighterModule"))
local FighterObjects = {}
function GetPlayerInputs(Player:Player?)
local Inputs = {
Move = Vector2.zero,
A = false,
B = false,
C = false,
D = false,
Evasive = false,
}
if not Player then return Inputs end -- no player
local folder = ServerStorage:FindFirstChild("PlayerInputStorage"):FindFirstChild(Player.Name)
if folder then
Inputs.Move = folder:GetAttribute("Move") or false
Inputs.A = folder:GetAttribute("A") or false
Inputs.B = folder:GetAttribute("B") or false
Inputs.C = folder:GetAttribute("C") or false
Inputs.D = folder:GetAttribute("D") or false
Inputs.Evasive = folder:GetAttribute("Evasive") or false
if Inputs.Move.Magnitude > 1 then -- prevents exploiters from having a really high input for movement
Inputs.Move = Inputs.Move.Unit
end
end
return Inputs
end
function LoadCharactersOnClient()
for i, v in ipairs(FighterObjects) do
ReplicatedStorage.Remotes.ObjectAdded:FireAllClients(v[2],0)
end
end
function AddFighterObject(name:string)
local object = ReplicatedStorage.Assets.Fighters:FindFirstChild(name)
if object then
object = object:Clone()
object:SetPrimaryPartCFrame(CFrame.new(0,10,0)) -- rotation doesn't matter
object.Parent = workspace.Game.Objects
local Config = require(object:WaitForChild("Config"))
local Update = FighterModule.New(object,Config)
table.insert(FighterObjects,{Update,object})
else
warn("could not find anything.")
end
return object
end
ReplicatedStorage.Remotes.InputRemote.OnServerEvent:Connect(function(Player,inputs)
local folder = ServerStorage.PlayerInputStorage:FindFirstChild(Player.Name)
if folder then
folder:SetAttribute("A",inputs.A==true)
folder:SetAttribute("B",inputs.B==true)
folder:SetAttribute("C",inputs.C==true)
folder:SetAttribute("D",inputs.D==true)
folder:SetAttribute("Evasive",inputs.Evasive==true)
folder:SetAttribute("Move",inputs.Move or Vector2.zero)
end
end)
Players.PlayerAdded:Connect(function(Player)
local InputFolder = Instance.new("Folder")
InputFolder.Name = Player.Name
InputFolder:SetAttribute("Move",Vector2.zero)
InputFolder:SetAttribute("A",false)
InputFolder:SetAttribute("B",false)
InputFolder:SetAttribute("C",false)
InputFolder:SetAttribute("D",false)
InputFolder:SetAttribute("Evasive",false)
InputFolder.Parent = ServerStorage:FindFirstChild("PlayerInputStorage")
local object = AddFighterObject("TestGuy")
local owner = Instance.new("ObjectValue")
owner.Name = "Player"
owner.Value = Player
owner.Parent = object
end)
--AddFighterObject("TestGuy")
task.spawn(function()
while task.wait() do
for i, v in ipairs(FighterObjects) do
local update = v[1]
local object = v[2]
if update and object ~= nil and object.Parent ~= nil then
local Player = object:FindFirstChild("Player") and object:FindFirstChild("Player").Value
update(GetPlayerInputs(Player))
else
table.remove(FighterObjects,i)
end
end
end
end)
task.spawn(function()
task.wait(4)
LoadCharactersOnClient()
end)
send hlep.rbxl (107.2 KB)
