changeset 18:56a42ec95024

Changes to Init and logging. Moved an Init function; hence events now depends on Init rather than vice-versa. Paths and logging set up by Init's static this(). Logging location set at compile time. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Tue, 18 Mar 2008 17:51:52 +0000
parents 5f90774ea1ef
children db0b48f02b69
files codeDoc/imports.txt codeDoc/jobs.txt codeDoc/other_projects.txt doc/Readme.txt mde/Init.d mde/events.d mde/global.d mde/mde.d mde/options.d mde/resource/paths.d
diffstat 10 files changed, 363 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/codeDoc/imports.txt	Tue Mar 18 17:51:52 2008 +0000
@@ -0,0 +1,170 @@
+Import relationships:
+(only imports from mde, to show inter-code relationships)
+As of commit 2aacda05d942e74cdeefe952e701be7b5d3342d6, Sat Mar 15 15:14:25 2008 +0000
+As of 18th March, updated but index/letters no longer current.
+
+Index (n) is such that it is greater than that of any module importing the module.
+Additionally, each module importing nothing is given a unique letter. Then each module depending (directly or indirectly) on this is given this letter.
+
+Lines:
+->  x
+<-  y
+means this imports x, and y imports this.
+
+mde.events ():
+->  mde.exception
+->  mde.global
+->  mde.Init
+->  mde.input.input
+->  mde.scheduler
+<-  mde.mde
+
+mde.exception (12e):
+<-  mde.events
+<-  mde.i18n.I18nTranslation
+<-  mde.Init
+<-  mde.input.exception
+<-  mde.mde
+<-  mde.mergetag.exception
+
+mde.global (4):cde
+->  mde.input.input
+<-  mde.events
+<-  mde.mde
+
+mde.i18n.I18nTranslation (1):cde
+->  mde.exception
+->  mde.mergetag.DataSet
+->  mde.mergetag.exception
+->  mde.mergetag.Reader
+->  mde.options
+->  mde.resource.paths
+<-  mde.mde
+
+mde.Init ():
+->  mde.exception
+->  mde.options
+->  mde.resource.paths
+<-  mde.events
+<-  mde.mde
+<-  mde.SDL
+
+mde.input.config (6):cde
+->  mde.input.exception
+->  mde.mergetag.Reader
+->  mde.resource.paths
+<-  mde.input.input
+
+mde.input.exception (7):e
+->  mde.exception
+<-  mde.input.config
+<-  mde.input.input
+
+mde.input.input (5):cde
+->  mde.input.config
+->  mde.input.exception
+<-  mde.events
+<-  mde.global
+<-  mde.mde
+
+mde.input.joystick (2a):
+<-  mde.SDL
+
+mde.mde (0):abcde
+->  mde.events
+->  mde.exception
+->  mde.global
+->  mde.i18n.I18nTranslation
+->  mde.Init
+->  mde.input.input
+->  mde.scheduler
+->  mde.SDL (not a dependancy)
+
+mde.mergetag.DataSet (9):de
+->  mde.mergetag.DefaultData
+->  mde.mergetag.exception
+<-  mde.i18n.I18nTranslation
+<-  mde.mergetag.iface.IReader
+<-  mde.mergetag.iface.IWriter
+<-  mde.mergetag.Reader
+<-  mde.mergetag.Writer
+<-  mde.options
+<-  mde.resource.paths
+
+mde.mergetag.DefaultData (10):de
+->  mde.mergetag.exception
+->  mde.mergetag.iface.IDataSection
+<-  mde.mergetag.DataSet
+<-  mde.mergetag.Reader
+
+mde.mergetag.exception (11):e
+->  mde.exception
+<-  mde.i18n.I18nTranslation
+<-  mde.mergetag.DataSet
+<-  mde.mergetag.DefaultData
+<-  mde.mergetag.Reader
+<-  mde.mergetag.Writer
+<-  mde.options
+<-  mde.resource.paths
+
+mde.mergetag.iface.IDataSection (11d):
+<-  mde.mergetag.DefaultData
+
+mde.mergetag.iface.IReader (9):de
+->  mde.mergetag.DataSet
+<-  mde.mergetag.Reader
+
+mde.mergetag.iface.IWriter (9):de
+->  mde.mergetag.DataSet
+<-  mde.mergetag.Writer
+
+mde.mergetag.internal (9c):
+<-  mde.mergetag.Reader
+<-  mde.mergetag.Writer
+
+mde.mergetag.Reader (8):cde
+->  mde.mergetag.DataSet
+->  mde.mergetag.DefaultData
+->  mde.mergetag.exception
+->  mde.mergetag.iface.IReader
+->  mde.mergetag.internal
+<-  mde.i18n.I18nTranslation
+<-  mde.input.config
+<-  mde.options
+<-  mde.resource.paths
+
+mde.mergetag.Writer (8):cde
+->  mde.mergetag.DataSet
+->  mde.mergetag.exception
+->  mde.mergetag.iface.IWriter
+->  mde.mergetag.internal
+<-  mde.options
+<-  mde.resource.paths
+
+mde.options (3):cde
+->  mde.mergetag.DataSet
+->  mde.mergetag.exception
+->  mde.mergetag.Reader
+->  mde.mergetag.Writer
+->  mde.resource.paths
+<-  mde.i18n.I18nTranslation
+<-  mde.Init
+
+mde.resource.paths (7):cde
+->  mde.mergetag.DataSet
+->  mde.mergetag.exception
+->  mde.mergetag.Reader
+->  mde.mergetag.Writer
+<-  mde.i18n.I18nTranslation
+<-  mde.Init
+<-  mde.input.config
+<-  mde.options
+
+mde.scheduler (4b):
+<-  mde.events
+<-  mde.mde
+
+mde.SDL (1):abcde
+->  mde.Init
+->  mde.input.joystick
+<-  mde.mde
--- a/codeDoc/jobs.txt	Sat Mar 15 15:14:25 2008 +0000
+++ b/codeDoc/jobs.txt	Tue Mar 18 17:51:52 2008 +0000
@@ -42,3 +42,5 @@
 
 
 Done (for git log message):
