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
[Feature] Architecture organization #3426
Comments
Thoughts so far? I think this is a beautiful idea @cuberite/core-team |
I agree with most of this, but...
Language has nothing to do with it. The notchian server implementation (and bukkit/spigot) support a Also, you do need to be careful about vanilla support - the actual contents of the worlds directory can't be changed since it needs to be (at least mostly) anvil-compatible. But you could put them in a subfolder without issue, methinks. |
I think I did not express myself very well there, what I was trying to say is that it would be very easy to do such thing since cuberite is a binary. |
I have mixed feelings about this. I've always wanted to have more order in the binary folder - have a separate folder for plugin configuration, separate folder for plugin data (-bases), possibly even modify the Lua API to disallow writing to the plugin "program files" folder. On the other hand, I think the current way of running "portably", from a single folder, is much better than having an installation that uses some kind of OS-specific folders. It allows me to have multiple configurations of the server, try out multiple versions in multiple folders, all without the fear of one instance overwriting the data for another instance. So I'd go with a mix - default to the portable installation, possibly with improved folder structure, and allow an installation-like operation too. There are a few questions for this, though:
Also we must not forget about plugins, they do make some assumptions about paths, we'd need new API to get the various paths and the plugins will need to start using them. |
You could maybe also do a mixture of both:
Package managers could probably accomplish this by setting some kind of environment variable? I don't know much about this to be honest. This base folder would then be split up in say |
This is something I've scratched for the portable installation. I've put the binary on root folder instead of a /bin directory because I think it should be simpler for both, the user and the coders.
Then we could have a build flag for installed mode: things could get messy if we used ENV and the variable weren't properly set at runtime. This has the downside of building two binaries but is safer IMO. As for migrating, the installed binary could have something like |
What about plugins needing their own DB files (such as the Gallery plugin's database of all areas, or LastSeen's database of last online time of players)? |
@PureTryOut |
We can do auto-detection of the folder layout based on, say, the |
madmaxoft: I think these could go somewhere like (cuberite root)/data. Please also take a look on my comment on this issue regarding database management. |
@madmaxoft @PureTryOut probably means that you install the binary system-wide but the server directory is at .local/share/cuberite I wanna drop in my comment, that it's actually also atm possible to place the binary anywhere or run it from the PATH, for example for android the binary lies in the apps internal storage (/data/data/org.cuberite.android/files) which is only accessible by the app, but the server directory is on the external storage (usually /sdcard/cuberite) but configurable. This is also needed because android prevents binary execution on /sdcard and every other directory which is world writable (which every app and the user can access) Apart from that, I don't really care how the server directory is structured |
Yes, binary install to wherever the binary normally goes ( |
Basically:
Thoughts? Too restrictive? Missing something? Do you need a hug? |
I like it. However, about the dbs: I would prefer cuberite has all databases open when it runs, of course every plugin gets its own database. When switching to mysql (for whatever reason, this can also happen during run time) the server connects to mysql, has a configurable prefix for databases and iterates over every database they have in sqlite. During iteration it means creating database on mysql, moving all content (creating tables, inserting everything) then replacing the access pointer (so plugins will use mysql without knowing from exactly this point) and then deleting the sqlite file. And yeah, plugins config files should also be managed by cuberite, this makes sure they are in a good format where you can easily provide a webadmin interface for changes during runtime. Plugins should avoid thinking that a variable hasn't changed since they looked it up in the config the last time. Ask cuberite every time you need to use some config. (this makes reload almost every time obsolete...) |
This is a bit too Unix-centric in my opinion - Windows doesn't use folder name to indicate hidden-ness, I don't quite like the dot in There's still a few things to sort out. Where in this structure would you put the following:
|
Aren't
|
|
Making it read-only would also mean we can't use the webadmin to configure plugins. |
@madmaxoft: We would then set the hidden flag on the It would be nice if the webadmin became a plugin, like I said on #3003 this would also allow plugins to show public facing pages, not just admin content. Even though
|
Where to place the various ini files? I would suggest config files regarding the server Like crafting should be read and written only by the server and a plugin can access various contents (read and write) and cuberite handles where they are located and when it's written down. This is essentially because it preserves the state of the file, not that 5 plugins try to access them at the same time, and again allow cuberite to activate changes immediately. And a general guidelines about what to consider as "data" and what as settings: data is what is generated during the process of playing, could also be stored in DATAbases most of the time. |
@Cl1608Ho the ones used as settings should go on |
Yeah I updated my post. Settings (also for plugins) should all be handled by cuberite and plugins get an interface, whereas data is either always stored in a database (why not) or to a file. |
I like where you are going with this. Just read your edit: some find it complex to deal with databases, so I guess it's nice to give them the option of using files. |
Uhm actually I didn't mean that. I meant it to be a bit more high level, a plugin asks cuberite to store data and cuberite has the database implemented, lets say you can configure cuberite to either use files for the data backend or sqlite or mysql...... And actually all work with files is done through asking cuberite to do it Edit: there should be command line options which data backend to use and for mysql eg user and password.... Or For passwords the option to provide them via environment variables (cause options can be seen by any user on the system, an environment variable not) |
I see. I think the user should be able to choose the driver for each plugin then, with sqlite being the default storage for everything. And also being able to change the default storage for everything at once (a key under cuberite's settings called |
I don't think the discussion of the underlying storage is necessary for this issue. |
Better drop the |
Any more thoughts about this? Or should we move this discussion to the forum? |
I have changed my opinion a bit.
The cleanest way to modify these, which I don’t think many do manually, would be by loading a plugin (bonus points for not leaving left overs behind). If one still wants to change these manually, then they should create a folder called |
Whoops, I forgot that one! |
I would rather go for readonly plugins similar to readonly server config (readonly default plus files overwriting settings) because that makes updating cuberite easier. At the moment I don't even check for updates of the Server directory for the Android app because any time you re-download you would overwrite the past settings completely. There should be a way to keep the settings while to just unzipping an update on top of an existing folder (and overwriting existing stuff) |
@Cl1608Ho That's fixing the effect, rather than fixing the cause. |
Right, but we need to break older plugins so devs update them with the new settings and data locations :P |
What is going on with this issue? I think this is a very serious and important topic @cuberite/core-team ? |
I like the idea of this, I would rather install Cuberite globally onto my server, can I suggest running a single binary as a user? Maybe a Right now creating an init script/systemd script is a nightmare, I was required to switch into the user to then run Cuberite. |
This is neither serious nor important. Just a nice bonus. |
I think one could write a simple Linux-specific wrapper script for achieving this.
|
If we make sure Cuberite respects current directory, then for a "portable self contained config, system-wide binary" mode, one could do this:
|
This is your opinion. In my opinion this is as serious as anything else we do for this project. |
Okay, so I sketched out a sample implementation at https://github.com/lkolbly/MCServer/tree/architectureChanges. Basically, everything is grouped into a category, and then there's a class (https://github.com/lkolbly/MCServer/blob/architectureChanges/src/FileLayout.h) which defines paths for each category. It'll auto-detect whether you're using the new file format or the legacy file format. Here are the categories and descriptions I've come up with so far (largely based on the conversation here so far):
Thoughts? |
Sorry for the delay, I wanted to see it running but I couldn’t get it to compile (using macOS), so I kind of forgot to reply. First, thanks for taking a look at this issue. Your code seems fine to me, but I’m no xoft 😝
Wondering if that could be extended in the future to allow vanilla/spigot migrations too.
As I read this issue again, this got me thinking: couldn’t the prefabs folder be moved to each world’s folder? Then I started thinking about the plugins dependencies (a world can have), to finally realize I was reinventing Minecraft PE behaviour packs/addon packs. I wonder if that’s an idea to be explored further. In Cuberite context that would mean something like per-world crafting, prefabs, plugins, etc. and the concept that there’s a “vanilla” behaviour pack bundled into the binary itself. A different issue perhaps? Thoughts? |
I wouldn't exactly ship everything with the World, but naming it would be good. So that the server outputs a warning if it loads a map without a plugin that's mentioned inside the map. I would really really vote for two distinct config files, one default and one for overriding. This makes automated updating far easier. |
I was going to take a look at that, but then I merged master and now it doesn't compile for me either. I guess I should go fix it before it gets worse.
To some extent, probably, although there would have to be further work done to auto-convert the config files as well. The auto-detector could be expanded so that it detects not just what the folder layout is, but whether the config files are yaml or ini (and cRoot could select an appropriate parser based on that).
Some things would probably have to stay per-server (plugins that operate across worlds, for instance). With things like BungeeCord, operationally it may make more sense to just run multiple servers when you want that functionality. In my mind worlds are somewhat ephemeral - they could be created and destroyed programatically (and in fact I have a branch that does exactly that: https://github.com/lkolbly/MCServer/tree/dynamicWorlds) (imagine, if you will, a HungerGames plugin that creates a new world for each game). Crafting, prefabs, plugins, etc. are not ephemeral, they're setup by the server operator manually (or by script). I'm definitely no authority on the subject, though, I'd be curious what others think.
Yes, but still a good issue.
...and if resource packs were ever implemented it might be nifty to have worlds that specified resource pack dependencies. EDIT: Fixed the quotes. |
World-scoped plugins would make sense for instances where you want to host a survival, a creative and a minigame on the same server (though cuberite is really lean and you might want to get yourself a bungeecord network with multiple cuberite instances…). I think the first step should be rearrange the current, portable directory structure (as @lkolbly did), then add overrides for (almost) everything, varying scopes:
(the order matters) Do note I’m calling anything that overrides the default behaviour, be it a plugin, a setting or a prefab, an override. And of course some of these scopes apply to one but not another: I don’t think someone would want to store a plugin inside an environment variable, but I wouldn’t use the I think this kind of overlaps with the “Ultimate solution”, but instead of trying to read different config file syntax, the class would read stuff from above locations. Adding other backends should not be too hard (like, I don’t know, docker secrets to store the webadmin password). Config could be identified internally like:
In order to make setting cli/env vars easier, File overrides (like the crafting recipes) would need special treatment. There needs to be ways to wipe/turn off inherited files/values. Having the ability to append to would be nice too. Perhaps this should work like systemd unit files? Binary overrides (like the favicon.png and prefabs) only need ways to be deleted (maybe using a setting) and to be completely overridden. So much for defining where to put things in a minecraft server 🤣 |
@lkolbly I didn’t see your comment before I finished mine, but luckily it seem to answer most of what you pointed out. But:
It could work perfectly fine as an world-scoped plugin. The fact that the plugin wipes region files and changes the seed every now and then doesn’t mean it can’t live the same folder the world does. You have a good point about maps which need server-wide plugins to function though. |
I'd prefer not to have such long config names. Also, World-specific plugins sounds like an big architectural change in the code. |
I think internal config could be aliased to much more shorter, but I don’t see how third party (read plugin config) could be different (so that no clashing occurs). I had in mind a lua table holding the whole server config, that’s why I wen’t for this kind of reverse DNS thing. There might be better solutions though. World-specific plugins could be temporary implemented as an alternative plugin loading directory (just like the system wide one), just for the sake of finishing this issue. |
It's not just the plugin loading. Since Cuberite started supporting plugins there has always been one PluginManager instance. Now suddenly each world would require it's own PluginManager. This could give allot of unforeseen side-effects. That doesn't mean plugin loading isn't also an issue. Where do you keep the plugins? Do we still have one plugin folder but in the config we assign plugins to different worlds or does every world get it's own Plugin folder? The latter would be rather wasteful as updating a plugin would require updating the plugin in every world. However the first one could have an issue where a plugin could conflict with itself as it's trying to load some files two or more times. Think of a plugin that uses a SQLite database. Once the plugin is loaded a second time for a different world it tries to load the database again even though it might be locked now. Moving all the config over to Lua has the downside that we can't add comments to the config file. That was one of the reasons we didn't support YAML as configuration language: #2560 |
I don’t get it. How is this any different than having a plugin elsewhere symlinked to the current plugin folder?
If you want a plugin in “every world” then it only makes sense to add it to one of the server-wide locations described above, not in every world’s folder.
If you get the same plugin multiple times (ex: in system wide, server wide and world wide locations), then the lowest in the chain should be the only one loaded (just like you would do with a binary file like the
I did not state that. Config files can be whatever (just like what’s described on xoft’s comment on #2560 ), I just said how I think that could be implemented: overriding settings from “higher up” and some additional backends, like runtime flags and env vars. Then webadmin would only write to the files (ini, nbt, sql, yml, whatever) the bits that are indeed different from the settings higher in the chain. Another example: you could set world settings from:
Although I personally would store all world settings on NBT or I said it was inspired by the way lua tables work because I don’t know what would be the C++ equivalent to such data structure. EDIT:
The same applies if you wanted to set those from cli. But if you wanted to set the same setting but using the plugin’s setting file, you could’ve simply used So, (for the purpose of making updating cuberite not a nightmare) some overrides are way more important than others. So I think we could focus on those instead, possibly breaking this issue in two. What do you guys think? |
Too long, don't want to read: The first three points are all "if you have per-world plugins, how does aliasing work?" (I'm coming from a standpoint that each per-world plugin gets its own global state) and the last point is "Unified global configuration seems cool! I haven't thought about it very hard yet, though."
How would a world-specific plugin interact with the server-wide plugin, and vice-versa? If there are multiple world-specific plugins "HungerGames" (for multiple worlds), and a server-wide plugin calls DoWithPlugin("HungerGames"), what happens?
Not necessarily. I can imagine someone lazy (i.e. me) making a plugin that uses global variables to store e.g. game status (I can imagine this because, guess what, this is unfortunately the architecture of my BedWars plugin). This is totally acceptable for a per-world plugin - if you want multiple BedWars worlds, just stick the BedWars plugin in each world, and each one gets its own global state. A server operator may not have the time or the expertise to re-architect my poorly written plugin if they want a whole bunch of BedWars worlds.
If per-world plugins are lower in the chain than per-server and per-system, this could be difficult to detect, and even impossible if runtime world creation is ever implemented, since it requires checking every world for plugins before loading any plugin. And then, if a server operator has a world that uses e.g. ProtectionAreas, but then they decide to install ProtectionAreas for the whole server and forget to remove it from the world, they may be confused. You get the same problem if the chain is reversed. For this reason, I think you'd have to load every plugin, and deal with aliasing somehow.
I like! That said, I think you could get away with a terser DNS-style configurations, maybe just |
But imo the easiest thing wold be to make a setting in the world I I that allows worlds to specify plugins they depend on. No need to ship them with the world. Plus, you could specify which plugins should be executed (maybe all can be loaded at server startup but for example hooks are only executed for a certain world |
I like the idea of allowing worlds to list plugin dependencies, though I think for backward compatibility the default would have to be that all plugins interact with the world. |
I don't think we should even consider world-restrained plugins. All plugins should be written in such a way that they work in worlds where the admin has configured them to work, and not touch the other worlds. If you're too lazy to make your plugin correct, your plugin isn't worth running on such a wonderful piece of software like Cuberite is today :) |
I have to admit it would be a case of “premature optimisation”. Because if Cuberite ever adds support for PE (or someone hacks a compatibility layer on top of forge to bring PE features or…), then, correct me if I’m wrong, Cuberite will need to support per-world “add-on packs”. So we could agree to call that a dead subject until someone starts implementing PE support or something. |
Yeah, you won't want to run any plugins I write. :-) |
I fixed the architectureChanges branch, now it compiles (don't know about macOS, but clang will compile it for me). |
News on this? |
Ideally, binaries, configuration and user data should not be mixed together. Currently, cuberite, and most of the minecraft servers, use a single folder for everything, without much organization (ie. there's no "worlds" folder, if a server runs many worlds the root directory becomes a big mess; plugins and plugins data are stored together; etc.).
Since this is (thankfully) not a java application but instead a native, first class citizen application, it should be easy to implement it in a more organized way.
This could be useful for:
I see three ways of doing this:
There should also be a migration guide/script, for those wanting to switch between these two models.
The text was updated successfully, but these errors were encountered: