Help with fixing mining system

  1. What do you want to achieve? Keep it simple and clear!
    Topic could be a bit complicated, so i’m making a hitbox based mining system which communicated between server/client
  2. What is the issue? Include screenshots / videos if possible!
    exactly when i tested it with 2 players, i figured out what only one player can mine in same time, the other one can’t
    image

image

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    well i have no idea how that happened
    After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

Full code of script:

local ObjectHandler = workspace.Events.ObjectHandler
local PlayerManager = require(script.Parent.PlayerManager)
local TweenService = game:GetService("TweenService")
local radius = 20
local action = ""

...


function Animate(Player)
	local aTable = {
		[1] = 9981038826,
		[2] = 9981055504
	}
	local pChar = Player.Character
	local random = math.random(1,2)
	local CurrentAnimation = Instance.new("Animation")
	CurrentAnimation.AnimationId = "rbxassetid://"..aTable[random]
	local LoadAnim = pChar.Humanoid:LoadAnimation(CurrentAnimation)
	LoadAnim:Play()
end

function Cooldown(player, target)
	local Timer = target.Parent.Rock:GetAttribute("Delay")
	local GameGUI = player.PlayerGui.GameGUI
	GameGUI.Reload.Visible = true
	while Timer > 0 do
		GameGUI.Reload.Bar.ProgressBar:TweenSize(UDim2.new(Timer/target.Parent.Rock:GetAttribute("Delay"),0,1,0), Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.1, true)
		Timer -= 0.1
		task.wait(0.1)
	end
	if Timer <= 0 then
		GameGUI.Reload.Visible = false
	end
end



local function Mine(player, target)
	if target and target.Parent and target.Parent.Rock then
	if target.Parent.Rock:GetAttribute("isRegenerating") == false then
		local Tool = player.Character.WoodPick
	player.Character.Humanoid.WalkSpeed = 6
	ObjectHandler:FireClient(player, target, "Change")
		local DPC = Tool:GetAttribute("DPC")
		local Value = target.Parent.Rock:GetAttribute("Value")
		target.Parent.Rock:SetAttribute("Owner", player.Name)
		while target.Parent.Rock:GetAttribute("CurrentHealth") > 0 and Tool:GetAttribute("inUse") ~= true and action == "Start" do
				Tool:SetAttribute("inUse", true)
			Animate(player)
			local distance = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude	
			
			if distance > radius then
				action = "Override"
				if target then
					ObjectHandler:FireClient(player, target, "Disable")
						target.Parent.Rock:SetAttribute("Owner", "Empty")
						Tool:SetAttribute("inUse", false)
					player.Character.Humanoid.WalkSpeed = 16
				end
			end
			
			local calculate = target.Parent.Rock:GetAttribute("CurrentHealth") - DPC
			target.Parent.Rock:SetAttribute("CurrentHealth", calculate)
			if target.Parent.Rock:GetAttribute("CurrentHealth") ~= 0 then
				Cooldown(player, target)
			end
		end
		if target.Parent.Rock:GetAttribute("CurrentHealth") == 0 then
			player.Character.Humanoid.WalkSpeed = 16
			ObjectHandler:FireClient(player, target, "Disable")
			Tool:SetAttribute("inUse", false)
			PlayerManager.SetCoal(player, PlayerManager.GetCoal(player) + Value)
			target.Parent.Rock:SetAttribute("isRegenerating", true)
			Regenerate(target)
		end
		end
		end
end

function Regenerate(target)
	local info = TweenInfo.new(
		2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut, 0
	)
	local tween = TweenService:Create(target.Parent.Rock, info, {Size = Vector3.new(0.002,0.001,0.002)})
	local adTween = TweenService:Create(target.Parent.Union, info, {Size = Vector3.new(0.002,0.001,0.002)})
	tween:Play()
	adTween:Play()
	tween.Completed:Connect(function()
		target.Parent.Rock.Transparency = 1
		target.Parent.Rock.CanCollide = false
		target.Parent.Union.Transparency = 1
		target.Parent.Union.CanCollide = false
		local regenTimer = math.random(5, 6)
		Timer(regenTimer, target)
		target.Parent.Rock.Transparency = 0
		target.Parent.Rock.CanCollide = true
		target.Parent.Union.Transparency = 0
		target.Parent.Union.CanCollide = true
		local reverseTween = TweenService:Create(target.Parent.Rock, info, {Size = Vector3.new(6.3, 3.15, 6.3)})
		local adReverseTween = TweenService:Create(target.Parent.Union, info, {Size = Vector3.new(3.5, 6.5, 6.4)})
		reverseTween:Play()
		adReverseTween:Play()
		reverseTween.Completed:Connect(function()
			target.Parent.Rock:SetAttribute("isRegenerating", false)
			target.Parent.Rock:SetAttribute("Owner", "Empty")
		end)
	end)
end

function Timer(timer, target)
	while timer > 0 do
		timer -= 0.1
		target.Parent.Rock.Settings.Regenerate.Visible = true
		target.Parent.Rock.Settings.Default.Visible = false
		if timer > 0 then
			target.Parent.Rock.Settings.Regenerate.MainFrame:WaitForChild("RegLabel").Text = Suffix(timer, 2).."s" -- 1.05
		end
		task.wait(0.1)
	end
	if timer <= 0 then
		target.Parent.Rock.Settings.Regenerate.Visible = false
		target.Parent.Rock.Settings.Default.Visible = true
		target.Parent.Rock:SetAttribute("CurrentHealth", target.Parent.Rock:GetAttribute("MaxHealth"))
	end
end

ObjectHandler.OnServerEvent:Connect(function(player, target, Action)
	if Action == "Start" then
		action = "Start"
		Mine(player, target)
	elseif Action == "Override" then
		action = "Override"
		if target then
			player.Character.Humanoid.WalkSpeed = 16
			if player.Character:FindFirstChild("WoodPick") then
				player.Character:FindFirstChild("WoodPick"):SetAttribute("inUse", false)	
			elseif player.Backpack:FindFirstChild("WoodPick") then
				player.Backpack:FindFirstChild("WoodPick"):SetAttribute("inUse", false)
			end
			target.Parent.Rock:SetAttribute("Owner", "Empty")
		end
	end
end)

Bumping that topic (30 lettersssssssssss)

Have you tried putting those while loops in a coroutine?

Wrap each function call that contains a while loop inside of a coroutine:

local mineThread = coroutine.create(function()
	Mine(player, target)
end)

coroutine.resume(mineThread)

Let me know if you need any more help!

1 Like

I don’t really get how i should use coroutine here, plus i don’t really wanna use hard loops like coroutine. Plus i’m not really acquainted with coroutine. I even don’t have an idea how to transfer args to Mine() via coroutine.resume.

local mineThread = coroutine.create(function(player, target)
	Mine(player, target)
end)

ObjectHandler.OnServerEvent:Connect(function(player, target, Action)
	if Action == "Start" then
		action = "Start"
		coroutine.resume(mineThread {player, target})
	elseif Action == "Override" then
		action = "Override"
		if target then
		player.Character.Humanoid.WalkSpeed = 16
		target.Parent.Rock:SetAttribute("Owner", "Empty")
		end
	end
end)

like i think this is wrong, isn’t it?

EDIT: Also i think the problem is in some server delays/debounces, though i can’t really find them

EDIT: Oh well, i got it right with coroutine.resume, though now mining happens only once. Like it mines 1 time and then just stop(it should run with while a lot times until ore is broken)

This would be correct:

ObjectHandler.OnServerEvent:Connect(function(player, target, Action)
	if Action == "Start" then
		action = "Start"
		local mineThread = coroutine.create(function()
			Mine(player, target)
		end)

		coroutine.resume(mineThread)
	elseif Action == "Override" then
		action = "Override"
		if target then
			player.Character.Humanoid.WalkSpeed = 16
			if player.Character:FindFirstChild("WoodPick") then
				player.Character:FindFirstChild("WoodPick"):SetAttribute("inUse", false)	
			elseif player.Backpack:FindFirstChild("WoodPick") then
				player.Backpack:FindFirstChild("WoodPick"):SetAttribute("inUse", false)
			end
			target.Parent.Rock:SetAttribute("Owner", "Empty")
		end
	end
end)

Do this when you call your cooldown function as well. I highly recommend learning how to use coroutines, and reading up the documentation I provided in my previous comment. Good luck!

Yeah as i edited last post, it works one time, but then just stops. Here is video, i can’t really explain it normally

Then I might’ve been wrong in thinking you could wrap function calls in coroutines. I would try wrapping the while loop in the actual function in a coroutine like this:

function Cooldown(player, target)
	local Timer = target.Parent.Rock:GetAttribute("Delay")
	local GameGUI = player.PlayerGui.GameGUI
	GameGUI.Reload.Visible = true

	local cooldownThread = coroutine.create(function()
		while Timer > 0 do
			GameGUI.Reload.Bar.ProgressBar:TweenSize(UDim2.new(Timer/target.Parent.Rock:GetAttribute("Delay"),0,1,0), Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.1, true)
			Timer -= 0.1
			task.wait(0.1)
		end
	end)

	coroutine.resume(cooldownThread)

	if Timer <= 0 then
		GameGUI.Reload.Visible = false
	end
end

Wait… That’s really strange, but my code suddenly stopped working as should. Like it defenitely clicks one time and stops without coroutine. I just got old code before adding coroutine and it now gives same result as coroutine one, weird

Just to clarify, your issue was that 2 people couldn’t mine different ores at once, correct?

Correct, but now without any reason my code for mining broke, i didn’t do anything, everything was same as a hour ago, buttttt now it is broken
Edit: oh well i fixed it somehow, let’s get back to topic, let me try coroutines rq


It just stucks if wrapping the while loop

About wrapping mine function it just stops after 1 click

Unless I’m wrong about the coroutines (hopefully someone else can join the conversation and tell me), I think they’re required so that you don’t have your previous issue. It looks like now you just need to debug your code, since as you said before, it randomly broke. I can’t really help you with that, but if you get stuck you can point me to where you need help and I can do my best.

Also make sure that there are no other loops that you’re forgetting about. Also maybe try putting the entire code of the function in a coroutine?

No code don’t randomly broke, i just added an if statement which broke while loop exactly, you mean put function cooldown in coroutine, if yes then i tryed it just stops after 1 click

I was talking about this, is that not what you meant?

No, that was a bit newer code, here is old one:

local ObjectHandler = workspace.Events.ObjectHandler
local PlayerManager = require(script.Parent.PlayerManager)
local TweenService = game:GetService("TweenService")
local Debounce = false
local radius = 20
local action = ""


local function Suffix(Number, Precision)
	local Suffixes = {
		"", " K"," M", " B", " T",
		" Qd", " Qn", " Sx", " Sp",
		" Oc", " No", " De", " Un",
		" Tr", " Qud", " Qun", " Sxd",
		" Spd", " Ocd", " Nod", " Vg",
		" UVg"," DVg"," TVg"," QdVg",
		" QnVg"," SxVg"," SpVg"," OcVg",
		" NoVg"," Tg"," UTg"," DTg"," TTg",
		" QdTg"," QnTg"," SxTg"," SpTg"," OcTg",
		" NoTg"," qg"," Uqg"," Dqg"," Tqg"," Qdqg",
		" Qnqg"," Sxqg"," Spqg"," Ocqg"," Noqg",
		" Qg"," UQg"," DQg"," TQg"," QdQg"," QnQg",
		" SxQg"," SpQg"," OcQg"," NoQg"," sg"," Usg",
		" Dsg"," Tsg"," Qdsg"," Qnsg"," Sxsg"," Spsg",
		" Ocsg","Nosg"," Sg"," USg"," DSg"," TSg",
		" QdSg"," QnSg"," SxSg"," SpSg"," OcSg"," NoSg",
		" Og"," UOg"," DOg"," TOg"," QdOg"," QnOg"," SxOg",
		" SpOg"," OcOg"," NoOg"," Ng"," UNg"," DNg"," TNg",
		" QdNg"," QnNg"," SxNg"," SpNg"," OcNg"," NoNg"
	}

	local Index = math.floor(math.log10(Number) / 3)
	if Index < 0 then
		Index = 0
	end
	local Suffix = Suffixes[Index + 1]

	Number = Number / (10 ^ (3 * Index))
	Number = tostring(math.floor(Number * 10 ^ Precision) / 10 ^ Precision)

	return Number .. Suffix
end


game:GetService("Players").PlayerAdded:Connect(function(plr)
	PlayerManager.Start()
end)

function Animate(Player)
	local aTable = {
		[1] = 9981038826,
		[2] = 9981055504
	}
	local pChar = Player.Character
	local random = math.random(1,2)
	local CurrentAnimation = Instance.new("Animation")
	CurrentAnimation.AnimationId = "rbxassetid://"..aTable[random]
	local LoadAnim = pChar.Humanoid:LoadAnimation(CurrentAnimation)
	LoadAnim:Play()
end

function Cooldown(player, target)
	local Timer = target.Parent.Rock:GetAttribute("Delay")
	local GameGUI = player.PlayerGui.GameGUI
	GameGUI.Reload.Visible = true

	local cooldownThread = coroutine.create(function()
		while Timer > 0 do
			GameGUI.Reload.Bar.ProgressBar:TweenSize(UDim2.new(Timer/target.Parent.Rock:GetAttribute("Delay"),0,1,0), Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.1, true)
			Timer -= 0.1
			task.wait(0.1)
		end
	end)

	coroutine.resume(cooldownThread)

	if Timer <= 0 then
		GameGUI.Reload.Visible = false
	end
end


local function Mine(player, target)
	if target and target.Parent and target.Parent.Rock then
		if target.Parent.Rock:GetAttribute("isRegenerating") == false then
			local Tool = player.Character.WoodPick
			Tool:SetAttribute("inUse", true)
			player.Character.Humanoid.WalkSpeed = 6
			ObjectHandler:FireClient(player, target, "Change")
			local DPC = Tool:GetAttribute("DPC")
			local Value = target.Parent.Rock:GetAttribute("Value")
			target.Parent.Rock:SetAttribute("Owner", player.Name)
			while target.Parent.Rock:GetAttribute("CurrentHealth") > 0 and Debounce == false  and action == "Start" do -- here is checked if debounce false
				Animate(player)
				local distance = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude	

				if distance > radius then
					action = "Override"
					if target then
						ObjectHandler:FireClient(player, target, "Disable")
						target.Parent.Rock:SetAttribute("Owner", "Empty")
						player.Character.Humanoid.WalkSpeed = 16
					end
				end

				local calculate = target.Parent.Rock:GetAttribute("CurrentHealth") - DPC
				target.Parent.Rock:SetAttribute("CurrentHealth", calculate)
				Debounce = true
				if target.Parent.Rock:GetAttribute("CurrentHealth") ~= 0 then
					Cooldown(player, target)
				end
			end
			if target.Parent.Rock:GetAttribute("CurrentHealth") == 0 then
				player.Character.Humanoid.WalkSpeed = 16
				Debounce = false
				ObjectHandler:FireClient(player, target, "Disable")
				Tool:SetAttribute("inUse", false)
				PlayerManager.SetCoal(player, PlayerManager.GetCoal(player) + Value)
				target.Parent.Rock:SetAttribute("isRegenerating", true)
				Regenerate(target)
			end
		end
	end
end

function Regenerate(target)
	local info = TweenInfo.new(
		2, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut, 0
	)
	local tween = TweenService:Create(target.Parent.Rock, info, {Size = Vector3.new(0.002,0.001,0.002)})
	local adTween = TweenService:Create(target.Parent.Union, info, {Size = Vector3.new(0.002,0.001,0.002)})
	tween:Play()
	adTween:Play()
	tween.Completed:Connect(function()
		target.Parent.Rock.Transparency = 1
		target.Parent.Rock.CanCollide = false
		target.Parent.Union.Transparency = 1
		target.Parent.Union.CanCollide = false
		local regenTimer = math.random(5, 6)
		Timer(regenTimer, target)
		target.Parent.Rock.Transparency = 0
		target.Parent.Rock.CanCollide = true
		target.Parent.Union.Transparency = 0
		target.Parent.Union.CanCollide = true
		local reverseTween = TweenService:Create(target.Parent.Rock, info, {Size = Vector3.new(6.3, 3.15, 6.3)})
		local adReverseTween = TweenService:Create(target.Parent.Union, info, {Size = Vector3.new(3.5, 6.5, 6.4)})
		reverseTween:Play()
		adReverseTween:Play()
		reverseTween.Completed:Connect(function()
			target.Parent.Rock:SetAttribute("isRegenerating", false)
			target.Parent.Rock:SetAttribute("Owner", "Empty")
		end)
	end)
end

function Timer(timer, target)
	while timer > 0 do
		timer -= 0.1
		target.Parent.Rock.Settings.Regenerate.Visible = true
		target.Parent.Rock.Settings.Default.Visible = false
		if timer > 0 then
			target.Parent.Rock.Settings.Regenerate.MainFrame:WaitForChild("RegLabel").Text = Suffix(timer, 2).."s" -- 1.05
		end
		task.wait(0.1)
	end
	if timer <= 0 then
		target.Parent.Rock.Settings.Regenerate.Visible = false
		target.Parent.Rock.Settings.Default.Visible = true
		target.Parent.Rock:SetAttribute("CurrentHealth", target.Parent.Rock:GetAttribute("MaxHealth"))
	end
end

ObjectHandler.OnServerEvent:Connect(function(player, target, Action)
	if Action == "Start" then
		action = "Start"
		Mine(player, target)
	elseif Action == "Override" then
		action = "Override"
		if target then
			player.Character.Humanoid.WalkSpeed = 16
			if player.Character:FindFirstChild("WoodPick") then
				player.Character:FindFirstChild("WoodPick"):SetAttribute("inUse", false)	
			elseif player.Backpack:FindFirstChild("WoodPick") then
				player.Backpack:FindFirstChild("WoodPick"):SetAttribute("inUse", false)
			end
			target.Parent.Rock:SetAttribute("Owner", "Empty")
		end
	end
end)

Thank you for clarifying. It looks like in that code you provided, you forgot to wrap the timer loop in a coroutine. Does your current code do this? Edit: Your mining function also doesn’t contain the coroutine

No its not same here is the change
image

in topic code
image

No, i didn’t wrap cooldown into corotine because it just makes mining one click

Have you tried wrapping the Timer function? I understand that the mining function might break because of it.

Timer function is being called when time for regenerating the ore, timer function called from Regenerate one

Can you try doing this?

function Timer(timer, target)
	local timerThread = coroutine.create(function()
		while timer > 0 do
			timer -= 0.1
			target.Parent.Rock.Settings.Regenerate.Visible = true
			target.Parent.Rock.Settings.Default.Visible = false
			if timer > 0 then
				target.Parent.Rock.Settings.Regenerate.MainFrame:WaitForChild("RegLabel").Text = Suffix(timer, 2).."s" -- 1.05
			end
			task.wait(0.1)
		end
	end)

	coroutine.resume(timerThread)

	if timer <= 0 then
		target.Parent.Rock.Settings.Regenerate.Visible = false
		target.Parent.Rock.Settings.Default.Visible = true
		target.Parent.Rock:SetAttribute("CurrentHealth", target.Parent.Rock:GetAttribute("MaxHealth"))
	end
end

Or have you already done this and it broke your system? Edit: It probably won’t fix anything, you’re right. But it’s worth a shot.