+Moved an Init function; hence events now depends on Init rather than vice-versa.
+Paths and logging set up by Init's static this(). Logging location set at compile time.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/codeDoc/other_projects.txt	Tue Mar 18 17:51:52 2008 +0000
@@ -0,0 +1,32 @@
+What this is: a list of projects (mostly from dsource.org) which may interest me, and some I've looked at and realised don't.
+What I'm interested in:
+1:  a platform independant windowing & event library, like SDL or GLFW
+2:  a graphics engine (or part thereof)
+
+
+Possible projects to use/cooperate with:
+
+GLFW        http://glfw.sourceforge.net/                    replacement for SDL; details on website look good
+Schooner    http://www.dsource.org/projects/schooner        GLFW in D + freetype font library (definitely take a better look; but is phobos based and needs a new build method)
+
+Odin's eye  http://www.dsource.org/projects/odinseye        3d engine; ~16000 lines. Lots of classes, many of which don't do a lot. Not sure, possibly just take any code I want.
+
+
+Projects I'm less interested in:
+
+http://www.dsource.org/projects/universal       some kind of programming framework, only 448 lines of code, long untouched
+
+gefuege     http://www.dsource.org/projects/gefuege         game engine; ~7500 lines, don't like the coding
+Torus Trooper (tt)  (URL?)                                  simple game; not very generic coding
+
+http://www.dsource.org/projects/l8night         win32-based GUI toolkit
+http://www.dsource.org/projects/dwt             win32/GTK (?) based GUI toolkit
+http://www.dsource.org/projects/cursestui       win32-based GUI toolkit
+http://www.dsource.org/projects/gtkd            GTK wrapper
+http://www.dsource.org/projects/dxul            abandoned; no source
+
+Other:
+
+Sinbad      http://www.dsource.org/projects/sinbad          reimplementation of Ogre in D (untouched for a long time; only appears to contain the function/class prototypes and very little actual code)
+
+Element     http://www.dsource.org/projects/element         replacement for SDL + windowing toolkit (alpha); cool demo but no recent activity & source deleted from repo 25/1/08 (before more recent changes): now dead?
--- a/doc/Readme.txt	Sat Mar 15 15:14:25 2008 +0000
+++ b/doc/Readme.txt	Tue Mar 18 17:51:52 2008 +0000
@@ -1,12 +1,11 @@
 Copyright © 2007-2008 Diggory Hardy
 License: GNU General Public License version 2 (see doc/License.txt)
 
-
-There is no proper readme for mde yet.
+Some brief info:
 
 Platforms:
 I work on mde using linux and perform most testing on this platform.
-I have done a little testing on Windows, but currently is highly likely there may be problems on this platform.
+I have done a little testing on Windows, but currently it's highly likely there may be problems on this platform.
 
 Build instructions:
 dsss build
@@ -23,3 +22,38 @@
 Walter Bright and Digital Mars for D and DMD.
 The tango team for Tango.
 
+
+
+--- Goal ---
+
+Mde is an attempt to build a game engine.
+
+It is my second serious attempt at a game engine, following on from ge118 (http://diggory.hardy.googlepages.com/ge118). GE118 was written in C++, starting from some basic OpenGL and SDL tests and some of my early ideas about implementing an engine, and was heavily revised over time as I learned more about programming.
+
+Mde − a Modular D (game-oriented) Engine − was started after I discovered D, and decided I wanted to make a clean start. The acronym starts "modular" because, from experience working on ge118, I had discovered that tightly-related module dependancies (and worse, circular dependancies) caused many difficulties while programming and prevented good designs of components; thus I wanted to separate each part of the project in order to enable better design and facilitate developing a replacement for a part of the program (in case someone would wish to do so).
+
+It is not a library, since:
+a)  IMO, different games using the same engine should, where possible (which should be the case with an open source engine) share the same executable and only use config/command line arguments to select the different game.
+b)  The source is tied together so tightly and is only designed to be used in one way; thus there seems little point organising it as a library and example app.
+Anyway, anyone wanting to use it as a library need only remove mde/mde.d and compile it.
+
+
+--- FAQ (or just a few questions) ---
+
++ How many lines are there?
+# wc -l $(find . -name "*.d")
+As of last count, that was 3908.
+
++ Why is the code so DENSE?
+That's just my coding style (I still mostly stick to the D style guide). I like code to look neat, but I don't like spacing it out a lot because then it takes more room (more scrolling) and IMO it becomes harder to visualise what goes on.
+
++ What toolkits/external libraries are used?
+    tango
+    tango.scrapple
+    SDL (possibly will be replaced)
+
++ What libraries are planned to be used?
+    OpenGL (of course...)
+    OpenAL (most likely; however audio support is a LONG way off)
+    Schooner: GLD & fonts (or conversions to tango) ?
+    GLFW ?
--- a/mde/Init.d	Sat Mar 15 15:14:25 2008 +0000
+++ b/mde/Init.d	Tue Mar 18 17:51:52 2008 +0000
@@ -16,22 +16,22 @@
 /**************************************************************************************************
  * Initialisation setup and exit cleanup module.
  *
- * This module controls most of the initialisation and deinitialisation of the program.
+ * This module provides an infrastructure for handling much of the initialisation and
+ * deinitialisation of the program. It does not, however, provide much of the (de)initialisation
+ * code; with the exception of that for the logger.
  *************************************************************************************************/
 module mde.Init;
 
 import mde.exception;
-
 import mde.options;
-import mde.events;
-import global = mde.global;
-import mde.input.input;
+import paths = mde.resource.paths;
 
 // tango imports
 import tango.core.Thread;
 import tango.core.Exception;
 import tango.util.log.Log : Log, Logger;
 import tango.util.log.ConsoleAppender : ConsoleAppender;
