-
Notifications
You must be signed in to change notification settings - Fork 145
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
Feather Plugin System #309
Comments
I guess #213 should be linked here as well? |
That implementation looks like it was based on dynamic linking, the goal currently is wasm. |
I think the goal is WASM in the long long run (when it gets the required feature set). For now, we are aiming for Dlibs and having a plugin database with pre-compiled plugins for the most common targets (windows, osx, Linux). This, however, would require some additional infrastructure and build server, maybe we can offload some of the building to GitHub, just maybe. |
I'm starting to think that WASM will be adequate. I've done some looking into the WASM bytecode outputted by The only thing WASM currently lacks is networking support, which is critical in the long run. The WASI spec has an issue open for it: bytecodealliance/wasmtime#70. I hope that eventually (at least by the time Feather is ready for actual use, which will be a while) they'll settle on a solution there. Dynamic linking would be nice, but it comes with a whole slew of problems... security/sandboxing are impossible, plugin reloading is difficult and platform-sensitive, and maintaining the build infrastructure will be a pain. I think that WASM is the best choice in the long run. |
I've been working on some basic plugin stuff. My working branch can be found here. So far I've got:
I'd like to note that the API design is in no way final, or even preliminary. I would like to discuss how we would like to lay it out, and there are still things to add with my current testing API. The API currently only serves the purpose of testing basic requirements. I'd like some feedback from @caelunshun and anyone else who can provide it on the current way that I have plugins implemented, I think its kinda okay but I know @caelunshun mentioned registering systems from plugins. This may or may not be impossible due to ownership issues but we can certainly look into it. As for The source for the testing plugin is (basically) extern "C" {
fn print(ptr: *const u8, len: usize);
}
#[no_mangle]
pub extern fn on_load() {
let hello = "Hello from a Plugin!";
unsafe {
print(hello.as_ptr(), hello.len());
}
}
#[no_mangle]
pub extern fn on_tick() {
let to_print = "Plugin just ticked, awesome!";
unsafe {
print(to_print.as_ptr(), to_print.len())
}
} Obviously we don't want consumers of the API to deal with interfacing with the API directly (since its ugly and can't use rust types). Alongside plugin API development there will also have to be a consumer API for the WASM plugins to make it easier and safer for them to interact with the API. |
@amber Thanks, looks like a good start! Some comments...
I also scrapped together something here to experiment with what the high-level wrapper API might look like. All the functions there are I do feel it's best to hold off on implementing a plugin API at least until the 1.16 branch is more fleshed out. As so little functionality has been implemented, there could still be architectural changes over the next couple weeks. In the meantime, though, experimentation like this is definitely welcome! We need to test out different designs and see what works best :) |
Thanks! I was planning on looking at integrating nicely with fecs! I'll take a look at your api and namespace (and underscore ;) ) builtins. Thanks! |
Awesome! I've done some work on my implementation.
The other two changes are planned, I will probably work on switching crates tomorrow. Current issues:
(My friend also pointed out that symbols beginning with two underscores are reserved in C, might wanna take that into account) If you're curious on what plugins look like right now, here is a link to a hastebin. I can upload the plugin source to GitHub eventually, I need to get it to compile to WASM target without specifying in the build command first. |
If you read this before 18:34Z, read it again. I've massively edited it. I've implemented system registration in my fork. Additionally, I have made the testing plugin's source available on GitHub! Unfortunately, I'm not entirely happy with the implementation of system registration from WASM 😢
Otherwise I like it, and it works! Minor changes:
Another concern is the fact that WASM adds about 70 compilation units, I understand that there have been concerns about compile times in the past but it should be okay, especially if we leverage WASM to implement game logic since it would allow us to do extremely fast iteration. @caelunshun I'd like some input from you, mainly: how should plugins integrate with the "event loop" of the server, and how should I handle needing two parameters for executing plugin systems. I tried using a tuple and a struct but I wasn't able to get them to work. I have NOT switched to the |
@amber Thanks, this is looking good. I noticed that you implemented plugin systems by having a single system invoke each plugin for each stage. I think it would be preferable to have plugin systems as first-class citizens, probably by changing the systems executor to store
That seems good to me, since that's how the native codebase works as well (on the 1.16 branch). If you have concerns about that, please do share them.
It would make sense to me to intertwine the
Sounds good, that sort of high level interface is what we're looking at going into the future. I'll probably submit a bunch of commits or PRs on making this API as friendly as possible, as I have lots of ideas about what it should look like :)
Yes, we don't want any unwrapping at all in the server, unless we're absolute certain the unwrap will never panic. Feather should never panic—that's another one of the improvements in the 1.16 branch (since systems now return results).
I'm not sure what you mean by "event loop," but currently I think it's adequate just to support plugins registering systems. (I haven't implemented event handling on the 1.16 branch yet—once I do, then we can look at how an event handler API might work.)
I'm not sure what these two parameters are, could you clarify that? Overall, thanks for working on this! I'm glad to see we're getting some real implementation work going forward. |
I sent stuff in Discord, but I think I'll lay it out here as well. I believe that it is entirely safe to do this, the WASI implementation in wasmer seems to do something similar. However, I wanted you opinion before rolling with it. In the future if we need to pass more state to imported functions, we can just pass a struct. Thanks :) |
We (@Defman, @Schuwi, @amberkowalski) have been discussing how plugin dependencies should work, and we've come up with a plan. [plugin]
name = "plugin_name"
pretty_name = "Fun plugin"
version = "0.1.0"
[dependencies]
feather = "0.6.0" Implementation details (macros) have changed since we've put a few hours into trying to flesh out how we want to do this. However, the general idea is the same (and we're still using tomls). All the versions in plugins use However, other plugins as dependencies are much more complex. This is because plugins are dealt with by end-users and for their convenience and safety, there needs to be additional checks to make sure feather doesn't crash or, even worse, invoke UB. Because of this, we've decided to add additional manifests/sections to a plugin's package/toml. For example, take these two plugins. The The With the output of these two macros, feather can make sure that the struct layout on the dependency's exports is the same as what the plugin is expecting it to be, and thus can ensure type safety between plugins at runtime. This allows us to not strictly version every plugin, since as long as the interface is the same and When adding a plugin dependency, it is added to the Here is an example of a plugin exporting a function. Keep in mind that the plugin user will NOT have to write the export manually, they will just use the macros. It is extremely likely exported and imported types will be moved to their own files (exports.toml and imports.toml respectively). In the event that a plugin's imports do not match a dependency's exports, the plugin will fail to load, printing an error to the console. |
Hi @amber, Thank you, that looks good in general. I do have some feedback on where this is going in the future. For the crate structure, I think the following design makes sense:
Also, I believe it's best to maintain a clear distinction between "Quill, the plugin API" and "Feather, the server that supports Quill plugins." Since this API could be implemented by a server written in any language with WASM bindings, it makes sense to keep the two entities separate. This also has the benefit that Feather semver and Quill semver are no longer intertwined. If we release a Feather version with new features, then we can keep plugins compatible by not incrementing the Quill version. This would have the consequence that In terms of inter-plugin interaction—the only problem with your solution is that an upstream plugin can change a struct's layout without making a semver change, resulting in UB in a downstream plugin. This is a major security vulnerability. We need to put some more thought into how we ensure struct layouts are semver-correct. |
Here's my draft of the high level API (no functions are actually implemented): https://docs.rs/quill-prototype/0.0.0-prototype.1/quill_prototype/ I've started out very barebones. In particular, plugins can't add/access custom components at this time. However, I consider what's in the draft a good baseline API which we can build on in the future once we settle on a good design. |
@caelunshun Lots of changes :D. quill-internalsThis is the new name for the crate which holds logic that should only be used by feather and the quill API implementation. Below is a list of changes I've made to it.
I would like it if we could move this crate into the quill-internals::module_externsThis is a crate meant to hold safe wrappers for standard quill functions, right now it only contains a super basic (and slightly incorrect, but of course still safe) implementation for featherI've updated my branch implementing plugin support to use the new quill-internals library and types, as well as implementing the test-pluginI've updated I'm most likely going to be at work the whole time you're online unfortunately (until 8 PM PDT, 10 PM CDT). Please leave your comments here as you have been doing 😄 |
Today's update, woo. quill-internalstest-pluginUpdated to work with the latest quill-internals featherAdded support for tracking allocations in a testing branch that hasn't been pushed, it outputs
It turns out my FFI layer was prone to both UB and memory leaks, isn't that wonderful? I was deallocating at the right spots and with the correct sizes (in most cases), but the alignment is off. The rust docs say that passing layouts to the allocator for dealloc that are in any way different than the ones used to allocate is UB, and thus I was creating UB. If you're online today I'm probably going to be at work (until 8 PM PDT, 10 PM CDT). Please leave your comments here as you have been doing. (Make sure to read yesterday's) |
Hi @amber, I'm so sorry about my absence these past few days. That's totally on me, and I'll make sure I'm available over the next few weeks. I'll be on Discord for the rest of tonight and throughout the weekend, so if you want to discuss anything further I'm ready :)
It sounds reasonable to me to license
I think it even makes more sense to use the
Sure thing, I'll create a One thing—for consistency with Rust naming conventions, I think The tracking allocations look good, those should be useful for debugging. Thanks for all your work on this, and again I apologize for my absence :) |
I see that you guys are considering using wasm. I honestly don't even understand why you would twist yourself into using that. Why not use Lua? It is a very common language for plugins. |
Performance and power. WASM is way more powerful and allows us to use a sane language to develope plugins, and additionally allows any language that can compile to WASM to be used. Its also probably faster, idk. |
Luajit is blazingly fast. Probably faster than any current wasm
interpreter. Next I do not believe being indecisive about the plugin
programming language is a good idea. It has the potential to hinder the
plugin community. Lua is a good fast language that a lot of people are
familiar with. Simple plugins will be a lot quicker to write in Lua than in
rust. Lua is safe when embedded in rust. It is much better choice imo.
…On Sun, Nov 8, 2020, 12:07 AM Amber Kowalski ***@***.***> wrote:
I see that you guys are considering using wasm. I honestly don't even
understand why you would twist yourself into using that. Why not use Lua?
It is a very common language for plugins.
Performance and power. WASM is way more powerful and allows us to use a
sane language to develope plugins, and additionally allows any language
that can compile to WASM to be used.
Its also probably faster, idk.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#309 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AB6IUHQU3TKHTYUTF6EOQD3SOYRRZANCNFSM4RKLZ56A>
.
|
Our WASM is JIT'd.
We have no plans to offer first party support for anything other than Rust, but that is mentioned as a benefit because it technically is.
Potentially, but a large amount of plugins are not simple, and writing them in lua would bu more complex than in Rust.
So is WASM, except WASM has the potential to be even safer due to the simple base architecture. Whether this is true in practice, at this point, is debatable. |
How about Deno and plugins written in TypeScript/WASM ? There are multiple ways:
Using custom Minecraft ops we could make TypeScript plugins very easily, and with an ecosystem like Saurus, which uses git submodules, this would be awesome. Also, Deno develops a WASI interface https://deno.land/[email protected]/wasi, so feather plugins would be able to do native things, sandboxed. |
Heya, i'm just flying by here from that wasmtime networking issue mention, I find the project in general pretty interesting (a minecraft server in rust? hell yeah), and the initiative here as well. A few questions, though;
A more technical question; Is it possible to "expose" a function into the wasm runtime that can then be called upon? Maybe this could be used in a |
Tracking Issue: Feather Plugin System
#106 is semi-outdated but its recommended to give it a glance, #308 should be used to discuss what kinds of plugins should be developed as part of the
feather-rs
project.The idea of implementing a plugin system has been discussed more often recently, so its time to look at creating the plugin system more seriously. #106 did, however, have a good list of things that are important for the plugin system implementation. These will be mirrored here.
This list may be changed based on discussion, and it may be incomplete. The goal is to have plugins be able to do as much or nearly as much as code placed directly inside of feather can do, with as little overhead as possible. Obviously, as will be covered farther down, there will be overhead with the currently proposed methods.
Previously, there has been debate on whether to use dylibs or wasm for the plugin system, however as caelunshun says in a discord message, wasm is probably our best choice at this point due to its cross platform and sandboxing.
Feel free to add to the discussion (and suggest changes to the plan)! :)
The text was updated successfully, but these errors were encountered: