Register to post in forums, or Log in to your existing account
 

Post new topic  Reply to topic     Home » Forums » Zugg's Blog Goto page 1, 2  Next
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Feb 13, 2007 9:40 pm   

More Bug Fixing
 
Time for a new blog so that I can rant about Delphi bugs again.

OK, sure, this one is obscure, but what a pain it was. I use Frames in Delphi a lot. A Frame is like an object-oriented Window/Form. It's a portion of a form that also contains code/methods that you can drop onto another form.

An example: When you go into the Package Editor and click on an alias or trigger, there is a tab control with two tabs: Script and Compiled Code. The Script tab contains a Frame called "EditScript" which contains the multiline editor control and all of the code to handle editing the script (syntax checking, etc). The Compiled Code tab contains a Frame called "CodeFrame" which contains the memo control and all of the code needed to show the disassembled compiled code.

So today, I decided I wanted to add another tab to the Trigger page called "Compiled Pattern". This would show the compiled code that generates the trigger pattern, rather than the compile code for the trigger script.

Seemed like it would be easy. Just add another tab to the tab control, and drop in another CodeFrame and call it "PatternFrame". Then handle the tab selection so that instead of doing CodeFrame.LoadCode( Trigger), I could do PatternFrame.LoadCode( TriggerPattern).

Works fine in the Delphi IDE. Compile and run...create a new trigger...crash "Component CodeFrame already exists". What the hell? The new frame isn't called "CodeFrame", it is called "PatternFrame". I check the DFM form file and confirm that the first frame is declared like this:

CodeFrame: TCodeFrame

and the second frame is declared like this:

PatternFrame: TCodeFrame

So what's the problem? Restarted Delphi in case it was confused/corrupted. Still crashes. Restart Windows...still crashes. OK, time for Google searching.

Turns out that there is an obscure bug in Delphi. You cannot put two frames of the same base type on the same form. Works in the IDE, but not at RunTime. Just great.

Fortunately, I found a workaround. Just create the frames at runtime, and then make sure you assign unique names to each frame (which is apparently the bug in Delphi...it gives the frames the same name as the base type "TCodeFrame" without the "T" at the beginning).

So now the code looks like:
Code:
CodeFrame := TCodeFrame.Create(self)
CodeFrame.Name := 'CodeFrame';
CodeFrame.Parent := CodeTabSheet;
PatternFrame := TCodeFrame.Create(self)
PatternFrame.Name := 'PatternFrame';
PatternFrame.Parent := PatternTabSheet;

and this seems to work now. I know that all programs have bugs, and that this was pretty obscure, but it sure makes me wish that Borland worked more on fixing bugs in Delphi 7 instead of spending all their time and effort on .NET stuff. Maybe someday the new Turbo Delphi will be good enough and compatible enough with my existing 3rd party components. For now I'm just stuck with tearing my hair out and beating my head against the wall.

I hate days like this when one simple thing takes hours to deal with.
Reply with quote
Larkin
Wizard


Joined: 25 Mar 2003
Posts: 1113
Location: USA

PostPosted: Tue Feb 13, 2007 10:02 pm   
 
Come over to the Dark Side... Visual C# on .NET Framework 3.0!

Twisted Evil
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Feb 13, 2007 10:17 pm   
 
Yeah right...do you want to port my 1.2 million lines of Delphi code and find/buy the .NET equivalents for the thousands of dollars of 3rd party components that I use? Didn't think so.

Besides, I'm sure I'd find plenty of bugs to rant about in Microsoft's tools too. And then I could also rant about how the new version of the .NET Framework caused my code to break.
Reply with quote
Rainchild
Wizard


Joined: 10 Oct 2000
Posts: 1551
Location: Australia

PostPosted: Tue Feb 13, 2007 10:33 pm   
 
You could write zDelphiSharp ... an automatic converter for 1.2 million lines of code from Delphi to C#... Very Happy

And yeah, there's plenty to rant about on the MS side of the fence too (especially certain incompatibilities between 1.1 and 2.0... I haven't got 3.0 yet to find out what they broke in that migration).

Though, on the whole for a clean slate project it's a very nice language to work with... and I have ported portions of my old delphi/c++/vb code to it, but they're more like 0.02 million lines of code Wink
Reply with quote
Rorso
Wizard


Joined: 14 Oct 2000
Posts: 1368

PostPosted: Tue Feb 13, 2007 11:05 pm   
 
Rainchild wrote:
You could write zDelphiSharp ... an automatic converter for 1.2 million lines of code from Delphi to C#... Very Happy

And yeah, there's plenty to rant about on the MS side of the fence too (especially certain incompatibilities between 1.1 and 2.0... I haven't got 3.0 yet to find out what they broke in that migration).

Though, on the whole for a clean slate project it's a very nice language to work with... and I have ported portions of my old delphi/c++/vb code to it, but they're more like 0.02 million lines of code Wink

This might sound silly, but one of the reasons I dislike .NET is that it just compiles to some kind of bytecode. Decompilers are appearently able to produce a very good result which is not a good thing if you don't want to release the source code.

Also I don't like how they messed up with C++ .NET. They more or less changed the C++ standard to suit their needs.
Reply with quote
Larkin
Wizard


Joined: 25 Mar 2003
Posts: 1113
Location: USA

PostPosted: Tue Feb 13, 2007 11:17 pm   
 
The beautiful thing about .NET development, in my humble and extremely biased opinion, is that what takes 1.2 million lines of code in Delphi or C++ becomes 400,000 lines of code in C#. I converted a client/server pair of applications for data acquisition from a room full of sensors over to C#, and the code for the server was just a matter of instantiating a couple of objects and passing handler objects for processing connections and data. I eliminated 90% of the difficult coding from my applications in the re-write!

Still, I am only joking, and I know how frustrating it can be to work around bugs or quirks in someone else's product.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Feb 13, 2007 11:56 pm   
 
Larkin, have you used Delphi? Seriously, Delphi is *NOT* C++. And what takes 1.2 million lines of code in C++ probably only takes 400,000 in Delphi. Keep in mind that the guy who wrote C# was the same guy who wrote Delphi. C# is actually much closer to Delphi than it is C++ (I'm speaking about architecture and not syntax). That's why most of us Delphi coders tend to laugh at C# and .NET. Yeah, C# and .NET are great for people who used to code in C++, but Delphi programmers laugh because C# is about the same as what we've been using with Delphi for years.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Feb 14, 2007 12:05 am   
 
Anyway, back to the bug fixes. After getting the Compiled Pattern tab for triggers I was able to figure out why the %e wasn't getting into ANSI color trigger patterns. It ended up being a stupid 2-second typo fix in the parser (after all of that). I also added the feature to the pattern field where you can copy a line from the MUD and Paste it into the Pattern and if the ANSI Color option is enabled, the ANSI color codes will be converted to the proper %e[m ANSI syntax. zMUD had this feature, but I hadn't gotten around to adding it to CMUD until now.

I also messed with the bugs in the Variables setting screen where changing the Type dropdown value wouldn't update the editor screen properly. Now it's fixed so that selecting a Type of Stringlist will show the string list editor, and selecting Database record will show the database editor, etc. Fixed a couple of crashes in this too. It was all left over from when the editor screen controls were directly tied to the database.

Next, I started messing with some critical parser bugs. The bug with using A.a=100 as the first command in an #IF statement is now fixed. I also fixed the nasty bug where the %1 in a trigger script could get overwritten by the #ADD command. Turns out it was really a problem with putting %1 onto the stack, rather than putting a *copy* of %1 onto the stack. So it wasn't just the #ADD command. It only showed up because #ADD modifies the value on the stack and adds the variables current value. Since a pointer to %1 was on the stack instead of a copy, modifying the stack could cause the %1 variable to get changed. A bit obscure, but I'm glad I found it.

Taking tommorrow off (Valentines Day here in the US, and Chiara deserves some attention :) Thursday we have some Doctor appointments and other errands to run. We've actually lost so much weight on the low-carb lifestyle that our wedding bands are slipping off our fingers, so we need to get them resized! But, I'll be back to bug fixing on Friday.
Reply with quote
Larkin
Wizard


Joined: 25 Mar 2003
Posts: 1113
Location: USA

PostPosted: Wed Feb 14, 2007 2:19 pm   
 
I haven't used Delphi since version 1.0, so you got me there. Again, I was only joking! I just love coding in C# and I haven't had the chance to code at work in almost a year now. That's why I'm so grateful for CMUD right now. It's my primary coding platform, and I'm enjoying porting my massive zMUD scripts to the new CMUD way of doing things. Very Happy
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Feb 16, 2007 10:56 pm   
 
Today I made a major step forward in dealing with the stability of multiple windows/sessions. One of the major problems is how the window Docking system and the Toolbar system were saving and loading their customized layouts. The docking layout is stored in *.XLY files, and the toolbars are stored in *.TBZ files. The XLY file contains XML, while the TBZ file is an INI-file format. The XLY file is loaded/saved by routines in the aqDockManager 3rd party component, while the TBZ file is loaded/saved by routines in the DevExpress ExpressBars 3rd party component.

Both of these 3rd party components were designed assuming that there would only be a single window layout and single toolbar layout per application. So, loading multiple sessions has been difficult. I already worked on the XLY file format so that it can load a new layout on top of an existing layout without messing it up. I still need to work on this a bit more, but this is the routine that can cause windows to disappear when loading a new session sometimes.

But the toolbar routines were much worse. When a *.TBZ toolbar file was loaded, the entire existing menu/toolbar system was deleted and recreated based upon the information stored in the file. So, loading a second session would remove the existing toolbars and reload them, possibly causing the command line toolbar in the previous session to be removed.

Another side effect of this limitation is that when I add a new menu item to CMUD, loading your saved *.TBZ file for your session will overwrite this, making the new menu item not visible. This was going to create huge problems as new features were added to CMUD since existing users would be loading their existing *.TBZ files for their sessions and clearing out all of the new menu items.

What I was posponing was the fact that I needed to write my own load/save routines for both the XLY and TBZ files. I needed to write routines for the toolbar that could "merge" an existing TBZ file with the default toolbars, so any new menu items would be visible. This is what I spent today working on, and I think I've got it mostly working now. It's actually compatible with existing TBZ files, except that any menu items that have been removed via the toolbar Customization will reappear when you run the new version of CMUD.

However, if you go into the toolbar Customization and remove a toolbar or menu item in the new version of CMUD, your change will be properly recorded in the new *.TBZ file, and will stay removed the next time you load the session. Any new toolbar/menu items that I add to CMUD will appear properly even after loading your customized *.TBZ file. If you have added any new items to the menu/toolbar or renamed anything, all of those changes should still work in the new version.

I think it's the best of both worlds. I've been able to maintain compatibility (except for deleted menu items) with the previous *.TBZ files. And yet the new system does a much better job merging the session toolbars with the normal CMUD toolbars (and the default CMUD.TBZ file). This should prevent command line toolbars from disappearing with multiple sessions, although I still have more testing to be done for that.

Next week I will work more on the XLY load/save routines and try to get them working better for multiple sessions. I'll also be adding an option to Package Properties to mark a package as "Shared" (default will be Yes). A Shared package will only be loaded once when multiple sessions using the package are loaded. Currently when you load multiple sessions that use the same package, multiple copies of the package are loaded into memory. This really isn't what people expect. Loading a single copy of a package makes more sense, but you'll be able to uncheck the Shared option for the package to cause it to load multiple copies like it does now.

Anyway, that's the direction I'm going. I will really try to fix all of this so that multiple sessions and windows are easier to work with and work more how people expect them to.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Feb 21, 2007 5:13 am   
 
So yes, I *have* been working on CMUD for the past couple of days. I've been doing the web-site "work" instead of playing Final Fantasy XXII at night ;)

Yesterday I spent the day getting all of the rest of the bugs out of the Help system in CMUD. It will now update the help file database format, and should work with the standalone help program. I'm actually going to make the standalone help program a separate download to keep the CMUD download size down. The help program download will also include all of the images associated with the help file so that people without a network connection can view the help file offline and still get images.

I also fixed a bunch of problems with the images not being removed from the local cache in some cases, and a variety of other bugs.

Today I went back to work on the Multiplaying problems. The more I messed with this, the more I've come to realize that my design for multiplaying in CMUD is just simply broken. That would explain why so many people have trouble with it. My design makes some assumptions about Packages/Windows/Sessions that just aren't true in some cases. So, I went back to the drawing board and starting coming up with a design that will work and that isn't too hard to convert the code to use.

I made a lot of progress on that today. I think I have the basic data structures and logic in place for what is needed. I implemented the "Shared" option for packages so that only one copy of a shared package will be loaded into memory. The "Shared" option is Enabled by default for existing packages. So if you have a package that you want CMUD to create duplicate copies for each session, then you will need to go to the Package Properties and uncheck the Shared setting and then reload CMUD. That will cause your package to load like it does in the current 1.24 version.

The Shared flag only applies to the extra list of packages loaded for a session. The Primary package file for a session is always duplicated. So if you load more than one copy of the same session, it works properly.

It doesn't properly save stuff like the list of enabled packages for modules (which was broken anyway), and some stuff like that. It will probably take me another two days or so to get it all working again. Then I can start testing it more effectively than I did in the past so that it's working correctly in the next version.

After the Multiplaying stuff is working, then I'm going to do some basic work on the mapper to fix the bugs concerning the new docking system. I'm not changing the MDAC stuff yet (probably not till summertime), so I want the current zMUD mapper to at least work as well in CMUD as it does in zMUD.

That's the latest. Still hoping to release a new version before Chiara goes into the hospital.
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Wed Feb 21, 2007 6:41 am   
 
Sounds great, Zugg. Though I think we're missing out on a few Final Fantasy games! XII is out on Thursday here in the UK, and you're already on XXII! ;)
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Guinn
Wizard


Joined: 03 Mar 2001
Posts: 1127
Location: London

PostPosted: Wed Feb 21, 2007 9:53 am   
 
Quick feature request
An option in preferences somewhere that says "If CMUDHELP.exe exists then load this on pressing F1"

The multiple window stability and multiplaying improvements sounds like a good jump forward, and liking the website changes. Looking forward to 1.25 - almost feel it should be called 1.34 to show a nice jump from the 1.24 first public release what with all the feedback in the forums in the last month or two ;)
_________________
CMUD Pro, Windows Vista x64
Core2 Q6600, 4GB RAM, GeForce 8800GT
Because you need it for text... ;)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Feb 21, 2007 5:55 pm   
 
Guinn, that's a good idea and I'll add it to the list.

And yeah, Fang, it's just XII. Got carried away with my X's :)
Reply with quote
Tech
GURU


Joined: 18 Oct 2000
Posts: 2733
Location: Atlanta, USA

PostPosted: Wed Feb 21, 2007 6:33 pm   
 
Any chance of getting idea of what your new design for multiplaying is (a la the early days of Beta) to solicit additional feedback or do continue our (well placed) confidence in you that you got it right?

Just curious... I find the design of the product and discussions around it almost as fun as it's intended use.
_________________
Asati di tempari!
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Feb 21, 2007 7:05 pm   
 
My design philosophy has always been to look at how the players want to do something, and then code around that (rather than figuring out what I can easily code and making users live with it).

In the original design, I hadn't considered people wanting to be able to open the same session more than once. I could never see the reasons for doing it. In my mind, a session was the same as a particular character on a MUD, and I don't know of any MUDs that allow the same character to be logged in more than once.

However, when I saw some of the posts, I see that some people are using a Session as a particular package setup, and using manual login to login to different characters using the same session.

While it's true that I could just tell people: "You must create a unique session icon for each character and each MUD", I decided that I'd rather look at the current design and see what causes the problems when loading the same session more than once.

Part of the problem is that I was still getting confused myself with the "zMUD-way" of doing things. In zMUD, a "session" was really the same as a "*.MUD settings file" which was the same as a "window". But in CMUD, a session can contain multiple packages (multiple *.MUD settings) and each package can contain multiple windows. This makes it very different.

Anyway, the problem with the design in 1.24 is that an assumption was made that a package belongs to a single session. This is why 1.24 makes a copy of packages when two sessions using the same package are loaded into memory. In 1.24, each package record in memory needs to point to a single session record. When you use Close Session, for example, it loops through all of the packages in memory and closes any package associated with the session being closed.

In the docking XLY layout file, each window has a unique identifier (GUID). This identifier is stored in the *.PKG file for that particular window. So, CMUD loads the package and creates the windows, each with their unique GUID. Then when the *.XLY file is loaded, the docking system looks for a window with the matching GUID and sets its docking position.

When you load the same session twice, now you have two windows, each with the same GUID (loaded from what is stored in the *.PKG file). So when the XLY layout is loaded, it doesn't know which window to set. The XLY layout loading routine needs to be fixed so that it only looks for the window with the matching GUID within the current set of windows owned by the session being loaded.

In the TBZ toolbar file, each toolbar has a name (that is supposed to be unique). When the command line toolbar for a window is created, it is given the name "Session toolbar: Windowname", where Windowname is the name of the window. You can see the obvious problem with this when you have multiple windows with the same name (like when loading the same session twice). Again, the TBZ loading routine needs to be fixed so that it only looks for the Windowname within the windows owned by the session being loaded.

In the 1.24 version, sessions were identified by the record number in the sessions.db database. Each window stored this record number to indicate which session the window belonged to. However, if two copies of the same session were loaded, then the windows all had the same record number stored. So, even though two sessions were loaded, there was no way to tell which session a particular window belonged to because each window returned the same database record number for the session id. Thus, all of the windows looked like they belonged to a single session.

You can see how messy this all was. There are some other problems with the docking layout even when you load two different sessions, but those problems weren't as major as the fundamental assumption that only a single copy of a session would be loaded, and that each package belonged to a single unique session.

In the new design, I do not use the session database record number for the session ID. Each session record now has it's own unique number (essentially the order in which sessions are created...the first session has ID 0, the second session created has ID 1, etc). Each session record still has the database record pointer, but that isn't used as the "ID" of the session.

In addition, a package no longer belongs to a unique session record. Each package has a list of session records that it belongs to. When a session is closed, it is removed from the list of sessions for each package, and if the package is no longer used by any sessions, then it is removed. When packages are loaded, the "Shared" option for the package is checked, and if set, then the existing package is shared and the new session is added to the list. If Shared is disabled, then a new copy of the package is created instead.

Each window/module also has a list of sessions that it belongs to. So, when loading a XLY layout or TBZ toolbar file, it can check to see if a particular module or window belongs to the current session that is being loaded. This fixes the problem of loading a session causing the toolbar or window of a previous session to be messed up.

There are still potential problems with saving layouts. There is no way to only save the layout of windows belonging to a single session, because once you load more than one session, the user might dock windows from Session 2 into windows from Session 1. The window layout really requires the entire layout hierarchy, so I'm still going to disable any saving of the layout file when multiple sessions are loaded, unless I can find a way around that problem. The solution to this is to really require the user to use the Save Session option if they want to save the layout of a multiple window multiple character arrangement (merging multiple sessions into a single session icon with its own layout).

To summarize, I'm just trying to find a design that makes CMUD work as people expect it to naturally. When connected to a session, you should be able to select another session from the toolbar menu, or create a new window connected to a specific host/port using the New Connection option in the session menu. And it should just work. It shouldn't screw up your command line or existing windows.

The fact that loading the same session twice is a bad idea because each session will save itself to the same *.PKG file is still and issue. But this was the same issue in zMUD with it saving to the same *.MUD file, and people accepted that.
Reply with quote
Larkin
Wizard


Joined: 25 Mar 2003
Posts: 1113
Location: USA

PostPosted: Wed Feb 21, 2007 9:45 pm   
 
I'm with Tech. I find the discussions about the programming details and inner workings of the programs just as much fun as coding scripts for them. Thanks for the very detailed insights, Zugg!
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Thu Feb 22, 2007 1:57 am   
 
Bah. After making all of the changes that I've mentioned, CMUD is even worse. Of course, that's somewhat expected after changing a bunch of code. Today I started dealing with the bugs...some new, some old.

Mostly I was focused on the various ways in which your command line can get screwed up. Everyone has probably seen a case where they load a session (or multiple sessions) and either the command line is simply gone, or it's stuck in the middle of the MUD output window.

So what causes the command line to get stuck in the MUD output window? Well, it's a strange side effect of how the Developer Express toolbars work (or don't). The CMUD command line is a RichView component (similar to the Windows RichEdit control used in zMUD). This is a 3rd party component, so it looks like a generic external control to the DevExpress toolbar system. To put an external control onto a toolbar, you create the control, then create a ContainerItem component for the toolbar, then you assign ContainerItem.Control := MyControl. This places MyControl (the RichView control in this case) onto the toolbar.

MyControl initially gets created as part of the routine that creates the MUD output window. Each MUD Output window has a command line RichView control. The right-click Window Options menu allows you to toggle this control on and off. Since it is owned by the MUD Output window, Delphi initially creates it as a child control of the form. This just happens to place the RichView control in the middle of the output window (wherever I dropped it from the Delphi component palette onto the main form). It's only when CMUD performs the ContainerItem.Control := MyControl that the RichView control is moved from the MUD output window into the toolbar ContainerItem. If the ContainerItem ever gets deleted, the RichView control gets sent back to it's original location (the MUD output window).

The problem was that the routine that loads the TBZ toolbar file was first removing all toolbars and items, then loading them all from the file. If the TBZ file for the session being loaded was corrupted somehow (or if incorrect values were stored, as I'll take about in a minute), then the command line never gets set. Loading the TBZ file clears all toolbars (which removes ContainerItem and puts the command line back into the mud output window), and if it doesn't find a valid entry in the TBZ file to create a new ContainerItem and load the control, then the control remains stuck in the output window.

I actually put the command line control into the middle of the mud output window on purpose. Because when CMUD is working correctly, you should never see it there. So, the fact that it's in that wierd place actually makes it easier to debug. If I had placed the command line at the bottom of the screen, then people might not notice the problem with the bad layout files being loaded.

Turns out that there were a number of ways for the TBZ file to get screwed up. Part of it is because of the dumb way Developer Express was storing everything by name. Not only could it change the wrong toolbar (if multiple toolbars had the same name), but it was also storing the name of the parent docking control (essentially which edge of the window the toolbar is docked to). Each MUD output window has 4 toolbar "docks" located around the 4 edges of the window. The TBZ file stores the name of the dock for the toolbar.

Unfortunately, the DevExpress code stored the name of the dock as "FormName.DockName". The name of the MUD output window form is called "MUDForm". When a second window is created, Dephi gives it the name of "MUDForm_1". The next window is "MUDForm_2" and so on. So, if you load Session A and then save it and exit, the TBZ file stores the command line "Session Toolbar 1" with a dock name of "MUDForm.Dock". So now, imagine that you have two sessions. Each session TBZ file has the toolbar docked to MUDForm.Dock. Now when you load the two sessions together, the second session tries to put it's toolbar on MUDForm.Dock, which is actually in the first window created instead of the second.

So, I had to strip off the "MUDForm" part of the name, and replace it with the name of the window that is currently being loaded. So now, when you load the second session, the dock name gets converted to MUDForm_1.Dock since MUDForm_1 is the name of the second window.

Now, if we accidentally save the new layout when we exit, then the TBZ file will store the dock name as MUDForm_1.Dock. In CMUD 1.24, this could cause problems since it when the TBZ file was loaded the next time into a single window, it would look for MUDForm_1 and not find anything (since the dock is now back in the first MUDForm window). Whew, what a mess, huh? This system just wasn't designed for loading and saving partial toolbar layouts.

The other problem was that in CMUD 1.24, sometimes toolbars weren't getting removed from memory. So, when the session closed, a lot of extra toolbars and stuff were being saved to the TBZ file. Then when the session was loaded again, a bunch of toolbars were getting created in memory and marked as invisible, which were then saved again. So there was a lot of crap being saved in TBZ files, especially if you loaded multiple sessions.

In other cases, when the TBZ file was saved, the messed up toolbars were marked as invisible, which was preventing the RichView control being being moved to the ContainerItem. Also causing the command line to show in the middle of the output window.

I think I've got most of the problems fixed. My main remaining problem with the toolbar loading is handling these corrupted "Invisible" toolbars. I can't find a way to distinguish a corrupted toolbar from an toolbar where the user has specifically made it invisible (like when right-clicking a toolbar and unchecking one of the toolbars to make it invisible).

And this is just all of the toolbar stuff. I still have to rewrite the XLY docking system load and save routines to also deal with these issues. Bahh...my brain is full. No web work tonight.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Feb 23, 2007 12:41 am   
 
I made some good progress today. I fixed a few more problems with the TBZ toolbar loading. I've changed the name of the command line toolbars so that they contain the same unique window ID that is used by the window docking system. This prevents problems with conflicting toolbar names, and also helps me get away from some of the file corruption from the previous version. In other words, if you have customized your command line toolbar (and I doubt anyone has), then your changes won't load into the new version of CMUD. But once you re-customize the toolbar and exit, then the new changes are saved with the correct toolbar names, and then it will load properly.

This also allowed me to handle hidden toolbars, and that's all working now too. I also added the code to preserve the customized item order, which wasn't working because I wasn't deleting the toolbar and recreating from scratch when loaded, and so it was just appending new items to the end of the current toolbar. Now it inserts them in the correct order.

So, I think toolbars are *finally* working well. I also found and fixed the long-time annoyance that caused the command line "frame" to not get repainted properly. This happened usually when you had two windows tab-docked and then clicked between the different tabs. Turns out this was a problem with the RichView 3rd party component that I am using for the command line. They draw the frame of the memo box in the WM_NCPAINT message handler. That seems like the correct place to perform custom border painting. But for some reason, this windows message isn't getting fired when you switch tabs on docked windows. So, I modified the Paint routine for the RichView to also paint the borders. So, sometimes it will be drawing the borders twice, but this doesn't seem to be noticeable. And this keeps the command line painted properly now.

Then I started work on the XLY window layout loading/saving. The 3rd window docking system that I'm using was written Automated QA, the authors of AQTime and AQTestComplete, both excellent products. AQA writes some pretty nice code. In general, I find their design and code to be a bit better than Developer Express, and significantly better than the DevExpress ExpressBars component (which is the menu toolbar and their own docking system). However, even though their code design is better, I found that it was very difficult to pull out the LoadFromXML and SaveToXML routines that are used to load and save the docking layout. Their Load and Save routines are too intimately tied with the rest of the docking system code.

So, rather than pull out the code and then modify it to suit my needs, I was forced to modify their source code directly. In this case, I added a field to each docking control (window) called IsActive. When a layout is being loaded, it reads the GUID from the XLY file and then searches for a window with the matching GUID. If multiple copies of the same session are loaded, there will be multiple windows with the same GUID in memory. So when it searches for a window, it now checks to see if IsActive is True. Before loading the layout, I loop through all windows and set IsActive to True only for the windows that belong to the current session being loaded.

This fixes the XLY loading so that only the windows belonging to the current session are modified. Any windows belonging to another session are left alone. Global windows, such as the Reference flyout, or Status Window also have IsActive set to True so they can also be modified by a new layout being loaded. This seems to work well.

The next fix was to somehow merge the existing window layout, with the layout being loaded. The main CMUD window has a single "DockingSite" where all of the MUD windows are docked. What I am doing now is to save the current docking site, then clear it out, then load the XLY layout file. Then I essentially "dock" the newly loaded layout with the previous layout.

In other words, if you are currently viewing Session A and now you load Session B, it will save Session A, load the Session B layout (as if it were the only session), and then restore the Session A layout and dock the newly loaded Session B layout to it. So each session becomes a tabbed window in the main CMUD window. This seems to work well.

The only thing left is still loading two copies of the same session. The toolbar system seems to be working fine, but something about the XLY window layout still isn't working yet. I hope to finish finding this problem tomorrow and then do more extensive testing of the system to see if anything else is broken.

But it's already working *significantly* better than in v1.24. I haven't lost a single toolbar (or gotten any floating in the output window), and I'm no longer losing existing windows when loading new sessions (in 1.24 you would need to select the window from the Windows menu to make it appear, and often it was floating and not docked).

Cross your fingers...hopefully I'll finish this stuff tomorrow, and can spend the first part of next week on Mapper stuff and get the new version released at the end of next week as planned.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Mon Feb 26, 2007 11:27 pm   
 
Sorry I didn't post last Friday. I actually got a lot done, although today has been a bit more frustrating.

On Friday I thought I finally had all of this multisession stuff working. I fixed the XLY window layout loading and tested it for loading different sessions, including multiple copies of the same session. It all worked great. The nasty bug was being caused by my routine to try and merge the loaded session into the current layout.

The XLY file contains an XML hierarchy of all the docked controls. There is a single global Docking Site (in the main application window, filling all of the form below the toolbar). Each MUD window is contained within a "DockingControl". When multiple windows are docked together, the docking system automatically creates "Containers". There is a VerticalContainer which contains Controls that are docked vertically, and there is a HorzContainer with controls docked horizontally, and there is an InsideContainer which has controls docked via tabs. Each Container is also a "Control" so you can have any sort of complex hierarchy that you wish.

When the XLY file is saved, everything from the global docking site is saved. When restored, everything is reloaded, including the global docking site. So, what I was trying to do to merge the two layouts was this:
Code:
SavedSite := MainDockSite.MainItem; // save the contents of the docking site
MainDockSite.MainItem := nil; // clear the main docking site
LoadLayoutFile( filename.xly); // loads the XLY file into the main docking site
LoadedSite := MainDockSite.MainItem; // get the newly loaded docking site
MainDockSite.MainItem := SavedSite; // restore original docking site
LoadedSite.Dock( MainDockSite.MainItem, Inside); // dock new site to old site in tabbed form

OK, that's just psuedo-code, but you can hopefully get the idea. I save the current docking site, then clear it out and load the layout into it. I cannot specify which docking site to load the layout into...it will always load into the global docking site that I just saved. Once loaded, I restore the original layout and then dock the newly loaded layout into the original layout using the "inside" option, which makes a tabbed dock.

The pseudo-code seemed to be fine. The problem was the implementation. Turns out that setting the MainItem of the docking site (which is the top-level control in the tree hierarchy) has a lot of side effects. In particular, setting "MainDockSite.MainItem := nil" actually causes the docking system to undock all controls and then free up all of the containers! Not at all what I wanted!

Turns out, there was a protected property that I was able to access in the docking site that held the direct pointer to the MainItem. So, I was able to do "MainDockSite.MainItemPtr := nil" to bypass all of the side effects. Once I did this, then everything started working great!

So, I ended Friday thinking that I was done with this session stuff. Unfortunately I wasn't done. Over the weekend I realized that there were some other things to test. For example, the list of enabled packages for each module is stored in the session file. Was this information getting loaded/saved properly now? Nope.

So, today I started looking at the code that saves/loaded the list of enabled packages for each module. And boy, this code was a mess too! The "Modules" field in the database was being used to store this information. It was a "memo" string that was stored in CMUD Database format (Key=Value). Each "Key" was of the form "PackageFileName:ModuleName" and the Value was a string list of Package Filenames that were enabled for the named Module.

Several problems with this. First, the name of the module was being stored. What if the module/window name was changed (like renaming a window)? Well, then the list of enabled packages is lost. So using the module name isn't a good idea. Back when this routine was written, there wasn't any sort of unique name for a module/window. But when the docking layout stuff was implemented, each module/window got a unique GUID value. So it makes better sense to save the data using the module/window GUID instead of the name. Since this is unique across all packages, the PackageFileName isn't needed.

The other problem is the string list of enabled packages. So, if a package isn't in this list, should it be disabled? Or was the package just not known about when the data was first stored. When CMUD first loads the list of enabled packages, it defaults to having all packages enabled for Windows, and all packages disabled for Modules. Since the string list just contained a list of enabled packages, you could easily get a package re-enabled after disabling it.

I decided that I just didn't like this combination of database variable and string list. Too much of a pain to parse, and not general-purpose enough. What I really needed was an INI file for each session. But I didn't want to create an INI disk file for every session icon. I wanted all session information stored in the SESSIONS.DB database.

Fortunately, I had recently learned about in-memory INI files that could be saved and loaded from string variables. So, I have converted the "Modules" field in the sessions database to be in INI-file format. This allows me to use the following structure to save the list of enabled packages:
Code:
[Module GUID]
PackageFilename = 0
PackageFilename = 1...

The unique GUID for the module is used as the INI section name. Then, a list of package filenames is given, with a value of 0 if the package is disabled, or a value of 1 if the package is enabled. If a package isn't listed, then it is loaded based upon the default (enabled for windows, disabled for modules).

This works well, and provides future opportunity for saving other options into the INI file stored in the database. But it took all day to rip out the old kludgey code and get the new code all working. And then I also had to implement the routines to convert the old format into the new format. Although even with this conversion routine, if you have a package disabled in v1.24, it will probably show up as re-enabled in v1.25 (because the old database only stores the enabled packages, and not the disabled packages). But once you disable your packages again, then they should be saved properly in the future.

There is still one more item left to fix for multiple sessions, and that is the Save Session As option in the File menu. That is supposed to take all of the sessions currently loaded and merge them into a single new session icon. For example, if you load Session A and then load Session B, and then use Save Session As to save everything to Session C, then you will get a new session icon labeled "Session C" and loading it will be the same as loading Sessions A and B. This didn't really work properly in v1.24, and has several other problems with the new code for multiple sessions. My guess is that I will be spending all day tomorrow on that issue.

The other good news is that on Friday I also figured out the bug causing problems with user-defined functions. There were problems with the way %if,%case,%switch were being executed, and there was a problem with parsing an argument list for a user defined function. I was able to fix all of those problems and now the recursive example in the help file of the @fact user-defined function is working again. This was another big bug that was causing a lot of problems for people, so I was happy to get it fixed.

Hopefully I'll still have time to get the mapper working better, so cross your fingers. I'm going to be out of the office on Thursday, which is the day we take Chiara to the hospital for all of the pre-op tests and meetings (she goes into surgery on Monday). Today Chiara has spent all day dealing with the stupid tires on our car. We got a small nail in one of the back tires, and it apparently can't be fixed, and the treads are low enough on the other tires that we have to buy all four new tires.

I hate to rant again, but I really hate tires! With all of this technology that we have, we can't fix a simple small nail hole in a tire? The tire isn't even totally flat. I can pump it up and after driving around for several hours it only drops from 32psi down to 28psi. So it's a very slow leak (because the nail is still embedded and stopping the leak from being worse). Anyway, they claim it's too close to the edge of the tire. I think these tire companies just want us to buy new tires. Someone could make Millions if they came up with a way to repair small leaks like this that worked. What's worse is that our car is leased, and the lease is up in just 6 months. Seems silly to buy 4 brand new tires with only 6 months left on the lease, so we are buying the least expensive that we can get at Sears. But we are still out about $600 for all of this, just because of a stupid little nail! Not what we needed to add to our stress this week!

OK, rant over...back to programming now.
Reply with quote
nexela
Wizard


Joined: 15 Jan 2002
Posts: 1644
Location: USA

PostPosted: Tue Feb 27, 2007 12:13 am   
 
Quote:
they claim it's too close to the edge of the tire. I think these tire companies just want us to buy new tires.

It's more of a liability thing, plugs/patches near a sidewall won't hold (as well) as those on part of the tire in constant contact with the ground. So if a tire shop gave you a sidewall plug and it blew out on the highway well who do you think is responsible in the eyes of a lawyer :p, As for new tires call up a reputable used tire shop (usually the one with a nice phone book ad and electronic balancer) and tell them you need some tires that will last you for 6 months. usually you can get a decent set of used/new tires for around 250-300 installed. But sears is expensive anyway you look at it.
_________________
Zmud Support Library
Zmud Knowledge Base
Reply with quote
Tech
GURU


Joined: 18 Oct 2000
Posts: 2733
Location: Atlanta, USA

PostPosted: Tue Feb 27, 2007 1:36 am   
 
I've never replace car tires so I don't know if it'll help but I remember for a while there a product that was an automatic flat fixer (inflater/sealant). It might be worth looking into to get some more life out of the tires, although nexela's option sounds more sound. Never realized they were that expensive.

Back to bug fixing... I'm glad you're making so much progress on the bug fixes, I'm eagerly anticipating 1.25. And if it hasn't been said lately, CMUD rocks!
_________________
Asati di tempari!
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Tue Feb 27, 2007 1:46 am   
 
Zugg wrote:
I hate to rant
Quote:
Zugg's official personal blog where he will post... rants

Where's the problem?

Here's hoping that Tuesday and Wednesday's bug squashing and Thursday's meetings go okay.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Feb 27, 2007 2:39 am   
 
Actually, Sears was the cheapest we found. The TireWorld place near our house that we initially tried to get the puncture repaired quoted over $800! Yeah, it's Colorado so they are still expensive all-road tires. We still have potential winter weather for a couple more months, so I couldn't get *really* cheap tires. Oh well, hopefully by saving the receipt we'll be able to prove that the tires are almost new when the lease expires in August. From my research, they probably would have decreased the value of the car by the cost of new tires if we had turned in the lease with 24,000 miles on the original tires anyway.

Basically, there doesn't seem to be any way around the fact that tires just wear out, and faster than I'd like them to. That's just life. We just didn't need to be dealing with tires a week before Chiara's surgery and at the same time we are getting the bad tax news from our accountant.

Back to work so I can generate more CMUD sales!
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Feb 28, 2007 12:44 am   
 
As I expected, it took all day to get he Save Session As stuff working. Actually, it wasn't just that. I also found bugs in the routines that saved (or didn't) the host/port override for a module. These values were also being saved in a field of the session database (the HostList field). I fixed this so that they are saved into the same INI option area as the enabled package list.

Saving the session (merge the existing sessions) turned out to be some tricky work. Too many little details to go into here. But I think it's working now. I also fixed some bugs dealing with the Close Session command.

I've tested all of this using some complex session layouts, with multiple windows all connected to different MUDs. I fixed it so that when you connect to a session with multiple windows, it properly initiates the connection in each window, instead of just the primary window.

There are still some potential issues with AutoLogin. When you have multiple windows using the Override Host/Port options, then there is no character name or password assigned to the window (that information is only stored for the session icon itself). But it would be pretty easy to define your own autologin triggers to handle this.

I also fixed another bug that could cause a session layout file to become corrupted (and cause the infamous "command line in the middle of the output window" problem).

Finally, I also fixed the bug that was preventing aliases that start with non-alphabetic characters from working. So now in 1.25 you can have aliases that start and contain any character sequence and they should work properly on the command line (but not necessarily from a script...some will work, but some won't).

Tomorrow I'll take a look at the mapper and see if there is something I can do to fix the docking issues easily.
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » Zugg's Blog All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
© 2009 Zugg Software. Hosted on Wolfpaw.net