Structure

Started by Ryex, March 21, 2011, 08:13:13 pm

Previous topic - Next topic

Ryex

March 21, 2011, 08:13:13 pm Last Edit: March 22, 2011, 03:22:20 am by Ryex
Structure




Preface

I put a lot of thought and effort in to ensuring that RMPY would have an organized, stable, and easy to use structure. but this doesn't mean that it is perfect or suitable for ARC.

This first post will explain the overall structure of the current code base and will hence forth serve as a discussion topic where people can ask questions for clarification, post suggestions for modification and improvement, and in general discuss the structure.





Basic Components

The editor is divided up into three major components or groups of classes.


  • Launcher:  This part is the main application class derived from wx.App. It launches the application loads the Kernel, displays the splash screen ect.

  • Kernel: This is the CORE of the program. it consists of a group of classes that load components from plugins, organizes packages and (while this part hasn't been written yet) is intended to manage dependencies of components. It also provides and interface to access those components in an implamentation independent manner

  • Components: These are a large collections of classes and functions that operate in a modular fashion and implement a single piece of functionality, like loading game data or drawing the map so it can be edited. they can rely on other components to implement sub functionality and expose a uniform interface. the premises is that each components can be replaced individually so the extending or replacing functionality is easy.



each part is explained below.



Launcher

The launcher is fairly simple. it creates the Application loads the Kernel and all the components (core or plugin) loads the configurations (for components and other settings) and then loads the main frame components which in turns loads the rest of the program



Kernel

The kernel provides the base functionality of the entire system. it serves as an interface to load and dispatch components and a place to store objects and other values that more than one component needs access to. The kernel can register functions for callback to be run when certain events are raised, register components types, their implementations, and the packages they were loaded from. and dispatch those components with a simple call to  "get_component("TYPE")"

When a component is registered in the Kernel the structure looks like this

- static class Manager
   - property: types {}
       - key: "Type_Name" => value: object class Type
           - property: components {}
               - key: "PACKAGE_NAME_AUTHOR_VERSION" => value: object class Component
                   - property: object class or function object of component


a call to
Manager.get_component("Type_Name")
returns a Component object. either the one assigned to the types 'default' property or the first registered component. the class or function can be accessed through the components object's 'object' property



Components

there are several general types of components that shape the program

they are as follows


  • Frame Components: classes inheriting from wx.Frame. they include the main editor frame
  • Layout Components: Generic classes that server to load other components. A Frame will load one and it will load a bunch of other components to populate the Frame with controls.
  • Controls: a control is a GUI elements that the user sees and possibly interacts with. things like a scroll bar, a panel, a button, ect. a costume control would be the map display.
  • Dialogs: small frames with a single purpose. Ie. create a new project, save the current one, or import data.
  • Data Handlers: these classes serve to handle the data. they perform operations like saving data, loading data, or editing the data that is the Map ect.. the Undo/Redo action framework would be under this.




The Loading of Plugins

in the Kernel there is a class called package. the intended use of this class is to subclass it and then use the build in methods to first register any components types that the package introduces and then add the components in the package as implementations of those types. this is all done in the __init__ method of the Package subclass using the Package class functions
self.add_type()
and s
elf.add_component()
and at the end
self.register()
is called to register all the types and components in the Kernel.  in this sense a Plugin is really a set of components grouped into a package. the loading of the package is simply the creation of an instance of the Package subclass. Taking a look at the core_init.py file in the Core folder should give a good demonstration of how this is done as this file creates a Package "CORE" to load all the default components

I also have a config option in the Kernel that is used to set the default components. an ini file that uses sections for each component type and properties like author, name, package, and version to set the default. the User_defaults.ini shows this.


you can read the source for further clarification of the details of the implementation. I've documented it fairly well but if you have any questions please ask away.




I no longer keep up with posts in the forum very well. If you have a question or comment, about my work, or in general I welcome PM's. if you make a post in one of my threads and I don't reply with in a day or two feel free to PM me and point it out to me.<br /><br />DropBox, the best free file syncing service there is.<br />

ForeverZer0

I like the overall structure. Its simple, intuitive, and very functional.

I think the main make-it-or-break-it variable is going to come down to its extensibility. With the component feature, and even having the main GUI created with individual components, I would say that it is very important that we make this feature as easy as possible for others to extend themselves. Unless the editor is open source, it is going to be hard for others to know how to properly implement anything themselves since they cannot see the source code, nor how to follow it. My suggestion would be to keep all the components (the ones used to create the default editor) be open for public view. That, or create some type of template for others to follow.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Blizzard

I agree. I like how you properly divided and grouped the classes by functionality and responsibility. No objections from me.
I agree with a default template. We will likely going to have to implement a few plugins ourselves so people will have some examples they will be able to work with.

One of the simple interfaces that made sense to me was an interface with 5 methods: pre-create, post-create, pre-destroy, post-destroy and update. I pretty much used something like this in RMX-OS as well, though it's even simpler (initialize, update server, handle custom client message).
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Ryex

I added a section to the first post on the loading of plugins

Components take the form of a class or a function so the call operation differs greatly between components ie. a control has to be called with the frame it is being childed to and often components require extra data depending on what they are doing.  so the template is going to have to be flexible. so far the call signature each component type is different. so a rigid pattern like pre-create, post-create, pre-destroy, post-destroy doesn't really work well under this system as it is right now.

a control component is a subclass of wx.window at the very least and requires data like width, size, a wx.window to be childed to (or NONE if it is to be a top level parent) and creating a destruction method for windows is a bit tricky. Frame and dialog components also require some of the same information.

on the other hand things like data handlers require completely different information for creation that are as unique as the data they handle. layout components on a like note require information that is entirely dependent on what they are laying out.


Personally I think that publishing the call signature and interface for all the default Component Types would be a better approach to a template. that way if some one want to write a plugin they would write up alternatives for each component that needs replacing and write a Package class to register them all.


one thing that should be noted is that under this system components of different types but from different plugins can exist in tandem but two components can't stack. at least not by default.

a author would have to go through a great deal of trouble to compound the operations from different components and their efforts would be reliant on a way to control the order in which packages would be loaded (that at least is easily do able). basically it would be the author using the Kernel to load the components and transfer their methods into his new component and then call them. THIS is reliant on each component class implementing the pre-create, post-create, pre-destroy, post-destroy pattern with in the class.

here is a simple example oh how that would work


other_component1 = KM.get_component("TYPE", "NAME", "AUTHOR", "VERSION", "PACKAGE")
other_component2 = KM.get_component("TYPE", "NAME", "AUTHOR", "VERSION", "PACKAGE")
other_component3 = KM.get_component("TYPE", "NAME", "AUTHOR", "VERSION", "PACKAGE")

Class My_Component:

    def __init__(self):
        pass

    def pre_create(self):
        self.pre_create_other_component1 = other_component1.pre_create
        self.pre_create_other_component2 = other_component2.pre_create
        self.pre_create_other_component3 = other_component3.pre_create
        self.pre_create_other_component1()
        self.pre_create_other_component2()
        self.pre_create_other_component3()
        # do stuff for My_Component
       




as you can see the methods for the other components become methods for this component and are then called. I personally don't like this method. it feels really ineloquent so perhaps an alliterative should be discussed.
I no longer keep up with posts in the forum very well. If you have a question or comment, about my work, or in general I welcome PM's. if you make a post in one of my threads and I don't reply with in a day or two feel free to PM me and point it out to me.<br /><br />DropBox, the best free file syncing service there is.<br />