Crate system could be exploited?

I was recently looking over my crate system, and realised that there’s a massive potential flaw, that could allow exploiters to win any item.

local AllItems = {}
	local Result
	local FrameTimes = math.random(45, 47) -- How many frames it goes through before stopping
	local RewardItem
	
	-- Making the item list
	for i, item in pairs(CrateData[crate]) do
		local RarityNumber = RarityTimes[crate][item.Rarity]
		local Times = math.max(RarityNumber + chance)
		
		for i = 1, Times do
			table.insert(AllItems, item)
		end
	end
	
	math.randomseed(tick())
	
	-- Chooses the item
	for i = 1, FrameTimes + 5 do
		local Item = AllItems[math.random(1, #AllItems)]
		local NewFrame = SampleItem:Clone()
		NewFrame.Visible = true
		NewFrame.Parent = Inner
		NewFrame.ImageColor3 = Colors[Item.Rarity]
		--NewFrame.Icon.Image = Item.Image.Value
		NewFrame.ItemName.Text = Item.Item
		NewFrame.Position = UDim2.new((i - 1) * 0.25, 0, 0, 0)
		
		if i == FrameTimes then
			RewardItem = NewFrame
			Result = Item
			--wonItem:FireServer(item.Item.Value, item.Name)
		end
	end
		
	for i = 0.01, 5, 0.01 do
		if RewardItem.Position.X.Scale == 0.5 then
			break
		end
		
		for i, v in pairs(Inner:GetChildren()) do
			v.Position = UDim2.new(v.Position.X.Scale - 0.25, 0, 0, 0)
			if v.Position.X.Scale <= -0.15 then
				v:Destroy()
			end
		end
		
		script.Tick:Play()
		
		wait(i)
	end

This all handled on the client, and then at the end I fire a RemoteEvent which passes through the ‘RewardItem’, which an exploiter could make any item that. How can I transfer as much of this to the server as possible? Since a lot of it handles UI and what not :confused:

You may do a codified remote so it doesn’t fire if it doesn’t return a code as second value. Like: :FireServer(put code here with item to create) and server side a if that checks if code is returned

Can you explain in more detail???

Crates should already be handled by the server for say opening them, to remove the crate from the user’s inventory or whatever you do, like buying a crate and it instantly goes to opening. All currency should go through the server and anything that involves any type of data change.

Simply have the server determine the item and reward the player with that when they open the crate and remove the crate from their inventory, so if they leave they don’t lose the item. Then just fire the client, or return, could be a remotefunction or event, and then have the crate go through as normal with the item the server says.

Edit: Don’t let the client really say anything, the client should instead ask. The client asks the server to open/buy the crate, the server does the checks, generates the item and rewards it while removing whatever it needs to, then it tells the client to go ahead and do what it’s supposed to do.

1 Like

No but I have a create opening animation, I cant do that on the server.

Please provide coded examples

No need for coded examples here, the animation and everything can be done on the client but you should pre-determine the item beforehand and then tell the client to do the animation that ends up telling them the item they got.

I don’t wanna redo my system tho. Already looked at that, and it’s a lot of unneccsary stuff. I just want to be able to convert what I have already to make it safe, not redo it entirely

It’ll never be secure nor safe if you allow the client to tell the server the item they got, it’s quite unfortunate but the reality of it.

Okay, have the server generate the item and tell the client, and when you generate the frames, simply replace the frame instead of it being random, make the reward frame based off your numbers and everything be the reward item. The server will still decide what the winning item is, and it’ll finish on that item every time since you set it to that as it’s generating the frames.

This way the client never has to tell the server what item they got, and instead just match up to what the server actually gave them. :smile:

Your system should work like this:

Client buys crate >
Crate info passed to server >
Server handles rarity and selects item >
Server returns item name >
Client performs animation.

2 Likes

I still got no clue how that works…

local AllFrames, WinningItem = Spin:InvokeServer(crate, chance)


local AllItems = {}
	local Result
	local FrameTimes = math.random(45, 50)
	local RewardItem
	
	-- Making the item list
	for i, item in pairs(CrateData[crate]) do
		local RarityNumber = RarityTimes[crate][item.Rarity]
		local Times = math.max(RarityNumber + chance)
		
		for i = 1, Times do
			table.insert(AllItems, item)
		end
	end
	
	math.randomseed(tick())

I literally don’t know where to go from here…

Oop didn’t notice I was repeating someone’s post.

For your purpose you can randomly assign items to the first several frames, and then append the winning item as the last frame. If you need padding, ofcourse you can append a few more to ensure that it doesn’t just dead stop, and make sure the item == winningitem

Guess that may be a bad way to word it.
Being you’re using a random number of times, you can force the stop frame to be the winning item.

I’m so close atm

Server

local function SpinStart(player, crate, chance)
	local AllItems = {}
	local FrameTimes = math.random(45, 50)
	local RewardItem
	
	-- Making the item list
	for i, item in pairs(CrateData[crate]) do
		local RarityNumber = RarityTimes[crate][item.Rarity]
		local Times = math.max(RarityNumber + chance)
		
		for i = 1, Times do
			table.insert(AllItems, item)
		end
	end
	
	math.randomseed(tick())
	
	local RewardItem = AllItems[math.random(1, #AllItems)]
	
	return AllItems, RewardItem
end

Client

local AllItems, WinningItem = Spin:InvokeServer(crate, chance)
	
	local WinningFrame
	
	print(WinningItem.Item)
	
	for i = 1, #AllItems do
		local Item = AllItems[math.random(1, #AllItems)]
		local NewFrame = SampleItem:Clone()
		NewFrame.Visible = true
		NewFrame.Parent = Inner
		NewFrame.ImageColor3 = Colors[Item.Rarity]
		--NewFrame.Icon.Image = Item.Image.Value
		NewFrame.ItemName.Text = Item.Item
		NewFrame.Position = UDim2.new((i - 1) * 0.25, 0, 0, 0)
		
		if WinningItem.Item == Item.Item then
			WinningFrame = NewFrame
		end
	end

	for i = 0.01, 5, 0.01 do
		print(WinningFrame, WinningFrame.Position.X.Scale)
		if WinningFrame.Position.X.Scale == 0.5 then
			break
		end
		
		for i, v in pairs(Inner:GetChildren()) do
			v.Position = UDim2.new(v.Position.X.Scale - 0.25, 0, 0, 0)
			if v.Position.X.Scale <= -0.15 then
				v:Destroy()
			end
		end
		
		script.Tick:Play()
		
		wait(i)
	end

Problem is, with the print in the client, it prints

  SampleItem 82.75
  SampleItem 82.5
  SampleItem 82.25
  SampleItem 82
  SampleItem 81.75
  SampleItem 81.5
  SampleItem 81.25
  SampleItem 81
  SampleItem 80.75
  SampleItem 80.5
  SampleItem 80.25

So it is getting a winning frame, however the frame is a long long way. It shouldn’t be 80 away, it’ll take too long.

Are you making it slow down on purpose? because the wait(i) seems like it’d slow down way to fast which isn’t something you’d want.

The wait is osmething I want, as if you spin like a wheel, it’ll be fast at the start, and slow down towards the end, eventually stopping on the winning frame

I get that, but the increment variable just seems too small.

What should I make it then???

for i = 0, 1, 1/#frames  do --see how that might go

Still to far


Starts to slow down long before the winning frame is visible. I feel the winning frame needs to be a closer frame, not so far away