Remote Event Scenario... please helppppp

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    Eliminate using a ton of remote events.

  2. What is the issue? Include screenshots / videos if possible!
    If this scenario gets resolved, I should be able to figure out the rest…

Building an RPG game.
All stats are stored in server storage for protection from exploiters.
on a shop Gui, I have 3 buttons: Buy 1, Buy 10, Buy 100

Buy 1 button: buys a single item.
Buy 10 button: buys 10 of an item.
Buy 100 button: buys 100 of an item.

Currently I have a remote setup, an it does not work for obvious reasons in the code below. I have no clue how to use one remote for this setup. I need some inspiration or ideas AND HOW TO DO THIS THE RIGHT WAY :slight_smile:

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    Looked around, but could not find what I was looking for other than having a ton of remotes is bad.

I have stats such as player gold, level, exp, strength, stamina, agility, ability power, etc.
I have resources such as various mining ores, various wood types, etc.

SHOP HANDLER SERVER SCRIPT:

serverStorage = game:GetService("ServerStorage")
replicatedStorage = game:GetService("ReplicatedStorage")
Players = game:GetService("Players")


RemoteStats = replicatedStorage:WaitForChild("RemoteStats")
OreEvent = RemoteStats:FindFirstChild("OreEvent")
goldCoinsEvent = RemoteStats:FindFirstChild("GoldCoins")




local function buyStone(player,count)
	if goldCoins.Value >= 100 then
		stone.Value = stone.Value + count
		goldCoins.Value = goldCoins.Value - 100
		goldCoinsEvent:FireClient(player,goldCoins.Value)
		OreEvent:FireClient(player, stone.Value)
	end
end

local function buyTenStone(player, count)
	if goldCoins.Value >= 1000 then
		stone.Value = stone.Value + count
		goldCoins.Value = goldCoins.Value - 1000
		goldCoinsEvent:FireClient(player,goldCoins.Value)
		OreEvent:FireClient(player, stone.Value)
	end
end


Players.PlayerAdded:Connect(function(player)
	Folder = serverStorage:WaitForChild(player.Name)
	stone = Folder:FindFirstChild("Stone")
	goldCoins = Folder:FindFirstChild("Gold")
	
	
	wait(.1)
	
	OreEvent:FireClient(player, stone.Value)
	
	
end)


OreEvent.OnServerEvent:Connect(buyStone)
OreEvent.OnServerEvent:Connect(buyTenStone)

BUY 1 BUTTON LOCAL SCRIPT:


local player = game.Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local shopGui = playerGui:WaitForChild("Shop1GUI")
local itemName = shopGui.InfoTab:WaitForChild("ItemName")

local stone = game.ReplicatedStorage.Items.Stone:Clone()


local replicatedStorage = game:GetService("ReplicatedStorage")
local RemoteStats = replicatedStorage:WaitForChild("RemoteStats")
local stoneEvent = RemoteStats:FindFirstChild("OreEvent")


local button = script.Parent



button.MouseButton1Click:Connect(function()
	if itemName.Text == "Stone" then
		local count = 1
		stoneEvent:FireServer(count)
	end
end)

You could do actually do this with a single RemoteEvent. For other things that require near instantaneous sending of the RemoteEvent, this may not work, since it may cause some delay depending on the number of players currently firing it. But for buying things in a store, this should work just fine. To do this, you can send a string through the parameters, that we’ll call message, and check that on the server side each time.

In Localscript:

-- Variables
local buyRemote = -- Path to remote event
local buy1Button = -- Path to button to buy 1 stone
local buy10Button = -- Path to button to buy 10 stone
local buy100Button = -- Path to button to buy 100 stone

buy1Button.MouseButton1Click:Connect(function()
    buyRemote:FireServer("1")
end)

buy10Button.MouseButton1Click:Connect(function()
    buyRemote:FireServer("10")
end)

buy100Button.MouseButton1Click:Connect(function()
    buyRemote:FireServer("100")
end)

In Script:

-- Variables
local buyRemote = -- Path to remote event

local function buy1Stone(player)
    local playerGold = -- Player's gold amount; perhaps in leaderstats

    if playerGold.Value >= 100 then
        playerGold.Value -= 100
        -- Give player 1 stone
    end
end

local function buy10Stone(player)
    local playerGold = -- Player's gold amount; perhaps in leaderstats
    if playerGold.Value >= 1000 then
        playerGold.Value -= 1000
        -- Give player 10 stone
    end
end

local function buy100Stone(player)
    local playerGold = -- Player's gold amount; perhaps in leaderstats
    if playerGold.Value >= 10000 then
        playerGold.Value -= 10000
        -- Give player 100 stone
    end
end

buyRemote.OnServerEvent:Connect(function(player, message)
    if message == "1" then
        buy1Stone(player)
    elseif message == "10" then
        buy10Stone(player)
    elseif message == "100" then
        buy100Stone(player)
    end
end)

Hope this helps! And let me know if you need anything to be explained further.

EDIT: I just got through thinking, and realized there’s an even better way to do this, that’ll work much better for situations where you don’t just have a few fixed values (10 stone, 100, 1000, etc.) and in this scenario as well. Here’s the updated method.

In LocalScript:

-- Variables
local buyRemote = -- Path to remote event
local buyButton = -- Path to the button to buy stones
local stoneAmount = -- However many stones the player wants to buy

buyButton.MouseButton1Click:Connect(function()
    stoneAmount = -- Whatever the stone amount is; you can choose to have a text input on screen, or whatever
    buyRemote:FireServer(stoneAmount)
end)

In Script:

-- Variables
local buyRemote = -- Path to remote event
local pricePerStone = 100 -- However much a stone costs

local function buy1Stone(player)
    local playerGold = -- Player's gold amount; perhaps in leaderstats

    if playerGold.Value >= 100 then
        playerGold.Value -= 100
        -- Give player 1 stone
    end
end

local function buyStone(player, amount)
	local playerGold = -- Player's gold value holder; perhaps in leaderstats
	
	if playerGold.Value >= amount * pricePerStone then
		playerGold.Value -= amount * pricePerStone
	end
end

buiRemove.OnServerEvent:Connect(buyStone)

This new method will work for any (though there’s integer 64-bit precision limits) amount you throw at it. The difference is, you will need to setup a variable with the price of a single stone in this one, which I arbitrarily set to 100, and then receive the amount of stones the player would like to buy. You can customize the LocalScript to fire whatever you’d like to the server (using the same parameter values) and it’ll work. However, the above code is mainly for custom inputted values from the user, like if you had a text box where the player can input how much they want to buy. Then you’d take that number and plug it into stoneAmount in the LocalScript, and it should work properly.

2 Likes

Well to answer your question you would need to add something like an identifier for what the player want to purchase like

RemoteEvent:FireServer("ItemName")

I personally wouldnt recommend this as it can be unsecure(I think?)*

Edit: @MJTFreeTime explains this really well

1 Like

i don’t really know if this works, but something like this would work well

local shopPricing = {
 ["Stone"] = 100, -- 100 gold each
 ["Iron"] = 1000 -- 1000 gold each, ect...

}


game.ReplicatedStorage.OreEvent.OnServerEvent:Connect(function(player, material, value)
 local pstats = game.ServerStorage:FindFirstchild(player.Name)
 local gold = pstats:FindFirstChild("Gold")
 local mat =  pstats:FindFirstChild(material)
 local val = value or 1
 if not mat then return end-- invalid material, cannot be used
 -- do purchase
 local price = shopPricing[material] * val
 if price <= gold.Value then
 gold.Value = gold.Value - price
 mat.Value = mat.Value + val
 else
  return -- cannot complete purchase
 end
end)
local ForSale = {}
function AddItem(Name, Currency, Price, _Cfunc) ForSale[Name] = { ["Currency"]=Currency, ["Price"]=Price, ["_Cfunc"]=_Cfunc} end


------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
AddItem("100 Stone", "Gold", 900, function(Buyer)
	print("Giving "..Buyer.Name.." 100 Stone.")
	local success, err = pcall(function()
		local leaderstats = Buyer:WaitForChild("leaderstats")
		local Stone = leaderstats:WaitForChild("Stone")
		Stone.Value += 100
	end)
	return success
end)

