]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/synapse/doc/design.txt
more eol-style
[xonotic/netradiant.git] / libs / synapse / doc / design.txt
index 3e8fd317c7e9986068ed28dae78e8606360ce044..e3da4dc316e0e1fce25e7836792c7dad82464e82 100644 (file)
-synapse code design documentation\r
-=================================\r
-\r
-Objective:\r
-----------\r
-\r
-Provide a simple cross platform layer to use dynamically loaded code\r
-inside a core application. Portability intended to win32 / linux / MacOS (?)\r
-\r
-Main features are:\r
-\r
-- designed for single process only, no remote clients, no asynchronous processes\r
-- a client/server architecture, based on configuration files: a main binary,\r
-  loading a set of shared objects\r
-  \r
-Constraints:\r
-------------\r
-\r
-- large existing plugin code in Radiant!\r
-  must be compatible with minimal changes, specially for plugins (i.e. clients)\r
-\r
-- make things as much transparent as possible\r
-  (ideally, no real difference between a static linkage and dynamic linkage,\r
-  cf usage of #define macros to wrap a function call onto a code pointer)\r
-  \r
-Features:\r
----------\r
-\r
-Gather as much generic code as possible in a static .lib with minimal dependencies\r
-(only dependency should be configuration files parser)\r
-\r
-NOTE: current effective dependency is STL / glib / xml\r
-\r
-Main executable implemented as a server, all others as clients. What has to\r
-be done for a server / what has to be done for a client needs to be documented.\r
-Provide as much scripts and tools and guidelines as needed (scripted generation of\r
-some .h files?)\r
-\r
-Proposed implementation:\r
-------------------------\r
-\r
-- have linux/ and win32/ subdirectories with OS-specific implementations\r
-(such as dynamically loading shared objects, and doing the initial query?)\r
-\r
-- reduce the API of a client to the minimum: one exported function?\r
-provide a squeleton to make new clients easily?\r
-\r
-Server use case:\r
-1) build information about location of the modules (from code and config files)\r
-2) load all modules and query information about their APIs\r
-NOTE: could read the APIs from some XML description files instead of\r
-querying it from the modules?\r
-3) build information about the required function tables\r
-i.e.: setup a list with the function tables to be filled in, and what they\r
-need to be filled in with.\r
-4) resolve the function table\r
-NOTE: is this iterative? will some plugins request more APIs as they get filled\r
-up?\r
-NOTE: do we have optional tables?\r
-5) unload unreferences modules\r
-NOTE: we don't expect to be unloading a LOT of modules, otherwise we would\r
-setup a solution that allows exploring of the APIs a given module provides\r
-from a file description. Or you could 'cache' that (md5-checksum the file, and \r
-maintain an XML list).\r
-\r
-Client use case:\r
-1) dynamically loaded\r
-2) prompted for the interfaces it provides\r
-2) prompted for the interfaces it requires\r
-3) either unloaded, or told what interfaces have been filled in\r
-\r
-The client module exports an Synapse_EnumerateInterfaces entry point\r
-This returns an ISynapseClient, which lists what the plugin provides, and what it requires\r
-\r
-The APIs:\r
-\r
-An interface is a function table, GUID / major string / minor string\r
-GUID is a shortcut to reference a major string (i.e. the human readable thing)\r
-the GUID / major string is unique for a given interface\r
-minor string is used to reference a particular version of an API\r
-  (for instance when talking about image loading functionality, tga and jpg etc.)\r
-  \r
-The GUID scheme is handy because it provides easy tests. They are not strictly\r
-necessary but we will probably want to keep them. Should we extend to GUIDs\r
-for minor too?\r
-\r
-Roadmap:\r
---------\r
-\r
-Need to convert the core (as server) and the required modules. Will have\r
-clearer view of what's to be done along the way.\r
-\r
-Implementation design:\r
-----------------------\r
-\r
-There is a client and server side to synapse. Typically server is in Radiant or q3map,\r
-client is in any module. For implementation, we have one server class and one client class.\r
-It would be possible to have two seperate libraries, synapse-client and synapse-server. But \r
-that only brings down the statically linked stuff to make things more complicated build-sysem\r
-wise.\r
-\r
-Initial implementation has been using isynapse.h and synapse.h, to provide a pure virtual\r
-base class for server and client. But that doesn't bring any major functionality, it's easier\r
-if both sides see the full API of the client and server classes.\r
-\r
-A side problem is the diagnostic printing functionality. For easy debugging we require that \r
-the synapse code can have access to a Sys_Printf or similar function at all times (that is for \r
-client and server implementation). On client we will pipe through the main API to the server \r
-as soon as we can in most cases. Using Sys_Printf would bring us to a dead end situation, since\r
-when synapse is used as the server, the main code implements it's own Sys_Printf stuff.\r
-Instead we introduce a local Syn_Printf implementation, which can be overriden in the server\r
-to point to the appropriate print functions.\r
-\r
-Runtime config:\r
----------------\r
-\r
-Something that has not been looked upon a lot yet, runtime configuration. What interfaces\r
-are loaded etc. Ideally, from an XML config file. A client explicitely requests the\r
-server to load all the interfaces it requires (in this case, the client is radiant or\r
-q3map).\r
-\r
-Plugins are somewhat out of the 'required interfaces' frame, since they are loaded \r
-whenever they are found. It is possible however that some plugins would not want to be \r
-loaded in if the game doesn't match etc. in case they would need to access the global \r
-config?\r
-\r
-In most cases a given API is only required once for editor functionality. (VFS for \r
-instance), so our #define strategy for easy mapping of the functions should still work.\r
-\r
-Version checks, reference counting:\r
-------------------------------------\r
-\r
-Need version checking at several levels. A version string (major/minor) on the main API\r
-entry point (straight in the exported function to save as much as possible for \r
-compatibility). For individual APIs, we have been feeding the struct size into the first\r
-int of the struct so far, and it has worked very well.\r
-\r
-Reference counting: we introduced class based APIs to solve the ref counting issues,\r
-which are not easy to solve on C function tables. That problem would arise in plugin\r
-situations where we might want to 'reload' or 'unload' some plugins. The server could\r
-keep track of the ref count.\r
-\r
-Caching?\r
---------\r
-\r
-We are going to load every shared object we find and query it for it's interfaces. Then\r
-we will unload the stuff we don't want. This is going to slow down the startup process.\r
-We could extract the API information in a cache to avoid the loading step.\r
-\r
-Interfaces with multiple minors against I* objects?\r
----------------------------------------------------\r
-\r
-Looking at the iimage.h API, why not having instead something that enumerates C++ objects \r
-directly? Mainly because we want to be able to spread several minors accross multiple modules\r
-and still use them together. And straight laid out function tables in C structs are only\r
-one indirection when the table is static.\r
-\r
-This raises a broader topic, instead of requesting APIs, we could request objects directly.\r
-Would that be of any use?\r
-\r
-Loading interfaces / resolving interdependencies strategy\r
----------------------------------------------------------\r
-\r
-Some notes about how we load the modules and resolve interdependencies:\r
-\r
-We want to avoid requesting a module for an API it provides before all the APIs it requires\r
-have been filled in (mostly stability concerns, a module may be doing whatever internally \r
-when we request something from it). The exception being the module we are trying to resolve\r
-for (since we need a start point for resolution). But in all likelyness we resolve for radiant\r
-or q3map for instance.\r
-\r
-With this approach, it is possible that some situations could not be resolved, for instance:\r
-Radiant\r
-  requires A\r
-  provides B\r
-module 1\r
-  requires C\r
-  provides A\r
-module 2\r
-  requires A\r
-  provides C\r
-if we start by resolving Radiant, we will get stuck\r
-if we are ready to ask module to provide the API even though the required is not meant, it would work\r
-but that kind of situation is very unlikely, so sticking to safer strategy\r
-  \r
-Configuration\r
--------------\r
-\r
-the config info needs to go down to the clients too\r
+synapse code design documentation
+=================================
+
+Objective:
+----------
+
+Provide a simple cross platform layer to use dynamically loaded code
+inside a core application. Portability intended to win32 / linux / MacOS (?)
+
+Main features are:
+
+- designed for single process only, no remote clients, no asynchronous processes
+- a client/server architecture, based on configuration files: a main binary,
+  loading a set of shared objects
+  
+Constraints:
+------------
+
+- large existing plugin code in Radiant!
+  must be compatible with minimal changes, specially for plugins (i.e. clients)
+
+- make things as much transparent as possible
+  (ideally, no real difference between a static linkage and dynamic linkage,
+  cf usage of #define macros to wrap a function call onto a code pointer)
+  
+Features:
+---------
+
+Gather as much generic code as possible in a static .lib with minimal dependencies
+(only dependency should be configuration files parser)
+
+NOTE: current effective dependency is STL / glib / xml
+
+Main executable implemented as a server, all others as clients. What has to
+be done for a server / what has to be done for a client needs to be documented.
+Provide as much scripts and tools and guidelines as needed (scripted generation of
+some .h files?)
+
+Proposed implementation:
+------------------------
+
+- have linux/ and win32/ subdirectories with OS-specific implementations
+(such as dynamically loading shared objects, and doing the initial query?)
+
+- reduce the API of a client to the minimum: one exported function?
+provide a squeleton to make new clients easily?
+
+Server use case:
+1) build information about location of the modules (from code and config files)
+2) load all modules and query information about their APIs
+NOTE: could read the APIs from some XML description files instead of
+querying it from the modules?
+3) build information about the required function tables
+i.e.: setup a list with the function tables to be filled in, and what they
+need to be filled in with.
+4) resolve the function table
+NOTE: is this iterative? will some plugins request more APIs as they get filled
+up?
+NOTE: do we have optional tables?
+5) unload unreferences modules
+NOTE: we don't expect to be unloading a LOT of modules, otherwise we would
+setup a solution that allows exploring of the APIs a given module provides
+from a file description. Or you could 'cache' that (md5-checksum the file, and 
+maintain an XML list).
+
+Client use case:
+1) dynamically loaded
+2) prompted for the interfaces it provides
+2) prompted for the interfaces it requires
+3) either unloaded, or told what interfaces have been filled in
+
+The client module exports an Synapse_EnumerateInterfaces entry point
+This returns an ISynapseClient, which lists what the plugin provides, and what it requires
+
+The APIs:
+
+An interface is a function table, GUID / major string / minor string
+GUID is a shortcut to reference a major string (i.e. the human readable thing)
+the GUID / major string is unique for a given interface
+minor string is used to reference a particular version of an API
+  (for instance when talking about image loading functionality, tga and jpg etc.)
+  
+The GUID scheme is handy because it provides easy tests. They are not strictly
+necessary but we will probably want to keep them. Should we extend to GUIDs
+for minor too?
+
+Roadmap:
+--------
+
+Need to convert the core (as server) and the required modules. Will have
+clearer view of what's to be done along the way.
+
+Implementation design:
+----------------------
+
+There is a client and server side to synapse. Typically server is in Radiant or q3map,
+client is in any module. For implementation, we have one server class and one client class.
+It would be possible to have two seperate libraries, synapse-client and synapse-server. But 
+that only brings down the statically linked stuff to make things more complicated build-sysem
+wise.
+
+Initial implementation has been using isynapse.h and synapse.h, to provide a pure virtual
+base class for server and client. But that doesn't bring any major functionality, it's easier
+if both sides see the full API of the client and server classes.
+
+A side problem is the diagnostic printing functionality. For easy debugging we require that 
+the synapse code can have access to a Sys_Printf or similar function at all times (that is for 
+client and server implementation). On client we will pipe through the main API to the server 
+as soon as we can in most cases. Using Sys_Printf would bring us to a dead end situation, since
+when synapse is used as the server, the main code implements it's own Sys_Printf stuff.
+Instead we introduce a local Syn_Printf implementation, which can be overriden in the server
+to point to the appropriate print functions.
+
+Runtime config:
+---------------
+
+Something that has not been looked upon a lot yet, runtime configuration. What interfaces
+are loaded etc. Ideally, from an XML config file. A client explicitely requests the
+server to load all the interfaces it requires (in this case, the client is radiant or
+q3map).
+
+Plugins are somewhat out of the 'required interfaces' frame, since they are loaded 
+whenever they are found. It is possible however that some plugins would not want to be 
+loaded in if the game doesn't match etc. in case they would need to access the global 
+config?
+
+In most cases a given API is only required once for editor functionality. (VFS for 
+instance), so our #define strategy for easy mapping of the functions should still work.
+
+Version checks, reference counting:
+------------------------------------
+
+Need version checking at several levels. A version string (major/minor) on the main API
+entry point (straight in the exported function to save as much as possible for 
+compatibility). For individual APIs, we have been feeding the struct size into the first
+int of the struct so far, and it has worked very well.
+
+Reference counting: we introduced class based APIs to solve the ref counting issues,
+which are not easy to solve on C function tables. That problem would arise in plugin
+situations where we might want to 'reload' or 'unload' some plugins. The server could
+keep track of the ref count.
+
+Caching?
+--------
+
+We are going to load every shared object we find and query it for it's interfaces. Then
+we will unload the stuff we don't want. This is going to slow down the startup process.
+We could extract the API information in a cache to avoid the loading step.
+
+Interfaces with multiple minors against I* objects?
+---------------------------------------------------
+
+Looking at the iimage.h API, why not having instead something that enumerates C++ objects 
+directly? Mainly because we want to be able to spread several minors accross multiple modules
+and still use them together. And straight laid out function tables in C structs are only
+one indirection when the table is static.
+
+This raises a broader topic, instead of requesting APIs, we could request objects directly.
+Would that be of any use?
+
+Loading interfaces / resolving interdependencies strategy
+---------------------------------------------------------
+
+Some notes about how we load the modules and resolve interdependencies:
+
+We want to avoid requesting a module for an API it provides before all the APIs it requires
+have been filled in (mostly stability concerns, a module may be doing whatever internally 
+when we request something from it). The exception being the module we are trying to resolve
+for (since we need a start point for resolution). But in all likelyness we resolve for radiant
+or q3map for instance.
+
+With this approach, it is possible that some situations could not be resolved, for instance:
+Radiant
+  requires A
+  provides B
+module 1
+  requires C
+  provides A
+module 2
+  requires A
+  provides C
+if we start by resolving Radiant, we will get stuck
+if we are ready to ask module to provide the API even though the required is not meant, it would work
+but that kind of situation is very unlikely, so sticking to safer strategy
+  
+Configuration
+-------------
+
+the config info needs to go down to the clients too
 for instance, mapxml loaded for q3map or radiant, doesn't rely on the same major?
\ No newline at end of file