+import tango.util.log.SwitchingFileAppender : SwitchingFileAppender;
 import tango.stdc.stringz : fromStringz;
 
 /**
@@ -42,18 +42,31 @@
  */
 static this()
 {
-    version (mdeTest) {}   // test.mdeTest sets up its own root logger
+    // Find/create paths:
+    try {
+        paths.resolvePaths();
+    } catch (Exception e) {
+        throw new InitException ("Resolving paths failed: " ~ e.msg);
+    }
+    
+    // Set up the logger:
+    version (mdeTest) {}    // test.mdeTest sets up its own root logger
     else {
-        // For now, just log to the console:
+        // Where logging is done to is determined at compile-time, currently just via static ifs.
         Logger root = Log.getRootLogger();
-        root.addAppender(new ConsoleAppender);
+                
+        static if (true ) { // Log to the console
+            root.addAppender(new ConsoleAppender);
+        }
+        static if (true ) { // Log to files
+            // Use 2 log files with a maximum size of 1 MB:
+            root.addAppender (new SwitchingFileAppender (paths.logDir~"/log-.txt", 5));
+        }
         
         // Set the level here, but set it again once options have been loaded:
         debug root.setLevel(root.Level.Trace);
         else root.setLevel(root.Level.Info);
     }
-    
-    Init.addFunc (&miscInit);
 }
 static ~this()
 {
@@ -89,11 +102,13 @@
     */
     this()
     {
+        logger.info ("Init: starting");
         
-        logger.info ("Init: starting");
         //BEGIN Pre-init (loading options)
-        // Load options FIRST. Should be fast, most work is probably disk access,
-        // and it's a really good idea to let the options apply to all other loading.
+        /* Load options now. Don't load in a thread since:
+        *   Loading should be fast
+        *   Most work is probably disk access
+        *   It's a really good idea to let the options apply to all other loading. */
         try {
             Options.load();
         } catch (optionsLoadException e) {
@@ -101,6 +116,7 @@
         }
         addCleanupFct (&Options.save);  // not strictly cleanup, but needs to be called somewhere
         
+        // Now re-set the logging level:
         Log.getRootLogger.setLevel (cast(Log.Level) Options.misc.logLevel, true);  // set the stored log level
         //END Pre-init
         
@@ -240,18 +256,3 @@
         logger.info ("Unittest complete.");
     }
 }
-
-void miscInit () {
-    /* Some miscellaneous short calls.
-    *
-    * Was executed in the main loop, but for the sake of simplicity use another thread.
-    */
-    try {
-        global.input = new Input();
-        global.input.loadConfig ();         // (may also create instance)
-                
-        addEventsSchedule ();
-    } catch (Exception e) {
-        init.setFailure ();                // must clean up properly
-    }
-}
--- a/mde/events.d	Sat Mar 15 15:14:25 2008 +0000
+++ b/mde/events.d	Tue Mar 18 17:51:52 2008 +0000
@@ -16,6 +16,8 @@
 /// Handles all events from SDL_PollEvent.
 module mde.events;
 
+import mde.Init;
+
 import mde.scheduler;
 import global = mde.global;
 
@@ -29,10 +31,20 @@
 private Logger logger;
 static this() {
     logger = Log.getLogger ("mde.events");
+
+    Init.addFunc (&initInput);
 }
 