AddItem("10 Stone", "Gold", 100, function(Buyer)
	print("Giving "..Buyer.Name.." 10 Stone.")
	local success, err = pcall(function()
		local leaderstats = Buyer:WaitForChild("leaderstats")
		local Stone = leaderstats:WaitForChild("Stone")
		Stone.Value += 10
	end)
	return success
end)

AddItem("5 Stone", "Gold", 50, function(Buyer)
	print("Giving "..Buyer.Name.." 5 Stone.")
	local success, err = pcall(function()
		local leaderstats = Buyer:WaitForChild("leaderstats")
		local Stone = leaderstats:WaitForChild("Stone")
		Stone.Value += 5
	end)
	return success
end)
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------




local PurchaseFromStoreFunction = Instance.new("RemoteFunction", game.ReplicatedStorage)
PurchaseFromStoreFunction.Name = "PurchaseFromStoreFunction"
function MakePurchase(Buyer, ItemName, Amount)
	Amount = (Amount == nil) and 1 or Amount
	if Amount <= 0 then
		return false
	end
	local Item = ForSale[ItemName]
	if Item ~= nil then
		if Amount == 1 then
			if Buyer["leaderstats"][Item.Currency].Value >= Item.Price then
				Buyer["leaderstats"][Item.Currency].Value -= Item.Price
				local success, err = pcall(function()
					Item["_Cfunc"](Buyer)
				end)
				if not success then
					Buyer["leaderstats"][Item.Currency].Value += Item.Price
					return false
				else
					return true
				end
			else
				print(Buyer.Name.." does not have enough "..Item.Currency.." to buy '"..ItemName.."' x"..Amount)
				return false
			end
		elseif Amount > 1 then
			if Buyer["leaderstats"][Item.Currency].Value >= Item.Price*Amount then
				Buyer["leaderstats"][Item.Currency].Value -= Item.Price*Amount
				local Succeeded = 0
				for i = 1,Amount do
					local success, err = pcall(function()
						Item["_Cfunc"](Buyer)
					end)
					if success then Succeeded += 1 end
				end
				if Succeeded < Amount then
					Buyer["leaderstats"][Item.Currency].Value += Item.Price*(Amount-Succeeded)
					return false
				else
					return true
				end
			else
				print(Buyer.Name.." does not have enough "..Item.Currency.." to buy '"..ItemName.."' x"..Amount)
				return false
			end
		end
	else
		print(ItemName.." is not available in the shop.")
		return false
	end
end
PurchaseFromStoreFunction.OnServerInvoke = function(Buyer, ItemName, Amount)
	return MakePurchase(Buyer, ItemName, Amount)
end


game.Players.PlayerAdded:Connect(function(Player)
	local leaderstats = Instance.new("Folder", Player)
	leaderstats.Name = "leaderstats"
	local Gold = Instance.new("IntValue", leaderstats)
	Gold.Name = "Gold"
	Gold.Value = 1000
	local Stone = Instance.new("IntValue", leaderstats)
	Stone.Name = "Stone"
	Player.Chatted:Connect(function(msg)
		local Split = string.split(msg, " ")
		if #Split <= 1 then
			return
		end
		if Split[1]:lower() == "/buy" then
			local Amount = ((#Split > 2) and (tonumber(Split[#Split])) == nil) and 1 or tonumber(Split[#Split])
			if #Split ~= 2 and Amount ~= 1 then
				table.remove(Split, #Split)
			end
			table.remove(Split, 1)
			print(table.concat(Split, " "))
			MakePurchase(Player, table.concat(Split, " "), Amount)
		end
	end)
end)

I am using chat commands to purchase the items as an example but you can also make a gui that fires the remote function
example:

local PurchaseRemote = game.ReplicatedStorage:WaitForChild("PurchaseFromStoreFunction")

PurchaseRemote:InvokeServer("5 Stone", 5)

-- or..

PurchaseRemote:InvokeServer("5 Stone")

Make sure you test your server scripts with multiple players. It seems all of your players share the same variables in your server script, where really each function should be getting the variables from each player individually every function

my data store creates a folder for each individual player and in the folder are the stat names.

You are only seeing the same variables, but the way the remote events are passing these values are from that individual players folder.

That is a great idea and way of doing it!

wow. This seems pretty complex for a simple store system LOL. I’m definitely going to look into this.

It’s pretty simple but it lacks error handling and error script->player feedback