
๐ฅ๏ธServices
Services are a fundamental part of Noir. This page will go over them.
What Are Services?
Services, when simplified, are simply tables containing methods you can use throughout your code.
Unlike libraries, services tend to interact with the game and store data within itself or through g_savedata. 
Example:
local MyService = Noir.Services:CreateService("MyService") -- Create a service
-- Called when the service is initialized. This is to be used for setting up the service
function MyService:ServiceInit()
    self.AVariable = 5
end
-- Called when the service is started. This is to be used for setting up things that may require event connections, etc
function MyService:ServiceStart()
    Noir.Callbacks:Connect("onPlayerJoin", function()
        server.announce("Server", "A player joined!")
    end)
end
function MyService:ACustomMethod()
    server.announce("Server", "Hello World")
endYou probably noticed in the example that there are two strange methods - ServiceInit, and ServiceStart. These methods are strictly for service initialization and are actually quite useful.
Here's a quick rundown:
ServiceInit is called when the service is initialized by Noir after Noir:Start() is called. This method is called just before Noir.Started is fired.
ServiceStart is called when the service is started by Noir after Noir:Start() is called. This method is called straight after Noir.Started is fired.
Both of these methods are optional, but be sure to use ServiceInit if you need to store data within your service, setup things like events, or load data from your service's g_savedata accessible via Service:GetSaveData(), Service:Load(), etc.
Services can be stored as a variable and retrieved at anytime throughout your code, but you can also use Noir.Services:GetService("MyService") to retrieve a service. Note that this method only works if the service has been initialized (always the case after Noir has started).
Built-In Services
Noir comes with built-in services to prevent you from writing a lot of code. They can be found under Noir.Services along with everything else relating to services.
A very notable built-in service is Noir.Services.PlayerService that wraps players in classes to make interacting with players more OOP than not.
Creating A Service
Creating a service is simple. First, you need to give it a name. For this example, we'll assume the name of "MyService". Now, add the following code to your addon:
local MyService = Noir.Services:CreateService("MyService"):ServiceInit()
Now, we're not all done. We can optionally add a method to our service called ServiceInit. This isn't required but is often used to setup events and setup attributes, etc. See the code sample below.
function MyService:ServiceInit()
    self.Something = true
endAs mentioned under What Are Services?, ServiceInit is called when the service is initialized by Noir's bootstrapper after Noir:Start() is called but before the Noir.Started event is fired. This method is often used to store values in the service for later use.
You may be wondering what self is. Well, :ServiceInit() is a method, and self is automatically added as an argument because of the colon before ServiceInit(). Behind the scenes, Noir calls ServiceInit like so:
MyService:ServiceInit()Due to the :, Service is passed as the first argument to ServiceInit. This means that self in the example above is equivalent to MyService.
:ServiceStart()
Optionally, we can add another method to our service called ServiceStart which is called when the service is started by Noir which happens straight after the Noir.Started event is fired.
function MyService:ServiceStart()
    Noir.Callbacks:Connect("onPlayerJoin", function(...)
        server.announce("Server", "A player joined!")
    end)
endAdding Credit
This is not necessary, but if you would like to add credit to your service for whatever reason, you can add a few extra parameters to :CreateService().
Noir.Services:CreateService(
    "Name", -- The name of your service
    false, -- Whether or not this service is built into Noir. Always have this set to false
    "Short Description", -- A short description of your service
    "Loooongg description", -- A long description of your service
    {"Cuh4"} -- The authors of your service
)What's The Point?
Services help you structure your addon neatly. Instead of having 6 functions all in your script environment that interact with the same things but differently, you can bundle all of these functions into a service.
-- Unclean. Globals are cluttered.
kickedPlayers = {}
bannedPlayers = {}
function Kick(peer_id)
    server.kickPlayer(peer_id)
    table.insert(kickedPlayers, peer_id)
end
function Ban(peer_id)
    server.banPlayer(peer_id)
    table.insert(bannedPlayers, peer_id)
end-- Clean. Globals aren't cluttered. Functions are all bundled together in a service.
local ModerationService = Noir.Services:CreateService("ModerationService")
function ModerationService:ServiceInit() 
    self.kickedPlayers = {}
    self.bannedPlayers = {}
end
function ModerationService:Kick(peer_id)
    server.kickPlayer(peer_id)
    table.insert(self.kickedPlayers, peer_id)
end
function ModerationService:Ban(peer_id)
    server.banPlayer(peer_id)
    table.insert(self.bannedPlayers, peer_id)
endYou can see this sort of format in games like Roblox.
Intellisense Support
Services do have support for intellisense, but you do need to put some work into making your service intellisense-compatible.
For intellisense, you'll need this VSCode extension.
Assuming your service looks like:
local MyService = Noir.Services:CreateService("MyService")
function MyService:ServiceInit()
    self.something = true
end
function MyService:ServiceStart()
    
end
function MyService:ACustomMethod()
    server.announce("Server", "Hello World")
    return true
endYou would add this directly above :CreateService():
---@class MyService: NoirService
---@field something boolean A service attributeThe final result should look like:
---@class MyService: NoirService
---@field something boolean A service attribute
local MyService = Noir.Services:CreateService("MyService")
function MyService:ServiceInit()
    self.something = true
end
function MyService:ServiceStart()
    
end
function MyService:ACustomMethod()
    server.announce("Server", "Hello World")
    return true
endAlternatively, you can also do:
---@class MyService: NoirService
local MyService = Noir.Services:CreateService("MyService")
function MyService:ServiceInit()
    -- A service attribute
    ---@type boolean
    self.something = true
end
function MyService:ServiceStart()
    
end
function MyService:ACustomMethod()
    server.announce("Server", "Hello World")
    return true
endHowever if the attribute is clearly defined, you can remove the ---@typedeclaration.
Last updated