-void addEventsSchedule () {
-    Scheduler.perFrame (&pollEvents);
+// An Init function
+void initInput () {
+    try {
+        global.input = new Input();
+        global.input.loadConfig ();         // (may also create instance)
+                
+        Scheduler.perFrame (&pollEvents);
+    } catch (Exception e) {
+        init.setFailure ();                 // must clean up properly
+    }
 }
 
 void pollEvents (double) {
--- a/mde/global.d	Sat Mar 15 15:14:25 2008 +0000
+++ b/mde/global.d	Tue Mar 18 17:51:52 2008 +0000
@@ -25,6 +25,6 @@
 
 import mde.input.input;
 
-bool run = true;	// mail loop continues if this is true
+bool run = true;	// main loop continues if this is true
 
 Input input;		// Input instance. When multiple users are allowed instances will be per-user.
--- a/mde/mde.d	Sat Mar 15 15:14:25 2008 +0000
+++ b/mde/mde.d	Tue Mar 18 17:51:52 2008 +0000
@@ -44,7 +44,8 @@
 {
     //BEGIN Initialisation
     Logger logger = Log.getLogger ("mde.mde");
-    
+    logger.info ("Starting mde...");
+        
     try {
         init = new Init();	// initialisation
     } catch (InitException e) {
--- a/mde/options.d	Sat Mar 15 15:14:25 2008 +0000
+++ b/mde/options.d	Tue Mar 18 17:51:52 2008 +0000
@@ -137,6 +137,7 @@
 class OptionsMisc : Options {
     bool useThreads;    // set 0 to disable threading
     char[] L10n;        // locale, e.g. en-GB
+    
     int logLevel;       // tango logger level
     
     this () {
--- a/mde/resource/paths.d	Sat Mar 15 15:14:25 2008 +0000
+++ b/mde/resource/paths.d	Tue Mar 18 17:51:52 2008 +0000
@@ -102,13 +102,23 @@
         paths[pathsLen++] = path~'/';
     }
     
-    // Test a path and add if is a folder
-    void tryPath (char[] path) {
-        PathView pv = FilePath (path);
-        if (pv.exists && pv.isFolder) {
+    // Test a path and add if is a folder.
+    bool tryPath (char[] path, bool create = false) {
+        FilePath fp = FilePath (path);
+        if (fp.exists && fp.isFolder) {
             paths[pathsLen++] = path~'/';
-            logger.info ("Path "~path~" is writable: " ~ (pv.isWritable ? "yes" : "no"));
+            return true;
+        } else if (create) {
+            try {
+                fp.create;
+                paths[pathsLen++] = fp.toString~'/';
+                return true;
+            } catch (Exception e) {
+                logger.warn ("Creating path "~path~" failed:");
+                logger.warn (e.msg);
+            }
         }
+        return false;
     }
     
     // Use a static array to store all possible paths with separate length counters.
@@ -119,34 +129,11 @@
 
 /** These are the actual instances, one for each of the data and conf "directories". */
 mdeDirectory dataDir, confDir;
+char[] logDir;
 
 //BEGIN Path resolution
 static this() {
     logger = Log.getLogger ("mde.resource.paths");
-    
-    // NOTE: May fail. Currently I think I'll just let the exception halt execution.
-    resolvePaths();
-    if (!dataDir.pathsLen) throw new mdeException ("Fatal: no data path found!");
-    if (!confDir.pathsLen) throw new mdeException ("Fatal: no conf path found!");
-}
-
-private:
-// The maximum number of paths for any one "directory".
-// There are NO CHECKS that this is not exceeded.
-const MAX_PATHS = 3;
-
-Logger logger;
-
-/* Try each path in succession, returning the first two exist and be a folder. Throws if none are. */
-char[] findPath (char[][] paths ...) {
-    foreach (path; paths) {
-        PathView pv = new FilePath (path);
-        if (pv.exists && pv.isFolder) return pv.toString;    // got a valid path
-    }
-    // no valid path...
-    logger.fatal ("Unable to resolve a required path! The following were tried:");
-    foreach (path; paths) logger.fatal ('\t' ~ path);
-    throw new mdeException ("Unable to resolve a required path (see log for details).");
 }
 
 // These are used several times:
@@ -155,21 +142,33 @@
 
 version (linux) {
     void resolvePaths () {
+        logger.trace ("1");
         // Home directory:
         char[] HOME = fromStringz (getenv (toStringz ("HOME")));
         
+        logger.trace ("3");
         // Base paths:
-        char[] staticPath = findPath ("/usr/share/games/mde", "/usr/local/share/games/mde", "data");
-        char[] userPath = findPath (HOME~"/.config/mde", HOME~"/.mde");
+        // Static data (must exist):
+        PathView staticPath = findPath (false, "/usr/share/games/mde", "/usr/local/share/games/mde", "data");
+        // Config (can just use defaults if necessary, so long as we can save afterwards):
+        PathView userPath = findPath (true, HOME~"/.config/mde", HOME~"/.mde");
         
+        logger.trace ("5");
         // Static data paths:
-        dataDir.addPath (staticPath);      // we know this is valid anyway
-        dataDir.tryPath (userPath ~ DATA);
+        dataDir.addPath (staticPath.toString);      // we know this is valid anyway
+        dataDir.tryPath (userPath.toString ~ DATA);
+        if (!dataDir.pathsLen) throw new mdeException ("Fatal: no data path found!");
         
+        logger.trace ("7");
         // Configuration paths:
-        confDir.tryPath (staticPath ~ CONF);
-        confDir.tryPath ("/etc/mde");
-        confDir.tryPath (userPath ~ CONF);
+        confDir.tryPath (staticPath.toString ~ CONF);
+        bool sysConf = confDir.tryPath ("/etc/mde");
+        confDir.tryPath (userPath.toString ~ CONF, !sysConf);
+        if (!confDir.pathsLen) throw new mdeException ("Fatal: no conf path found!");
+        
+        logger.trace ("9");
+        // Logging path:
+        logDir = userPath.toString;
     }
 } else version (Windows) {
     void resolvePaths () {
@@ -181,17 +180,52 @@
         char[] staticPath = findPath (installPath ~ DATA);
         
         // Static data paths:
-        dataDir.addPath[dataLength++] = staticPath;   // we know this is valid anyway
-        dataDir.tryPath (userPath ~ DATA);
-        
+        dataDir.addPath (staticPath.toString);   // we know this is valid anyway
+        dataDir.tryPath (userPath.toString ~ DATA);
+        if (!dataDir.pathsLen) throw new mdeException ("Fatal: no data path found!");
+                
         // Configuration paths:
         confDir.tryPath (staticPath.toString ~ CONF);
-        confDir.tryPath (installPath ~ CONF);
-        confDir.tryPath (userPath.toString ~ CONF);
+        bool sysConf = confDir.tryPath (installPath ~ CONF);
+        confDir.tryPath (userPath.toString ~ CONF, !sysConf);   // create if no system conf dir
+        if (!confDir.pathsLen) throw new mdeException ("Fatal: no conf path found!");
+        
+        // Logging path:
+        logDir = userPath;
     }
 } else {
     static assert (false, "Platform is not linux or Windows: no support for paths on this platform yet!");
 }
+
+private:
+// The maximum number of paths for any one "directory".
+// There are NO CHECKS that this is not exceeded.
+const MAX_PATHS = 3;
+
+Logger logger;
+
+/* Try each path in succession, returning the first to exist and be a folder.
+* If none are valid and create is true, will try creating each in turn.
+* If still none are valid, throws. */
+PathView findPath (bool create, char[][] paths ...) {
+    foreach (path; paths) {
+        PathView pv = new FilePath (path);
+        if (pv.exists && pv.isFolder) return pv;    // got a valid path
+    }
+    if (create) {   // try to create a folder, using each path in turn until succesful
+        foreach (path; paths) {
+            PathView pv = new FilePath (path);
+            try {
+                return pv;
+            }
+            catch (Exception e) {}
+        }
+    }
+    // no valid path...
+    logger.fatal ("Unable to find"~(create ? " or create" : "")~" a required path! The following were tried:");
+    foreach (path; paths) logger.fatal ('\t' ~ path);
+    throw new mdeException ("Unable to resolve a required path (see log for details).");
+}
 //END Path resolution
 
 /** A special adapter for reading from multiple mergetag files with the same relative path to an