Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rojo Headless Plugin API Proposal #638

Open
boatbomber opened this issue Sep 25, 2022 · 7 comments · May be fixed by #639
Open

Rojo Headless Plugin API Proposal #638

boatbomber opened this issue Sep 25, 2022 · 7 comments · May be fixed by #639
Assignees
Labels
scope: plugin Relevant to the Roblox Studio plugin size: large status: in-progress Someone is working on this last time we checked. type: enhancement Feature or improvement that should potentially happen

Comments

@boatbomber
Copy link
Member

boatbomber commented Sep 25, 2022

Exposing a headless API into game will be very valuable to the Rojo ecosystem. Users can make companion plugins, and we can even utilize it ourselves for one-time plugin injections for rojo open. See issues #321 and #305.
Those are use cases and requests, and we've established there is demand and utility. Now, this issue is attempting to flesh out an execution of this idea. This is an ongoing discussion, not set in stone. Tell us about your use cases and desires so we can make this as valuable as possible!

Proposed interface:

ModuleScript game.Rojo

Plugins and command bar all share the same module cache which allows us to put our interface into a ModuleScript that can be accessed by any plugin. Using the key "Rojo" is easy to remember and follows branding.

Functions

Rojo.API:RequestAccess(plugin: Plugin, apis: {string}): {[string]: boolean}

In order to use any of the Rojo APIs, you must first explicitly request them with this function. Users will be prompted to allow/deny each API, (this function will yield until the user responds) and then the function will return a dictionary where the keys are the requested APIs and the values are booleans which represent the access status (whether or not access was granted). The first argument must be your plugin object for security and reliability purposes, and the second argument is a list of APIs you're requesting access to.

In order to keep our users safe from malicious plugins, RequestAccess must be the first function your plugin calls when using the Rojo API. Attempting to use an API without requesting it beforehand will throw an error. The only exceptions to this are Rojo.API.Version and Rojo.API.ProtocolVersion, since those are useful in checking compatibility before anything else is done.

Example Plugin:

local granted = Rojo.API:RequestAccess(plugin, { "Connected", "ConnectAsync" })
--[[
granted = {
	Connected = true, -- User granted access
	ConnectAsync = false, -- User denied access
}
--]]

image

Rojo.API:ConnectAsync(host: string?, port: string?): void

Attempts to connect Rojo to the given host and port. Same behavior as the user clicking the Connect button (sync locking, etc., all behave the same).

Rojo.API:DisconnectAsync(): void

Attempts to disconnect any active sync session.

Rojo.API:GetSetting(setting: string): any

Returns the value of the given setting.

Rojo.API:SetSetting(setting: string, value: any): void

Sets the value of the given setting.

function Rojo.API:Notify(msg: string, timeout: number?, actions: { [string]: {text: string, style: string, layoutOrder: number, onClick: (any) -> ()} }?): () -> ()

Sends a Rojo notification that indicates it comes from a third-party plugin. Returns a function that dismisses the notification.

image

Rojo.API:GetHostAndPort(): (string, string)

Returns the user's chosen host and port.

Rojo.API:CreateApiContext(baseUrl: string): ApiContext

Returns a new ApiContext using the given baseUrl.

Properties (All read-only)

Rojo.API.Version: {number}

The Rojo plugin version. (Example: {7, 2, 1})

Rojo.API.ProtocolVersion: number

The Rojo plugin's protocol version.

Rojo.API.Connected: boolean

Whether Rojo is currently connected to a serve session.

Rojo.API.Address: string?

When Rojo.API.Connected is true, this contains the address of the connected session.

Rojo.API.ProjectName: string?

When Rojo.API.Connected is true, this contains the project name of the connected session.

Events

Rojo.API.Changed: RbxScriptSignal (changedProperty: string, newValue: any?, oldValue: any?)

Fires when any property changes. Passes the name of the changed property and its new and old values.

Considering Please Discuss!

Rojo.InstanceMap

I am considering exposing the InstanceMap- the map of IDs to Instances. I don't know of any use cases though, it is only theoretically useful and feels potentially valuable. However, it's likely a decent chunk of work to do it safely (don't want third party plugins breaking things!) so unless there's a solid use case, it's not planned for this initial API.

@boatbomber boatbomber added type: enhancement Feature or improvement that should potentially happen scope: plugin Relevant to the Roblox Studio plugin status: needs design Needs more planning before implementation size: large labels Sep 25, 2022
@boatbomber boatbomber self-assigned this Sep 25, 2022
@boatbomber boatbomber linked a pull request Sep 25, 2022 that will close this issue
@boatbomber
Copy link
Member Author

I aim to have a security permissions layer as well, much like Roblox's own permission system. When a third party plugin attempts to use an API for the first time you will need to approve it first. This will hopefully mitigate a lot of potential abuse.

@boatbomber
Copy link
Member Author

I will also need to make it popup when something already exists at _G.Rojo, asking if you want this version to overwrite the existing version (will show you the versions and sources) so that you can have Rojo Boatly and Rojo simultaneously and control which one is exposing the API.

@Dekkonot
Copy link
Member

This might seem obvious, but why not use a more dependable solution like a ModuleScript? Initial testing suggests that plugins share the state of ModuleScripts, so there doesn't seem to be a downside.

If we were to put a ModuleScript somewhere in the data model, consumers would be able to require it and then use the API from there. It relies less on busywaiting to ensure _G.Rojo is loaded and we could have its Archivable property set to false to make sure it doesn't get saved into a file.

The only downside I see is that we may introduce noise into someone's game, but I think I prefer that over use of _G?

I'm more than open to being told "that's unacceptable and I would hate it" but that's my opinion right now

@boatbomber
Copy link
Member Author

What makes that more dependable? Just seems like more work and cases to consider since you start dealing with Instances and DataModel stuff instead of just Lua environments.

@Dekkonot
Copy link
Member

The reason I say it seems more dependable is because it's ordered. You can have a consumer use WaitForChild to know when the Rojo API is ready rather than the snippet suggested in the currently pending PR:

local function GetRojo()
	local api = _G.Rojo
	while not api do
		task.wait()
		api = _G.Rojo
	end
	return api
end

local Rojo = GetRojo()

I think as a consumer I'd prefer it to be local Rojo = require(Location:WaitForChild("RojoApi")) but that's my opinion.

@boatbomber
Copy link
Member Author

Yeah, that's a fair critique. I'm willing to rework it to be a ModuleScript if that's what people want.

@boatbomber
Copy link
Member Author

Done. Updated the docs as well. The suggested snippet is now:

local Rojo = require(game:WaitForChild("Rojo", math.huge))
print(Rojo.API.Version)

@kennethloeffler kennethloeffler added status: in-progress Someone is working on this last time we checked. and removed status: needs design Needs more planning before implementation labels Feb 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope: plugin Relevant to the Roblox Studio plugin size: large status: in-progress Someone is working on this last time we checked. type: enhancement Feature or improvement that should potentially happen
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants