Skip to content

Module system needs to be refactored #23

@Shockfire

Description

@Shockfire

I'm not really sure that the ModuleBase class is very helpful from a design perspective. Basically, from what I understand, it looks like it gives you access to the "main" ElDewrito interface objects, but it shouldn't be doing this through inheritance. Inheritance is intended to model "is-a" relationships, but we're only using it for code reuse (in fact there's nowhere in the codebase where a ModuleBase pointer is even used). You could argue that it indicates that a class "is a" module, but the term "module" is extremely vague and just covers up the fact that "modules" have way more responsibilities than they should. The problem with this, then, is that it leads to things like ModuleConsole.cpp having over 1k lines of code in it or ModuleForge having a disgusting-looking hook function inside of it. What if another piece of code wants to trigger a Forge delete operation, for example? It shouldn't have to execute a command to do it, which is clunky and requires string manipulation. We should never have to run commands internally to accomplish something which could easily be done if we had a properly-structured codebase.

In order to maximize reusability and keep our code simple, we need to clearly separate the different layers of the system instead of combining everything into "modules":

  • Hooks should be objects which are completely isolated from the rest of the codebase (because they're nasty) and have very few dependencies. They should simply either be event sources or provide public methods which can be used to control them. Hooks should never ever have to know about the command system being present. (Though whether they should be able to access named variables is debatable.)
  • The command system should just be a layer on top of everything else. (That is, you could take away the command system and have everything still work.) Each command should only be a few lines of code long and use classes/functions lower in the hierarchy (parse arguments, call function, output result). Additionally, we should be using std::function for command function callbacks so that commands can access non-global state. Commands could be created through an "ICommandRegistrar" interface of some sort which takes care of putting commands in a namespace corresponding to the plugin name.
  • If we want to keep the variable system the way it is currently, we need to separate the variable and command systems such that the variable system is much lower in the hierarchy. We can have something like an IVariableStore which stores variable names and values, and then the command system can access that when the user requests to get/set a variable. When a variable is updated, the store would fire an event. This would decouple variable accessors from the command system.
  • Any UI code (drawing the console, etc.) needs to be completely separated from everything else so that it can easily be disabled all at once in a dedicated server environment. I've thought about possibly using some sort of 3rd-party library (e.g. librocket) for stuff like the console and chat until we get further with the embedded Chromium renderer.
  • There needs to be a class whose sole responsibility is to provide interface objects to a plugin (and to internal code as well). We can pass a pointer to it to the InitializePlugin() function and leave it up to the plugin to use it how it likes. In its simplest form, it could just have a pure virtual CreateInterface() method, but I'm not necessarily sure I like that because then you lose type safety by having to cast a void*. I'd rather see it expose a function for each type of interface that can be retrieved.

TL;DR, frankly what I'm getting at here is that we need to stop making this so monolithic that it's impossible to reuse code outside of modules. The current system still makes things way too difficult to extend easily if we want to add business logic like variable synchronization that isn't tightly coupled to the console. Granted, it's a step in the right direction compared to the original codebase, but there's still a lot that needs to be done before I'm comfortable using it.

Any thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions