From this video, a RBXL file will be downloadable from the description that contains all of the working files from the video. If you have any problems with it, let me know!
Would you pls make a video of inheritance pls? __index was a bit difficult to understand on last video, i know the concept more less but i didnt understand it very good on the practice
I think I might be missing something your events lack the ability to pass in the player variable or any variables for that matter from the client to the server like the normal onServerEvent would.
ive been looking for this for months, this makes me revamp my whole game. its like a nice framework, this should be a standard of creating games. The creator is a man of focus, commitment, sheer f will. LoL
for anyone wondering how to make this work with arguments, add ... at the end of every function declaration and call. Ill give the example code I made, but I did change some of the names to fit my naming convention a bit better. If anyone doesn't know, ... basically means any amount of arguments. --in event listener --server-clients function module:AsyncClients(clients:table,...) forloop.forLoopsForClassMethods(self.SyncClient,clients,...) end --server-> all clients function module:AsyncAllClients(funcName:string,...) rs.Events.Async:FireAllClients(self.Name,funcName,...) end --server->singleClient->Server function module:SyncClient(client,funcName:string,...) rs.Events.Sync:InvokeClient(client,funcName,...) end --Client->server->client function module:SyncServer(client,funcName:string,...) rs.Events.Sync:InvokeServer(funcName,...) end --client->server function module:AsyncServer(funcName:Function,...) rs.Events.Async:FireServer(funcName,...) end --in forloop return { forLoopsForClassMethods = function(func,data,...) for client, FuncName in pairs(data) do func(nil,client,FuncName,...) end end and in event listener local success,errorMSG = pcall(function() rs.Events.Async.OnClientEvent:Connect(function(trace,func,...) if self.AsyncFuncs[func] then return self.AsyncFuncs[func](...) end end) rs.Events.Sync.OnClientInvoke= function(funcName,...) if self.SyncFuncs[funcName] then return self.SyncFuncs[funcName](...) else return nil end end end)
if not success then warn(errorMSG) rs.Events.Async.OnServerEvent:Connect(function(player,func,...) if self.AsyncFuncs[func] then return self.AsyncFuncs[func](player,...) end end) rs.Events.Sync.OnServerInvoke= function(player,funcName,...) if self.SyncFuncs[funcName] then return self.SyncFuncs[funcName](player,...) else return nil end end end return self -------FOR EXAMPLE ON HOW TO USE------ Server: EventHandler:AsyncClients(clients,"test1","Test2") client: local sync ={ ["TestOnClient"] = function(arg1,arg2) print(arg1,arg2) end, Output=>"test1 Test2" }
GREAT QUALITY VIDEOS, i have never seen any other playlist like this one! KEEP GOING PLS! This is the first time i believe you can learn write clean and good code with youtube videos!
Your tutorials are absolutely magnificent, please keep uploading. Using OOP the way you do, is brilliant and I will now be playing around with this to see how much I can get done with it. I'm working on a battlegrounds game currently and implementing this has put the project miles ahead.
It might not be a good idea to utilize a single remote function for all your events. This is because if two remote functions need to be called simultaneously, and one of them takes longer to return a value, it will delay the execution of the other remote function calls as well, which could have a significant impact on the game.
This is THE BEST Roblox scripting video. I completely rewrote my simple game after watching this and your previous video, and it's 10x better now. You've earned my sub!
How would I fire a event with arguments? ['updateCoins'] = function(value) game.Players.LocalPlayer.start.playerScreen.Stats.Coins.Frame.TextLabel.Name = value return true end, this is my thing: eventHandler:synchronousClient('updateCoins')
So I got this working with minimal changes, the quick and dirty way is to use Variadic Functions: At the end of every method in EventHandler, just add '...' and pass it down to your bridges. For example: function eventHandlerClass:asyncServer(funcName, ...) print("Calling asyncServer for "..funcName) asyncBridge:FireServer(funcName, ...); end Then do the same with all the methods in EventListener, pass the extra parameters into your functions. For example: rs.Events.Asynchronous.OnServerEvent:Connect(function(player, funcName, ...) if self.asyncFunctions[funcName] then return self.asyncFunctions[funcName](player, ...) end end) Now from my client, I can trigger an event along with a "crop" variable: eventHandler:asyncServer("collectCrop", crop); and handle it on the server: local asyncFunctions = { ['collectCrop'] = function(player, crop) end, };
I dont know if im misunderstanding, but for remotefunctions, or sync calls, wouldn't the listener not work? Since it returns a value to the invoke, it returns it to the handler. this skips the sync functions passed in on the listeners. So to get a value back from an invoke you would have to edit some of it like: function module:SyncServer(client,funcName:string,...) return rs.Events.Sync:InvokeServer(funcName,...) end instead of function module:SyncServer(client,funcName:string,...) rs.Events.Sync:InvokeServer(funcName,...) end and then get the data when you call the SyncServer() local Answer = EventHandler:SyncServer("GetAnswer"). I could be misunderstanding, but this is the only way I could get sync to work
No you are correct I did miss this out in the video, this was my mistake, to get the remote function to work you do need the return value for it to fully function. Great that you noticed however I will most likely make an updated version of this in the future without using remote functions as they are not the best.
Been doing something similar for years, but with a couple more events. This is slightly better organized than my system, though, so I'll give it a try, thanks. Not that I have time to develop as uni student, but whenever I do I'll make sure to remember and give it a try. It's funny how almost 90% of the things you do are nearly identical to what I used to do. The way you structure your scripts, the client and server scripts, and the system itself. I feel like I'm watching myself script 😂
My solution to 17:50 is i would pass "self" as an argument to the loop function then use it instead of nil since passing nil to these : functions are dangerous and prone to error if self is used at all
Im making a GUI based Game. I have about 50 RemoteEvents, a LocalScript that controls my GUI, and the Server script. Idk how im gonna refactor all the my code to fit this model. Im already not very good at using studio, my work is not very good, and my code is kinda all over the place.
But all these are useless for synchronic functions aren't they? You can connect asynchronous functions but on synchronic ones, you need to make the function EQUAL to another. Would you mind explaining this a bit further?
hello am just gonna say am not that advanced but i still enjoy your videos and i wanted to ask some questions like: why do you put ; after every line? can you use any var name or just self ?
Semicolons are optional in Lua, just like in JavaScript. He probably has experience in Java or C so prefers to include them. I also like including them to make it clear where statements end. I think you could call it something besides ‘self’, but better to use the standard naming convention so you don’t get confused.
I don't understand i tried 3 times the tutorial and the "changeNameOnUi" is not working for me but "movePlayer" is working i don't understand what i am doing wrong. It's not you're fault its mine but you're videos are so damn good, keep up the good work while i am trying to do something with my "changeNameOnUi" 😆.
Depends what for, the asynchronous clients method is used for sending events from the server to a certain amount of clients. To make a combat system you would usually go from the clients input to the server.
Is the method safe against exploiters that spam the remote events? And should I write database saving functions in the table? Great explaining nonetheless, I'm new to the roblox ide and learned a few front this 😅
The reason why I like this method so much is that you only have to prevent 1 remote event and 1 remote function from being spammed by hackers and this can all be done in the event listener, to prevent hacker spamming you can create a table in the event listener that stores the names of players that have recently called the remote event and with a specific function name they used. If the user is spamming that they will not be able to as they’re stored temporarily in that table. This means that they won’t be able to spam any event ever in your game 👍
@@shishidoseijuro7770 because any yielding you do in the OnClientInvoke will delay the server from running anything after it. So you could make a call to task.wait and pass in math.huge to make the server script exhaust its maximum execution time.
@@betapacket Wouldn't that only work if he explicitly added a handler that calls task.wait with the parameter you give it? Which nobody in their right mind would do?
@@kaleoscreations No(?), all you need to do for the server to yield is call some function that does yield (e.x. task.wait or wait) in the OnClientInvoke function. Client script: ```lua if not game:IsLoaded() then game.Loaded:Wait() end local ReplicatedStorage = game:GetService("ReplicatedStorage") local Remotes = ReplicatedStorage.Remotes local Client = Remotes.Client Client.Test.OnClientInvoke = function() task.wait(math.huge) end ``` Server script: ```lua if not game:IsLoaded() then game.Loaded:Wait() end local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local Remotes = ReplicatedStorage.Remotes local Client = Remotes.Client for _, player in Players:GetPlayers() do print("hello") Client.Test:InvokeClient(player) -- anything after this shouldn't run print("after invoke") end print("out of loop") ```
@@kaleoscreations The client can manipulate the way they handle such requests, it doesnt matter if the intended handler by the dev was supposed to work "x" way, because client can make it work "y" way and as @betapacket said, you could yield the respective server thread indefinitly. I'm not sure if roblox has a way to combat this with a timeout mechanism of sorts(after x time of waiting it just moves on or calls some subroutine idk). the key note to take from this is that you should never invoke client since the client can't be trusted(even a non malicious client could just lag out the request unintentionally). Instead do FireClient and if you really need a responde, then setup your script so the client can FireServer back as a response but write your code in a way that your game wont break if you don't get the response. For example, if you expect a player to select an owned character before a round in your game or something, you'd do something like FireClient for the gui to popup and allow player to make a selection and then they fireServer back to update their selection(doing all the necessary checks to make sure the player owns the character they selected etc.) and although it'd be nice to get a response from client, it may not come in time before the round starts and in that case we could perhaps just auto-select for the player a default character or the previous character they used as a safety coroutine in case we never get the expected response in time.
I get an error on line 4 saying: 08:03:04.473 ReplicatedStorage.Utility.ForLoops:4: attempt to call a nil value - Server - ForLoops:4 this is the code is the problem that I didn't give func a meaning? return { forLoopsForClassMethods = function(func, clientTable) -- passes the function and Table for client, funcName in pairs(clientTable) do func(nil, client, funcName) end end,
You need to parse a function as func in otherwise you will not be able to call the function in the for loop, can you show me where you call the forLoopsForClassMethod?
@@AshRBX_dev ok, return { forLoopsForClassMethods = function(func, clientsTable) -- passes the function and Table for client, funcName in pairs(clientsTable) do func(nil, client, funcName) end end,
I ran into this exact issue. For me, I didn't update the eventHandler and eventListener variables in the client script to reference the class. Not sure if I missed it or what but if you go here 26:07 you can see what those variables need to be changed to. (edit: fixed the time stamp)
From this video, a RBXL file will be downloadable from the description that contains all of the working files from the video. If you have any problems with it, let me know!
Would you pls make a video of inheritance pls? __index was a bit difficult to understand on last video, i know the concept more less but i didnt understand it very good on the practice
I think I might be missing something your events lack the ability to pass in the player variable or any variables for that matter from the client to the server like the normal onServerEvent would.
@@chasee895 add ... to all function calls and definitions as the last param.
ive been looking for this for months, this makes me revamp my whole game. its like a nice framework, this should be a standard of creating games. The creator is a man of focus, commitment, sheer f will. LoL
for anyone wondering how to make this work with arguments, add ... at the end of every function declaration and call. Ill give the example code I made, but I did change some of the names to fit my naming convention a bit better. If anyone doesn't know, ... basically means any amount of arguments.
--in event listener
--server-clients
function module:AsyncClients(clients:table,...)
forloop.forLoopsForClassMethods(self.SyncClient,clients,...)
end
--server-> all clients
function module:AsyncAllClients(funcName:string,...)
rs.Events.Async:FireAllClients(self.Name,funcName,...)
end
--server->singleClient->Server
function module:SyncClient(client,funcName:string,...)
rs.Events.Sync:InvokeClient(client,funcName,...)
end
--Client->server->client
function module:SyncServer(client,funcName:string,...)
rs.Events.Sync:InvokeServer(funcName,...)
end
--client->server
function module:AsyncServer(funcName:Function,...)
rs.Events.Async:FireServer(funcName,...)
end
--in forloop
return {
forLoopsForClassMethods = function(func,data,...)
for client, FuncName in pairs(data) do
func(nil,client,FuncName,...)
end
end
and in event listener
local success,errorMSG = pcall(function()
rs.Events.Async.OnClientEvent:Connect(function(trace,func,...)
if self.AsyncFuncs[func] then return self.AsyncFuncs[func](...) end
end)
rs.Events.Sync.OnClientInvoke= function(funcName,...)
if self.SyncFuncs[funcName] then return self.SyncFuncs[funcName](...) else return nil end
end
end)
if not success then
warn(errorMSG)
rs.Events.Async.OnServerEvent:Connect(function(player,func,...)
if self.AsyncFuncs[func] then return self.AsyncFuncs[func](player,...) end
end)
rs.Events.Sync.OnServerInvoke= function(player,funcName,...)
if self.SyncFuncs[funcName] then return self.SyncFuncs[funcName](player,...) else return nil end
end
end
return self
-------FOR EXAMPLE ON HOW TO USE------
Server:
EventHandler:AsyncClients(clients,"test1","Test2")
client:
local sync ={
["TestOnClient"] = function(arg1,arg2)
print(arg1,arg2)
end,
Output=>"test1 Test2"
}
I'm glad I found this comment. Thank youuu! :D
although code its different, it actually helpful, because i dont know where the dot should i put. thanks
GREAT QUALITY VIDEOS, i have never seen any other playlist like this one! KEEP GOING PLS! This is the first time i believe you can learn write clean and good code with youtube videos!
The effort you put in this tutorial is just stunning. And please keep on using OOP. Sub earned ...
Appreciate it, will keep using this model to create other useful programs. Thanks for watching anyways 👍👍
Your tutorials are absolutely magnificent, please keep uploading. Using OOP the way you do, is brilliant and I will now be playing around with this to see how much I can get done with it. I'm working on a battlegrounds game currently and implementing this has put the project miles ahead.
It might not be a good idea to utilize a single remote function for all your events. This is because if two remote functions need to be called simultaneously, and one of them takes longer to return a value, it will delay the execution of the other remote function calls as well, which could have a significant impact on the game.
This is THE BEST Roblox scripting video. I completely rewrote my simple game after watching this and your previous video, and it's 10x better now. You've earned my sub!
How would I fire a event with arguments?
['updateCoins'] = function(value)
game.Players.LocalPlayer.start.playerScreen.Stats.Coins.Frame.TextLabel.Name = value
return true
end,
this is my thing: eventHandler:synchronousClient('updateCoins')
So I got this working with minimal changes, the quick and dirty way is to use Variadic Functions: At the end of every method in EventHandler, just add '...' and pass it down to your bridges. For example:
function eventHandlerClass:asyncServer(funcName, ...)
print("Calling asyncServer for "..funcName)
asyncBridge:FireServer(funcName, ...);
end
Then do the same with all the methods in EventListener, pass the extra parameters into your functions. For example:
rs.Events.Asynchronous.OnServerEvent:Connect(function(player, funcName, ...)
if self.asyncFunctions[funcName] then return self.asyncFunctions[funcName](player, ...) end
end)
Now from my client, I can trigger an event along with a "crop" variable:
eventHandler:asyncServer("collectCrop", crop);
and handle it on the server:
local asyncFunctions = {
['collectCrop'] = function(player, crop)
end,
};
Very great content! I just started designing a game and I am working on using this model after watching the last few videos.
me too, this guy is the best one
I dont know if im misunderstanding, but for remotefunctions, or sync calls, wouldn't the listener not work? Since it returns a value to the invoke, it returns it to the handler. this skips the sync functions passed in on the listeners. So to get a value back from an invoke you would have to edit some of it like:
function module:SyncServer(client,funcName:string,...)
return rs.Events.Sync:InvokeServer(funcName,...)
end
instead of
function module:SyncServer(client,funcName:string,...)
rs.Events.Sync:InvokeServer(funcName,...)
end
and then get the data when you call the SyncServer()
local Answer = EventHandler:SyncServer("GetAnswer").
I could be misunderstanding, but this is the only way I could get sync to work
No you are correct I did miss this out in the video, this was my mistake, to get the remote function to work you do need the return value for it to fully function. Great that you noticed however I will most likely make an updated version of this in the future without using remote functions as they are not the best.
Great video. And thanks for putting the code up. Helped to find where i missed.
Appreciate the kind words 👍
Been doing something similar for years, but with a couple more events. This is slightly better organized than my system, though, so I'll give it a try, thanks. Not that I have time to develop as uni student, but whenever I do I'll make sure to remember and give it a try.
It's funny how almost 90% of the things you do are nearly identical to what I used to do. The way you structure your scripts, the client and server scripts, and the system itself. I feel like I'm watching myself script 😂
Haha no way, maybe because I’m also a uni student we have similar styles 😂. Hope you find it useful anyway 👍👍
Really great, informative content, will hopefully see more videos from you in the future.
Appreciate the kind words, I have a new video in the works that is almost complete which follows on from this series 👍
My solution to 17:50 is i would pass "self" as an argument to the loop function then use it instead of nil since passing nil to these : functions are dangerous and prone to error if self is used at all
Great video but can you show us how to save shop data to so if a player buy something in the shop and leave it will save what he bougth
Thank you for explaining this topic.
i love ur channel you need more followers, and can we get another series tutorial for like tycoon games? there's no good tutorials for it :)
Appreciate the kind words, I will definitely put that on my list of series to make 👍
did u cut the video? some code suddenly changed like during the event handler class in the server
this video permanently damaged whatever brain cells I have left
How would I pass specific variables to the functions?
Im making a GUI based Game. I have about 50 RemoteEvents, a LocalScript that controls my GUI, and the Server script. Idk how im gonna refactor all the my code to fit this model.
Im already not very good at using studio, my work is not very good, and my code is kinda all over the place.
But all these are useless for synchronic functions aren't they? You can connect asynchronous functions but on synchronic ones, you need to make the function EQUAL to another. Would you mind explaining this a bit further?
hello am just gonna say am not that advanced but i still enjoy your videos and i wanted to ask some questions like:
why do you put ; after every line?
can you use any var name or just self ?
Semicolons are optional in Lua, just like in JavaScript.
He probably has experience in Java or C so prefers to include them.
I also like including them to make it clear where statements end.
I think you could call it something besides ‘self’, but better to use the standard naming convention so you don’t get confused.
thank you man i might use semicolons from now on to make my code clear thanks again @@kaleoscreations
right on time!
Appreciate you being early 😄
The best tutorial I ever seen keep going
Appreciate it! Will do
I don't understand i tried 3 times the tutorial and the "changeNameOnUi" is not working for me but "movePlayer" is working i don't understand what i am doing wrong.
It's not you're fault its mine but you're videos are so damn good, keep up the good work while i am trying to do something with my "changeNameOnUi" 😆.
Bravo 👏👏
Appreciate you watching!
Hello! Nice tutorial, will u be able to explain loops and pair loops in the future?
Of course! I can do a beginner series if that is needed 👍
So if I made a combat system, would I use async clients?
Depends what for, the asynchronous clients method is used for sending events from the server to a certain amount of clients. To make a combat system you would usually go from the clients input to the server.
why do you put semi colons at the end of each line?
prob cause of habit from programming with other languages lol
Yes, as Phoenix states It's a habit from other programming languages, you don't need the ; however as it works in the Lua syntax I keep it with ; 👍
You are such a blessing!
Is the method safe against exploiters that spam the remote events? And should I write database saving functions in the table?
Great explaining nonetheless, I'm new to the roblox ide and learned a few front this 😅
The reason why I like this method so much is that you only have to prevent 1 remote event and 1 remote function from being spammed by hackers and this can all be done in the event listener, to prevent hacker spamming you can create a table in the event listener that stores the names of players that have recently called the remote event and with a specific function name they used. If the user is spamming that they will not be able to as they’re stored temporarily in that table. This means that they won’t be able to spam any event ever in your game 👍
@@AshRBX_dev Thanks !!🙏
don't tell ash that I will break his game if it uses RemoteFunctions for Server -> Client communication
wym?
@@shishidoseijuro7770 because any yielding you do in the OnClientInvoke will delay the server from running anything after it. So you could make a call to task.wait and pass in math.huge to make the server script exhaust its maximum execution time.
@@betapacket Wouldn't that only work if he explicitly added a handler that calls task.wait with the parameter you give it? Which nobody in their right mind would do?
@@kaleoscreations No(?), all you need to do for the server to yield is call some function that does yield (e.x. task.wait or wait) in the OnClientInvoke function.
Client script:
```lua
if not game:IsLoaded() then
game.Loaded:Wait()
end
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Remotes = ReplicatedStorage.Remotes
local Client = Remotes.Client
Client.Test.OnClientInvoke = function()
task.wait(math.huge)
end
```
Server script:
```lua
if not game:IsLoaded() then game.Loaded:Wait() end
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Remotes = ReplicatedStorage.Remotes
local Client = Remotes.Client
for _, player in Players:GetPlayers() do
print("hello")
Client.Test:InvokeClient(player) -- anything after this shouldn't run
print("after invoke")
end
print("out of loop")
```
@@kaleoscreations The client can manipulate the way they handle such requests, it doesnt matter if the intended handler by the dev was supposed to work "x" way, because client can make it work "y" way and as @betapacket said, you could yield the respective server thread indefinitly. I'm not sure if roblox has a way to combat this with a timeout mechanism of sorts(after x time of waiting it just moves on or calls some subroutine idk). the key note to take from this is that you should never invoke client since the client can't be trusted(even a non malicious client could just lag out the request unintentionally). Instead do FireClient and if you really need a responde, then setup your script so the client can FireServer back as a response but write your code in a way that your game wont break if you don't get the response. For example, if you expect a player to select an owned character before a round in your game or something, you'd do something like FireClient for the gui to popup and allow player to make a selection and then they fireServer back to update their selection(doing all the necessary checks to make sure the player owns the character they selected etc.) and although it'd be nice to get a response from client, it may not come in time before the round starts and in that case we could perhaps just auto-select for the player a default character or the previous character they used as a safety coroutine in case we never get the expected response in time.
I get an error on line 4 saying: 08:03:04.473 ReplicatedStorage.Utility.ForLoops:4: attempt to call a nil value - Server - ForLoops:4
this is the code is the problem that I didn't give func a meaning? return {
forLoopsForClassMethods = function(func, clientTable) -- passes the function and Table
for client, funcName in pairs(clientTable) do
func(nil, client, funcName)
end
end,
};
You need to parse a function as func in otherwise you will not be able to call the function in the for loop, can you show me where you call the forLoopsForClassMethod?
@@AshRBX_dev ok, return {
forLoopsForClassMethods = function(func, clientsTable) -- passes the function and Table
for client, funcName in pairs(clientsTable) do
func(nil, client, funcName)
end
end,
};
this the whole code
@@AshRBX_dev can you still help with the problem?
I ran into this exact issue. For me, I didn't update the eventHandler and eventListener variables in the client script to reference the class. Not sure if I missed it or what but if you go here 26:07 you can see what those variables need to be changed to. (edit: fixed the time stamp)