There is no 100% way to stop an exploiter from firing events. You say you can’t add sanity checks, but why not? How is the story structured? If your worry is them going through it in a second, why not add a time limit that stops them from spamming the event? What ways do the users normally progress in the story?
I don’t feel like adding a time limit could work, as an exploiter could just sit there and wait the minimum amount of time and fire it again.
Story progression occurs at different points, and some sections of the story will take less time than others.
The StoryProgress can increase after speaking to a certain npc, collecting a certain amount of items, reaching a certain area, killing a certain mob, etc. so there’s no 1 way to check for each individual thing
Is there a reason you can’t just remove the event entirely and implement it on server side listeners for those actions?
I could set it as a server side function, except for the fact that talking to an npc is client side purely.
Well I’d really suggest doing the story progress stuff on the server all the time. Instead of firing UpdateStoryProgress maybe do another event called OpenNPC or something. The next concern would be them just spamming the OpenNPC. If it is different NPCs then you could try checking magnitude from the NPC to the player, so then they’d have to teleport and then fire the event. You could also try to create some sort of anti teleport system but if you have any kind of portals or teleporters which often these types of games have, you’d have to include some way to allow that but not allow players to do their own teleport. It may never be perfect but these can deter the user.
Another thing I’m kind of confused by, is it just talking to the NPC or do they have to do something before that? In your previous post you said that it could be killing a certain mob or collecting items. Is the NPC just a way to progress once you’ve done those things, or is it a thing on its own?
Well using an example. The player starts at a StoryProgress value of 0. When they go to the npc to start the story and talk to them, they ask the player to do something, player selects yes, thus setting their StoryProgress to 1.
I needed to do this, as with my dialogue I didn’t want the NPC to continuely ask the player to do the quest. I wanted the dialogue to be determined based on the players story progress.
More detailed explanation can be found here:
Is the dialogue apart of the same counter as the actions are? If as you say the player says yes to maybe killing a mob and the StoryProgress goes from 0 to 1, when they kill the mob does their StoryProgress go from 1 to 2?
I haven’t gotten that far into yet, but my idea would be:
Progress always goes up by 1. Let’s say you are on ‘10’ right. If theres a mob out there, that you have to kill later on, but aren’t up to the right progress yet, then the progress would not occur.
So let’s say you kill a mob thats later required to kill to progress, but you’re only 10 into the story and that mob is say 20 into the story. It wouldn’t fire the +1 as I’d check to make that the player is at the right point in the story to get that story progress.
Lemme try explaining it a little more detailed
If you on 0. Talk to npc and they offer a quest for you to do. You take it, now go up to 1. Now you go out and kill said mob. Killing 100 of that mob wouldn’t increase you to 100, as I would do checks to see:
if StoryProgress ~= 1 then return end
-- Fire UpdateStoryProgress function
If I’m understanding this correctly, and you’re only worried about dialogue and have server side checks for all the other actions, why not just separate the dialogue counter from the story progress counter? There isn’t a great way to check if a user has actually completed dialogue so maybe just check in the story progress if they say they do then do other server side checks for the things you can like killing a mob?
Then how would like accepting quests, etc. work?
Instead of just keeping track of StoryProgress maybe try a linked list type thing. Keep track of a table that holds the current requirements to get to the next step, the next step, and a variable holding if you’ve completed it. That way whenever you do an action like kill a mob, whenever a mob dies by your hand it can check if it meets the steps requirements and move on to the next step. This should all be handled on the server. If it is like talk to NPC, you should fire an event to the server that says this player has talked to this NPC and check the current step if it is to talk to so and so.
Hmm ok, lemme see if I understand 100%
So I set up a table like so
return {
[0] = {Objective = "Go to talk to Ol' Mate"},
[1] = {Objective = "Go kill a mob"},
[2] = {Objective = "Go back to Ol' Mate"},
[3] = {Objective = "Go to the forest"},
}
So this would house the whole story line and what needs to be done to complete each event. Only store the story number in player data (0, 1, 2, 3, etc…)
So from there I’d have to link up events, etc. for when a player talks to an npc, do checks to make theyre within the radius, etc. When a mob is killed, check to see if player is at the right point, as well as entering an area.
Yeah that would probably work. Each time the player does an action that could be a goal you can check the number or the objective associated with it and see if you’ve completed the goal. If you have then you can move to the next number. It isn’t perfect but this way it is on the server so they can’t just get five billion story progress.
Kinda got basic setup. Just using the NPC dialogue stuff atm.
Client would fire the RemoteEvent whenever a task has been done
UpdateStoryProgress:FireServer('Dialogue', NPC)
Then on the server
local function Update(player, event, ...)
local User = PlayerData[player.UserId]
if not User then return end
local ExtraData = {...}
if event == 'Dialogue' then
local NPC = ExtraData[1]
local Character = player.Character
if not Character then return end
local Distance = (NPC.PrimaryPart.CFrame.Position - Character.PrimaryPart.CFrame.Position).magnitude
if Distance > 8 then return end
print('Player is within distance')
User.StoryProgress = User.StoryProgress + 1
end
end
My last question would be how can I use the players current story value to also make sure that the right story is being done?
Could I fire from the client like the stories description
UpdateStoryProgress:FireServer('Dialogue', NPC, "Go to talk to Ol' Mate")
and then on the server check something like this
Story = {
[0] = {Objective = "Go to talk to Ol' Mate"},
[1] = {Objective = "Go kill a mob"},
[2] = {Objective = "Go back to Ol' Mate"},
[3] = {Objective = "Go to the forest"},
}
-- update function
local function Update(player, event, ...)
local User = PlayerData[player.UserId]
if not User then return end
local ExtraData = {...}
if event == 'Dialogue' then
local NPC = ExtraData[1]
local StoryLine = ExtraData[2] -- Go to talk to Ol' Mate
local Character = player.Character
if not Character then return end
local Distance = (NPC.PrimaryPart.CFrame.Position - Character.PrimaryPart.CFrame.Position).magnitude
if Distance > 8 then return end
print('Player is within distance')
-- Somehow go through the 'Story' table and see if the Objective matches the StoryLine, and then check to make that Objectives number is == to the players current story?
User.StoryProgress = User.StoryProgress + 1
end
end
Well if you have what NPC it is talking to, could you not also hold the NPC in the StoryLine table like you were with the objective, then check if the NPC matches the object in the table with the StoryProgress index?
Something like this?
[0] = {Objective = workspace.NPCs["Ol' Mate"], Description = "Go to talk to Ol' Mate"},
Yeah, that’d probably work just fine.
local Globals = game.Worspace.Globals
local if Allowed = true
local function Update(player)
if Allowed = true then
Allowed = false
local User = PlayerData[player.UserId]
if not User then return end
if player = P1DataFolder.PlayerID.Value then
local Update = game.WorkSpace.P1DataFolder
end --repeat this for each player.
if Update.Checkpoint.Value = 1 and Update.Gold.Value >= Globals.ProgressCost.Value and etc etc etc etc then
User.StoryProgress = User.StoryProgress + 1
end -- this if statement checks everything needed to allow them to progress
wait(5) -- time they must wait before running all that code again.
Allowed = true
end
end
UpdateStoryProgress.OnServerEvent:Connect(Update)
The key is to have all your checks serverside. Make sure they have made it to the correct checkpoint etc. All the criteria you want before you allow them to proceed.
Also have the time limit on how often the server will run all the code. They can spam it all they want but they don’t lag out the server.
I wouldnt be able to do that with mobs for example, as they are randomly generated, and not preplaced in game
Try giving each mob an ID and associating the ID with the mob you generate. Then check if the mob that was killed has the same questId or whatever as the one in the current step table. When I say an ID for the mob, I don’t mean a single ID for every mob generated but instead a mobId for like a quest mob. Try putting a IntValue in the mob object and making it the index of the step. Then you can check if that IntValue equals the current StoryProgress.