generated from bitsquid_dt/dt-plugin-template
API design #2
Labels
No labels
bug
confirmed
bug
duplicate
bug
need-info
bug
new-report
kind
bug
kind
discussion
kind
documentation
kind
enhancement
kind
feature
kind
question
kind
upstream
stage
actionable
stage
blocked
stage
design
stage
proposal
status/good first issue
status/invalid
status/regression
status/wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: bitsquid_dt/p2p#2
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The overall API should be modeled after the engine's network features:
It should also be modeled to fit the more specific requirements of mods as consumers:
or if it should combine all hubs
Implementation details:
It might be necessary to evaluate the performance of having a loose, self-describing format like SJSON vs something like Stingray's strict, pre-defined
.network_config
.The latter can significantly decrease the amount of data transferred (especially by dropping field names), while the former has greater flexibility by allowing arbitrary tables, makes backwards compatibility possible, and doesn't require a complicated syntax to define data types.
I guess the best approach would even be to make both possible. Give
create_*
atype
parameter where one of the two options can be chosen.Overall API:
p2p.create_rpc(name, opts)
: Create a new RPC with the given name.name
must not contain a dot, so that we can use the dot as separator (unless something in libp2p makes a different separator useful)/
might be a good option too, since the nature of directories makes it impossible for that to be in a mod nameopts = { type, namespace, args }
:type
denotes the type above, defaults to the loose onenamespace
defaults to the mod's name (with string replacements to make it a suitable modifier, like lowercase and_
for space)args
to provide the argument definitionstype
orargs
don't match)p2p.send_rpc(name, args)
: Call an RPCname
: if no namespace given, use the mod's own namespace. See remarks about separator aboveargs
a table of arguments, either arbitrary, or validated, depending on RPC typep2p.receive_rpc(name, callback)
: Register a callback for the RPC. Seep2p.send_rpc
forname
parametercallback
will receive a singleargs
table. If something else is needed, it needs to be curriedp2p.create_object(name, opts)
: Create a new, shared object__index
and__newindex
metatables setup up such that the fields are mapped to a synchronized storagename
: Seep2p.create_rpc
opts
: Seep2p.create_rpc
type
is loose, provide methods to create fields at runtime.__newindex
might be enough, if not some kind ofregister_field
.p2p.fetch_object(name)
: Create a shared object, but expect another client to define itThoughts about versioning:
It may become useful to encourage versioning for RPCs/objects.
For individual entries, that's probably relatively simple, by just encouraging people to create
_v2
,_v3
suffixes, and then each mod can decide themselves if they want to implement backwards compatibility.Enforcing the scheme in code will likely be hard, unless I also force people to make add a
_v1
, but we could at least encourage in through documentation.Additionally, it might be useful in the long run, if the library can recognize when clients run different versions of a mod. Comparing their calls to
p2p.create_*
would be a way to realize this implicitly.But it could also be beneficial to the ecosystem as a whole, if we just force mods to provide a
mod.version
from the start, even if we don't need it right now.And to avoid the plethora of versioning schemes, that field should probably be restricted to a reasonable set like
\d{4}-\d{2}-\d{2}
\d+\.\d+\.\d+
Since the namespaces, and maybe the versioning, require knowing stuff about the particular mod that is going to call these functions, it would probably make sense to have one intermediate step that registers the mod once, instead of requiring it as argument in every function call:
I don't like the opposite way that some people have been doing:
This pollutes the
mod
object, is prone to run into issues about method names, and it makes it unclear where a function comes from.A slightly better way would be to at least prefix the method, e.g.
mod.p2p_create_object
.I guess that would actually have the benefit of not requiring
local p2p = P2P.create(mod)
orlocal p2p = mod.p2p
in every file.For comparison, the third option would be
Actually, seeing it laid out like that, I do like the last option, actually.
P2P
, which makes it both easier for the user, since they don't have to find a way to shuffle their instance around, and for the plugin implementation, since we only needlua.add_module_function
, and no nested thing creating libraries.But it does require the functions to be mostly stateless, since there is no instance to pass around.
Though I guess an entry the Lua registry, keyed by the mod would be an option.
Something that's still unclear: Is there a need for a host/client model, or can all peers be equal?
Probably not in a way that the p2p library forces it for all mods. If certain mods need this model, they should be able to negotiate it by themselves.
Though I guess if it were forced by the library, that could be helpful insofar that it's guaranteed to be the same peer for all mods.
It also depends on how much effort it actually is to implement that negotiation. If it is non-trivial, moving it into the library would also save on mods having to re-invent the wheel (and most of them probably poorly).
And I guess there is a certain range in how much the library implements. It could also be as simple as providing a function "which peer was the first in this session?" (which is a question that can be answered even if multiple peers came and left already). Though keeping track of join times only for this wouldn't make sense, either.
However, one thing that could make a host/client model very hard to implement is the fact that in Fatshark's sessions the dedicated server is the host, who will never leave, and so it might actually be possible that within a single mission, all original players leave and the session stays active with the newer players. If that situation is possible, then host migration would be mandatory, and that's quite the beast to implement.
That would actually make a good argument to not provide any concept of a "host" in the library, and to also discourage mods from relying too much on it.