Global Defaults, Logging (and XML format)

Posted by Magnum on Tue 21 May 2002 05:40 AM — 48 posts, 133,346 views.

Canada #0
Heh. Out of curiousity, I tried playing with an .MCC file, to what could be accomplished. First, I resaved my default colours to get a newer file coded in XML. Next, I loaded my most popular World XML file, and copied some "settings" to the colour file.

Part of the reason I did this, is because most of the settings I tried to copy are also "colour" related. (Colour of commands echoed to the output window, etc...).

I found that the extra information in the file was ignored. I assume because the code that loads the colour file only looks for certain tags, and ignores all others. Here is my modified file (which DOES NOT WORK):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE muclient>
<!-- Saved on Tuesday, May 21, 2002, 12:34 AM -->
<!-- MuClient version 3.22 -->
<!-- Written by Nick Gammon -->
<!-- Home Page: http://www.muclient.com/ -->
<muclient>

<!-- colours -->

<colours
muclient_version="3.22"
world_file_version="15"
date_saved="2002-05-21 00:34:06"
>

<ansi>

<normal>
<colour seq="1" rgb="gainsboro" />
<colour seq="2" rgb="maroon" />
<colour seq="3" rgb="darkgreen" />
<colour seq="4" rgb="#8A8A00" />
<colour seq="5" rgb="navy" />
<colour seq="6" rgb="purple" />
<colour seq="7" rgb="royalblue" />
<colour seq="8" rgb="#3C3C3C" />

</normal>

<bold>
<colour seq="1" rgb="white" />
<colour seq="2" rgb="red" />
<colour seq="3" rgb="forestgreen" />
<colour seq="4" rgb="#C1C100" />
<colour seq="5" rgb="blue" />
<colour seq="6" rgb="darkviolet" />
<colour seq="7" rgb="cornflowerblue" />
<colour seq="8" rgb="black" />

</bold>

</ansi>

<custom>
<colour seq="1" name="Custom1" text="#FF8080" back="gainsboro" />
<colour seq="2" name="Custom2" text="darkgoldenrod" back="gainsboro" />
<colour seq="3" name="Custom3" text="#515151" back="gainsboro" />
<colour seq="4" name="Custom4" text="#80FFFF" back="gainsboro" />
<colour seq="5" name="Custom5" text="#0080FF" back="gainsboro" />
<colour seq="6" name="Custom6" text="#FF80C0" back="gainsboro" />
<colour seq="7" name="Custom7" text="red" back="gainsboro" />
<colour seq="8" name="Custom8" text="#0080C0" back="gainsboro" />
<colour seq="9" name="Custom9" text="magenta" back="gainsboro" />
<colour seq="10" name="Custom10" text="#804040" back="gainsboro" />
<colour seq="11" name="Custom11" text="#FF8040" back="gainsboro" />
<colour seq="12" name="Custom12" text="teal" back="gainsboro" />
<colour seq="13" name="Custom13" text="#004080" back="gainsboro" />
<colour seq="14" name="Custom14" text="#FF0080" back="gainsboro" />
<colour seq="15" name="Custom15" text="green" back="gainsboro" />
<colour seq="16" name="Custom16" text="blue" back="gainsboro" />

</custom>
</colours>

<world
muclient_version="3.22"
world_file_version="15"
date_saved="2002-05-20 20:04:29"

input_font_name="FixedSys"
log_line_postamble_input="&lt;/font&gt;"
log_line_preamble_input="%H:%M&gt; &lt;font color=#FF8040&gt;"
log_line_preamble_notes="%H:%M&gt; "
log_line_preamble_output="%H:%M&gt; "
output_font_name="FixedSys"
display_my_input="y"
echo_colour="11"
echo_hyperlink_in_output_window="y"
history_lines="1000"
hyperlink_adds_to_command_history="y"
hyperlink_colour="#0080FF"
indent_paras="y"
input_background_colour="darkgray"
input_font_height="9"
input_font_weight="400"
input_text_colour="black"
log_html="y"
log_input="y"
log_in_colour="y"
log_notes="y"
log_output="y"
max_output_lines="5000"
mud_can_change_link_colour="y"
note_text_colour="#040000"
output_font_height="9"
output_font_weight="400"
show_underline="y"
underline_hyperlinks="y"
unpause_on_send="y"
use_default_input_font="y"
use_default_output_font="y"
wrap_column="125"
write_world_name_to_log="y"
> <!-- end of general world attributes -->

</world>

</muclient>

(I left off forum codes to prevent conflicts).
Amended on Wed 24 Sep 2003 02:25 AM by Nick Gammon
Canada #1
[continued]

Since the above did not work, what I would like to see eventually is a seperate box on "Global Preferences | Defaults" that allows for the loading of default World settings.

Also, there are several options that I feel are colour related, and should be 'moved' to the colour section, particularly those above that have anything to do with colours and fonts.

The logline pre-amble and post-amble (above), plus file pre-amble and post-amble (not shown) can be a real pain to modify in every new world, and I would LOVE to be able to define them in a default file that loads for each world. The biggest problem is that I would like to be able to insert a tag in such code that would replace, for example %WorldName with the name of this particular world (Already stored in the "name" option - not shown), and the %PlayerName (already stored in "player" option - not shown). This should be fairly easy to accomplish by adding %worldname and %playername to the list of variables that are already available (such as %H and %M)

Another handy variable here would be %WorldsDir, which would be the "Default World Directory" from the "Global Preferences | Worlds" window.

Something else I have noticed, is that Custom Colours can not be modified in a world that loaded the Default Colours. I find this somewhat interesting. I would have expected that the colours COULD be modified, but would be stored in the World file. The Default Colours file would be loaded each time the world is initialized, but then any values in the World file would over-ride the default settings. This kind of behaviour would be a MUST for the other default files (I would expect). I haven't tried the other defaults out, so I can't comment on that.

Anyway, I'll wrap this up now. It seems like the new XML files are relatively stable, so hopefully adding these new features would not be too much of a bother, during this "upgrade" session. :)
Canada #2
Speaking of the logfile, I've noticed that text sent to the log file is sent as black text. I don't know why it chooses that colour.

I was about to suggest a function to WriteColourLog, but I also noticed that there is no line pre- and post-amble for lines sent.

The only reason I use World.WriteLog is to log World.Send commands:

Sub LogSend (SendString)
	World.Send SendString
	World.WriteLog SendString
End Sub


The better solution would be to automatically log anything sent to the mud using World.Send, as if it had been sent from the command line. (I.E: Use the "Commands" pre- and post-amble).
Canada #3
Argh, one more thing about logging:
I've noticed my log starts with the following:

Ages of Despair - Tuesday, May 21, 2002, 12:03 AM ------------------------------------------------- 

I would prefer if the line of dashes appeared on the second line, underneath the world name - date.

Oh, and I assume that text I mentioned was in black because I don't set a font colour in the file preamble header, and black is the default...?
Canada #4
LOL, Yet another subtle suggestion:

%z and %Z do the same thing.

I propose one of them be altered to display the time zone offset (and code, if possible), for example:

Times (on the left) are %z.
Times (on the left) are Eastern Daylight Time.

Times (on the left) are %Z.
Times (on the left) are EDT.

Times (on the left) are %z (%Z).
Times (on the left) are Eastern Daylight Time (EDT).

Times (on the left) are %#z.
Times (on the left) are GMT -04:00.

Times (on the left) are %Z (%#z).
Times (on the left) are EDT (GMT -04:00).
Amended on Tue 21 May 2002 07:18 AM by Magnum
Australia Forum Administrator #5
Quote:

Part of the reason I did this, is because most of the settings I tried to copy are also "colour" related. (Colour of commands echoed to the output window, etc...).

I found that the extra information in the file was ignored. I assume because the code that loads the colour file only looks for certain tags, and ignores all others. Here is my modified file (which DOES NOT WORK):


Correct, when loading colours it sets an internal mask so that only colour sections are processed, however one work-around right now is to use File -> Import, which gives you control over what is imported, and the default is everything.

What your posts touch on here is what I have been working on over the last couple of days, and that is file inclusion. In my current (development) version, this works right now:


<muclient>
<include name="mystandardsettings.xml" />
<world
  blah blah
 >
</muclient>


What is more, include files can include other files, so you can nest them. I think this will address most of your concerns about having to have "default" behaviour in each world, simply put it all in an include file.

However the problem that I am wrestling with, and will look at again tomorrow, is collisions. Let us say, for instance, you had a file like this:

  • Include "A" - "A" sets note text colour to "red"
  • Include "B" - "B" sets note text colour to "blue"
  • <world note_text_colour="green" >


Which colour should prevail? This is not like collisions of trigger names, colours like that don't have names, plus you can't not have one. There has to be a colour, even if it is the default.

Say you have an option, like this:


<include name="a" overwrite="y" />


The intention here is that by having "overwrite" set the include file would overwrite (override might be a better word) the main world settings. But what if you used the option twice, like this?


<include name="a" overwrite="y" />
<include name="b overwrite="y" />


And both "a" and "b" change the colour?

If you can make some sensible suggestions for working around this, they would be appreciated.
Canada #6
This is where my "virtual machines" theory comes into play.

Each "include" directive (I.E: Each XML File) runs in it's own "vitual mudclient machine". I posted much about this in the first XML thread.

Much of what can be configured could run in it's own virtual machine. For example:

Aliases
Triggers
Timers
Scripts
Some "formatting" (Like Note colour).
etc...

There are, of course, some things that should only be set in the "core" machine. For example:

Output screen formatting
Command box formatting
Logging
Connection info
Scrollback buffer
etc...

This may require a significant overhaul to the settings file:

Seperate CORE World settings to it's own XML file, and GUI interface.
Allow for loading "Default" core settings via interface (like you do now for colours, triggers, etc).

Provide a seperate interface for interacting with settings that can run in multiple "virtual machines". (Namely: Aliases, Triggers, Timers, Scripting).

In this second interface, the user would indicate WHICH "machine" (XML file) they are interacting with.

On world initialization, you would:

Load Default Colour file
Load Default Alias file
Load Default Trigger file
Load Default Timer file
Load Default Preferences file

Load World File. Override any Defaults if World File has "colliding" values. This is the "CORE" machine.

Load "Include" files. Ignore "CORE" settings (settings which should only be declared once, as above). Each "Include" file runs in a virtual machine, so collisions are NOT relevant.

The HUGE advantage to this method is that it supports "plugins" in a very versatile way. That is, each machine could run it's own script file, and they could even be programmed in different languages!

You would process mud output first through the CORE machine, then through the INCLUDE machines (probably in the order they were loaded). Each INCLUDE would have a turn at processing input and output to act on triggers/aliases, etc...

Critical CORE settings are only loaded in 2 places, the "Defaults" file, and the core "World" file. Collisions are handled by over-riding the Defaults file (which is natural, of course).
Australia Forum Administrator #7
Quote:

Something else I have noticed, is that Custom Colours can not be modified in a world that loaded the Default Colours. I find this somewhat interesting. I would have expected that the colours COULD be modified, but would be stored in the World file.


This was done before the include stuff. At the time, the defaults basically overwrote the world ones, so modifying them would have been pointless.

This takes us back to collisions, which colour has preference?
Australia Forum Administrator #8
Quote:

Speaking of the logfile, I've noticed that text sent to the log file is sent as black text. I don't know why it chooses that colour.

Not sure what you mean by that. Do you mean, from a script? - and, you mean HTML/Colour logging? If so, it should log in the current text colour on the screen. That is supposed to be how it works.
Amended on Tue 21 May 2002 10:05 PM by Nick Gammon
Australia Forum Administrator #9
Quote:

I was about to suggest a function to WriteColourLog, but I also noticed that there is no line pre- and post-amble for lines sent.

The only reason I use World.WriteLog is to log World.Send commands:


You can now get at the line pre and postamble with world.GetAlphaOption. Just modify your routine to use those. Plus, you could pull in the command colour while you were at it. eg. something like this:


Sub LogSend_HTML (SendString)
 World.Send SendString

 World.WriteLog world.getalphaoption ("log_line_preamble_input")

 World.WriteLog "<font color='" & _
            World.RGBcolourtoname _
            (World.GetOption ("input_text_colour")) & _            
            "'>"

 World.WriteLog FixupHTML (SendString)

 World.WriteLog "</font>"

 World.WriteLog world.getalphaoption ("log_line_postamble_input")
 
End Sub
Amended on Tue 21 May 2002 10:03 PM by Nick Gammon
Australia Forum Administrator #10
Quote:

Argh, one more thing about logging:
I've noticed my log starts with the following:


Ages of Despair - Tuesday, May 21, 2002, 12:03 AM -------------------------------------------------


I would prefer if the line of dashes appeared on the second line, underneath the world name - date.



Hmmm - that bug has been there for a while. I gather you are logging in HTML? I forgot to convert the world name to HTML, and put a <BR> before the hyphens, in HTML mode. If you edit the log file, you will see there is a newline before the hyphens, but that doesn't cause a newline in HTML.
Canada #11
WriteLog doesn't capture from the screen, so there is no colour set for it. You nailed it in your second post though...
Quote:

This was done before the include stuff. At the time, the defaults basically overwrote the world ones, so modifying them would have been pointless.

This takes us back to collisions, which colour has preference?

Under my proposal, the Output screen, and the Command box would use the "Core" machine's colours. The colours would not have to be a "Core" only setting though. A virtual machine could also have it's colour settings, and utilize them as well.

For example, a "virtual machine" could inherit the "core" machine's colours, but modify, for example, number 16, then use that colour for "World.Note" functions. If this seems to confusing, or you just see too many conflicts/collisions with this setting, you could mark it as "core" only, so that only the "core machine" would accept this setting. (It would be ignored in "Include" files/machines).

Under this latter scenario, there are two places to grab colour settings: The Default file, and the World file. Load the Default file first, then over-ride any colour settings if they are also set in the world file. Ignore colour settings in any other place.

Even nested "include" files would still run in their own "virtual mushclient machine", so you COULD load the colour settings just once more per VM, over-riding the [inherited] core machine's colours. The colours in the virtual machine's settings would only have effect in a few places though, like the "Trigger - Change colour" option, or World.Note, etc... Once again, main output and command box couldn't be effected through an "include" virtual machine, those would be set by the "core" machine.

Basically, any setting that can not tolerate collisions, you mark as "core only". They can only be set in two places: The Default file, and the Core file. The Core over-rides collisions from the Default. (You ignore that setting in any other XML file).

All other settings (and there should be plenty), can run in the "core" machine AND/OR "virtual machines".

Should I explain furthur using examples?
Australia Forum Administrator #12
Quote:

The biggest problem is that I would like to be able to insert a tag in such code that would replace, for example %WorldName with the name of this particular world (Already stored in the "name" option - not shown), and the %PlayerName (already stored in "player" option - not shown). This should be fairly easy to accomplish by adding %worldname and %playername to the list of variables that are already available (such as %H and %M)

Another handy variable here would be %WorldsDir, which would be the "Default World Directory" from the "Global Preferences | Worlds" window.



I have added the following:


Added extra options to the formatting strings for log file preamble, postamble, line preamble etc. These are:

  %N - name of current world
  %P - player name
  %F - default world directory
  %L - default log file directory
USA #13
Hmm.. Magnum's idea for color collisions is a good idea with respect to the main and world files, though this virtual machine stuff is confusing in as much as I am not entirely sure what he means. My suggestion would be this.. Make the defaults load, override them with the specific world files, but require that any changes to colors not in an actually script be added/changed 'only' by direct importation (which can warn of such changes, show a sample and ask if they really want the change), otherwise included color definitions would be simply ignored. Any other method bring up to many conflicts and if any future version that allows direct setting of a color within triggers is implimented this will become mostly irrelavent, since most 'standard' ansi color sets are left as is and only the custom set is usually modified, a feature needed for backward compatibility, but which won't be critical for triggers using a full color definition.
USA #14
One thing... If you do end up with a default file, then load a world file over it. It would be nice if there was also a few additional options:

DefaultNoteColorRGB for one example.

While you can set the note color any way you want in a script it is inconvenient having to look up the original in the world file in order to find the correct thing to set it back. Being able to change a setting and then later reset it to the default would be useful. Of course in this case the default would likely be whatever the world file defined as the 'normal' color and not the actual actual default (unless the world file lacks a raplacement for it). ;)
Canada #15
Cool! Thanks for implementing those, Nick.

Regarding this whole other "VM" thing:

First, you already have "virtual machines" in a sense, in that the client can be connected to multiple worlds (or the same world multiple times), and each runs with their own independant settings, etc.

When I talk about "virtual machines", I am thinking of the same thing, in most respects, except that each independant/virtual world settings space shares the command/box and output, and the "core" settings. This can be accomplished fairly easily by importing the "core" settings when you initialize the plugin/include code space.

If I were zapped into Nick's shoes, here's what I would do:

Create a new settings module, perhaps call it "formatting".
- Move "Echo my input in <colour>" to "formatting"
- Move "Note Colour" to "formatting".

Break the "World Details" GUI interface in two:
- Core/Main/Root/Base World Settings.
- World Interaction settings.

Core/Main/Root/Base World Settings:
- General | IP address
- General | Notes
- Appearance | Output
- Appearance | MXP/Pueblo
- Appearance | Sounds
- Appearance | Printing
- Input | Commands (Excluding input colour and SpeedWalking)
- Input | Keypad
- Input | Macros
- Input | Autosay
- Paste/Send | Paste
- Paste/Send | Send
- Scripting | Only the "Script Prefix" setting.

Interactive Settings:
- General | Connecting (Name, Password)
- General | Logging
- General | Timers
- Appearance | ANSI Colour *
- Appearance | Custom Colour *
- Appearance | "Formatting" * (See above)
- Appearance | Triggers
- Input | Commands - SpeedWalking ONLY
- Input | Aliases
- Scripting | Scripts (Except Prefix Character)
- Scripting | Variables

The Core settings could only be established in two places:
Global Preferences | Defaults.
Main/Core World file (which will over-ride Defaults).

The Interactive settings could be established in multiple places:
Global Preferences | Defaults.
Main/Core World file (which will over-ride Defaults).
Include files (run in a virtual machine).

You could then have multiple "virtual machines" acting on the same world input/output.

Each VM/Include inherits the Core settings, but NOT the Interactive Settings (from the Core file).

Items marked with a * can be over-ridden in VM/Include files, but the Core file MUST have default values, and certain aspects (such as Input/Output screen formatting) can not be over-ridden. (They are ignored). (I.E: Output from the mud will use the "Core" colour files, but Output from scripts/Triggers/Aliases may use colours further customized in their own VM/Include space.)

The GUI Interface for adjusting "Interactive" settings would allow you to dictate which machine's settings you are interacting upon. (By default the "Core" machine's).

I feel like I've went over this quite a bit. Hopefully, you comprehend what I'm trying to get across. If you want even more details, I'll be happy to go into even more detail, though I am pretty sure this thread is fairly comprehensive now. :)
Canada #16
I wrote that last posting while offline, & watching my home team the Leafs lose another hockey game. :(

Shadowfyr: I call them virtual machines, because it's a pull from Windows technology.

If you load a DOS program in Windows, it is run in what is called a "virtual machine". From the perspective of that DOS program, it has a whole machine to itself. You can open up a second DOS program at the same time, and from the second DOS program's perspective, it ALSO thinks it has a whole machine to itself.

What you see on the screen, and what you type at the keyboard is only directed to the DOS program that has focus. They each run in their own "virtual machine", but they share the same hardware, and even software drivers (though they don't know it).

When you open up Mushclient, each world has it's own "World Space". You can have a "heal" alias in one world, and a "heal" alias in another world, and they may do two completely different things. You could have a variable called "Target" in one world, and another variable called "Target" in another world, and they can have different values. In a sense, each world is running in a "virtual" environment (or machine), and you can have multiple environments open at the same time.

What I propose is to allow "virtual" environments to be open for the SAME world, much in the sense VM's operate under windows, except what is typed at the keyboard, and what comes in from the world, is processed by EACH VM, instead of just one. Understand? Good. :) ...Now go back and read this thread from the beginning (and the other one where I proposed this too). :)
Australia Forum Administrator #17
Hmmm, Magnum you have obviously put a lot of thought into this so I hate to pour cold water onto it :P but I do have a few problems with it ...

  • Complexity - I don't want MUSHclient to get so complex to configure that people start saying "ah yes, MUSHclient was good until it got too complex to use". This idea of virtual machines might appeal to programmers but for many users could just be confusing
  • Implementation issues - you still have collision problems as far as I can see. For example, there is only one command area, so if you have two aliases (say, called "shop") - one in each VM - and one does "north; west" and the other does "south; east" what happens when you type "shop"?
  • GUI changes - it will get more complex to configure, and you still have the issue of how do you represent in the GUI interface whether a variable (eg. note colour) is a default or a world override?
  • Time - if you said to me "I just want the new version with the bug fixes and the include files, and I'll wear the possible overwriting of values" I could have it out in 30 minutes.
    But if you want all the changes to support virtual machines - multiple script environments, multiple script files, more complex GUI configuration interface, and so on - then I might have to say "that'll be another month away".
    Then I ask myself "will the time taken to make all those changes be repaid in registrations"? I mean, are there (say) 500 people who have not yet registered, who will if MUSHclient offers virtual machines. I doubt it somehow.


I have been thinking about this at some length today, and think this will be a workable compromise ...

  • You can have any number of <include> directives. Each of them can have, if you wish, the general settings (the <world> ones).
  • When reading the include files any setting that is duplicated will simply take the later one. eg.

    <world input_background_colour="gray" > </world>
    <world input_background_colour="blue" > </world>
    <world input_background_colour="red" > </world>

    So, it is up to you to have reasonably well set-out include files. A simple method is simply to have a default world file as the include file, that way you simply edit that world to change your default settings.
  • Any value read from an include file (but not from the "main" world file) is marked as "an included value" - in a separate internal table.

    eg.

    input_background_colour="red" (included)
    history_lines="1000" (main file)
  • If you then present the same attribute in the main file it still overwrites the included value. This gives you the means to have different values for some fields (eg. world IP address).
  • In the case of triggers, aliases, timers and variables, the overwriting occurs (if it occurs at all) based on the label (name). eg. trigger label "a" will overwrite trigger label "a".


So far, so good. The general rule here is "the last value prevails".

Now, the important bit ...

  • When saving a world file, any value that was:
    • Originally found in an include file; and
    • Still the same value as in the include file

    is not written out to the main world file.

    If this wasn't done, then every value (eg. input_background_colour) would be written back to the main file, and then would override the defaults, which isn't what you would want, if you ever changed the defaults - that is, the include file.
  • You will have provision (eg. a button) to "revert to defaults" which will have the effect of changing every attribute back to the value from the include file. Thus, if you changed the input_background_colour to green, and wanted to put it back to the default (included) value, clicking that button would do it. If you wanted to go back to the MUSHclient defaults, you would simply throw away the reference to the include file (ie. un-include it).


I think these proposals are reasonably simple to understand, and easy enough to implement. :)
Amended on Wed 22 May 2002 04:37 AM by Nick Gammon
Canada #18
You'll pardon me for countering your points, if only for the sake of a productive conversation that serves as a history for why MUclient has evolved in the manner that it did. :)
Quote:

Complexity - I don't want MUSHclient to get so complex to configure that people start saying "ah yes, MUSHclient was good until it got too complex to use". This idea of virtual machines might appeal to programmers but for many users could just be confusing

A lot of what I propose appears complicated for the manner in which you may have to overhaul/upgrade the client, but in the end, the interface would not be all that much different. I'll explain more 2 points down.

Under the current implementation (or the one you propose), as soon as the user attempts to do some "advanced" actions, things can get very complicated. I am thinking especially of shared "plugin" scripts. Frankly, installing someone else's script is a bitch, because you have to edit your main script file (Something newbies are afraid to do) to modify the current "workaround" method for using an "include" script. Your method would still not allow multiple scripts be called from XML files, and further, all script files MUST be in the same language.
Quote:

Implementation issues - you still have collision problems as far as I can see. For example, there is only one command area, so if you have two aliases (say, called "shop") - one in each VM - and one does "north; west" and the other does "south; east" what happens when you type "shop"?

When installing 3rd party XML or Script files, the user (or the programmer) has to worry about Alias\Trigger\Timer\Variable collisions. This is NOT as much an issue under my suggestion. (Since each are stored in their own "world space"\"virtual machine"). Same trigger names? Who cares! They won't collide.

Yes, your example presents a problem. First one VM would act on the alias (moving "north; west", THEN the other VM would ALSO act on the alias (moving "south; east"). However, I expect movement collisions like that would be very rare. On the other hand, an alias like "status" might be common, and if you had one in each VM, you would probably PREFER (as the user) that they BOTH get processed, rather than only one (wrong) one. I feel my proposal actually servers to improve the collision situation, since they all get processed, rather than just one. (Isn't that better)?

When it comes to plugins, it may be very hard to predict what another programmer will do. (Erase variables, Triggers, Aliases?!). Not an issue with my proposal.

Heh... As a side note, if you had each VM configured to use slightly different "Input my commands in <colour>" setting, you could tell by the colour which VM's Script/Alias/Trigger had sent what commands. :)
Quote:

GUI changes - it will get more complex to configure, and you still have the issue of how do you represent in the GUI interface whether a variable (eg. note colour) is a default or a world override?

Breaking the "World Details" into two entities sounds complicated, but I believe it doesn't really have to be. You could still have icons to quickly access your alias, trigger, timer screens, etc... For the amateur user, they would likely only interact with the "Core" VM settings, and that's perfectly fine. It would be much the same as it already is. Besides seperate interfaces for "Core" and "Include" settings, you basically only need to add one more tool. Here's a crude ANSI drawing:

------------------------------------------------------
Configuration - Merentha
------------------------------------------------------
- Main Settings       |
|  - General          |
|  |   L IP address   |
|  |   L Notes        |
|  + Appearance       |
|  + Input            |
|  + Paste/Send       |
|  + Scripting        |
- EQ Plugin           |
|  + General          |
|  + Appearance       |
|  + Input            |
|  + Paste/Send       |
|  + Scripting        |
+ Spell Queue Plugin  |
------------------------------------------------------
Canada #19
In that example, you operate on the different values that belong to the different VM's by accessing the appropriate "tree" branch. Another way to do it might be to have a frame across the top of the settings dialog box with a pulldown box, where you select the VM you would like to operate with, and click one button to add a new VM, or another button to remove a VM. (A VM really being an "include" directive to use an additional XML file). I like this latter method better, since it works better with the icons (which would take you to the alias screen, and whatever was last set in the pulldown would still be active).

In either case, for the amateur, most of the time they would likely have the main XML file selected. If they want to add a plugin, it's just a few clicks. No fuss!
Quote:

Time - if you said to me "I just want the new version with the bug fixes and the include files, and I'll wear the possible overwriting of values" I could have it out in 30 minutes.
But if you want all the changes to support virtual machines - multiple script environments, multiple script files, more complex GUI configuration interface, and so on - then I might have to say "that'll be another month away".
Then I ask myself "will the time taken to make all those changes be repaid in registrations"? I mean, are there (say) 500 people who have not yet registered, who will if MUSHclient offers virtual machines. I doubt it somehow.

That's a hard point to argue, but I'll try. :)

"-Multiple script envirmonments, multiple script files". You already do this, in a sense. I can have 2 worlds open, each with their own complete environments. I don't know - how hard would it be to code starting a new environment, and loading the same XML world file, but ignoring anything in the "Interactive" category I mention earlier in the thread? Also, modifying your code so that instead of sending input/output to just one environment, you send it to all environments tied to this world?

You've shown that you are already capable of ignoring some settings from an XML file. (See the very start of this thread). The second point is a little more complex. I imagine it's a matter of making multiple calls to multiple environments, instead of one call to one environment. Hmm... Depending on your code, could be very easy, or very hard.
? -current- ?
Send "latest output line to trigger matching routine"
? -multiple VM's- ?
Send "latest output line to trigger matching routine - main VM"
Send "latest output line to trigger matching routine - plugin 1 VM"
? -- ?
Would be nice if it were that easy. :)

Anyway, to get more directly to your point, I can only offer this:

If your client program is the (or one of only a few) clients that allows very user friendly plugin support, it may serve as more of a "requirement" to mud users.

Suppose, for example, I refined by EQ script even more (to handle a new EQ feature on the mud), refined and made public my SpellQueue, and made other plugins useful for a particular mud, I might actually get to a point where I pull people over to using your client because it's just so damn easy to install plugins, and them users just got to have my nifty plugins!

The nice thing is, you could use multiple plugins no fuss, no worries about which language they were programmed in.

Then it's a matter of fostering a community of people capable and willing to write plugins. Who knows? Plugins could be so popular they are the leading reason people elect to use your client! (Muds with Admin supported plugins on their webpage would be amazing).

Now granted, ShadowFyr and I are two of the most vocal users here, and if it's just us two, you probably won't see any dramatic sales (if any) in the very near future. This is more an issue of potential, I guess.

(I think I've already gotten at least 10 people from AOD to use Mushclient - wonder if any have registered?) ...and they don't even use my plugin for that mud. :)
Amended on Wed 22 May 2002 07:05 AM by Magnum
Canada #20
One more rebuttal, and I don't mean to be rude. You DO rock. :)

Seems to me, the compromise that you propose seems somewhat complicated itself. In order for an end-user to use multiple XML files, they have to be weary of ANY setting collisions. If there are collisions, it may result in expectations not being met. ("I typed this alias and nothing happened!", "I programmed this trigger in my plugin XML, but it won't execute!")
Quote:

Any value read from an include file (but not from the "main" world file) is marked as "an included value" - in a separate internal table.

Any way to view these via interface? If you have multiple internal tables, isn't that just one step shy of what I proposed? (Each internal table is essentially the "environment"/"Virtual machine", isn't it?). ...or did you just mean only certain values (I.E: NOT triggers, aliases, variables)?
Quote:

You will have provision (eg. a button) to "revert to defaults" which will have the effect of changing every attribute back to the value from the include file.
Thus, if you changed the input_background_colour to green, and wanted to put it back to the default (included) value, clicking that button would do it. If you wanted to go back to the MUSHclient defaults, you would simply throw away the reference to the include file (ie. un-include it).

Sounds dangerous! All of the settings revert? ("Damnit, all the variables configured in my plugin XML file just reverted back to their 'empty'/ititialized defaults!") Err, where were those variables stored anyway? The main XML file, or the Plugin? ("Damnit, all those triggers I disabled just got re-enabled!").

Heh... Ok, I've made my points, I think. If you are firm in your position, Nick, I won't keep pushing my proposal. Of course, I'll adapt to whichever method you choose, as will the rest of us. :)
Australia Forum Administrator #21
Quote:

You'll pardon me for countering your points, if only for the sake of a productive conversation that serves as a history for why MUclient has evolved in the manner that it did. :)


This is a good point. Sometimes it is interesting to know why something was not done, whereas release notes usually describe what was done.

While you were writing that I was getting things a bit more organised internally (options into an internal table including default values), which hasn't really committed me yet to any particular direction.

Whilst I am quite keen on your point about easy plug-ins, I don't quite see how this interacts with the VM concept.

OK, I suppose if you type "shop" and an alias in VM 1 picks it up, then that word could be echoed in a different colour - cute I suppose, although the idea of switching these VMs maybe for every alias and trigger is a bit daunting - I think you might get a speed penalty.

Without getting bogged down in whether or not we want virtual machines, or whatever, let's explore this plugin concept a bit more, and see where it takes us.

I can see the usefulness of (say) a wilderness walker plugin (snippet) being distributed as a single file. This might require:

  • Some triggers
  • Some aliases
  • Some variables
  • Some global settings (eg. scripting on), aliases enabled etc.
  • Some script routines


The new "import" function addresses all that (excepting maybe trigger/variable/alias name collisions). The only real remaining problem is incorporating scripts, maybe in different languages.

Now, making new script machines probably isn't too hard, as it does it anyway for different worlds. So, a snippet (plugin) could look like this:


<triggers>
  <trigger
   custom_colour="1"
   enabled="y"
   expand_variables="y"
   match="@target falls to the ground"
   name="target_falls"
   regexp="y"
   script="vm1:OnTargetFalls"
   sequence="100"
  >
  <send>kill @target</send>
  </trigger>
</triggers>
<script vm="vm1">

sub OnTargetFalls (a, b, c)

' blah blah

end sub

</script>


The important bits here (and they are just ideas right now) is that the trigger calls a script in vm1 (hence vm1:OnTargetFalls), and then the script tag defines the script that belongs to that vm. I use "vm" loosely, "namespace" might be just as good a term, or "script engine".

We could probably work around collisions by a simple system of registering some prefix (eg. "magnum_") so that each scripter has to use a different prefix, and it is their job to be internally consistent. Thus you might have variables "magnum_hp", triggers "magnum_trigger_1" and so on.

It wouldn't have to be that long-winded, every forum user could, on request, be assigned a 4 character code, for example.
Australia Forum Administrator #22
Quote:

Any way to view these via interface? If you have multiple internal tables, isn't that just one step shy of what I proposed? (Each internal table is essentially the "environment"/"Virtual machine", isn't it?). ...or did you just mean only certain values (I.E: NOT triggers, aliases, variables)?


That would be simple enough. Right now you can view the internal table of configuration names.

Anyway, what I meant was one-off attributes (eg. text colour), not multiple things like triggers. So, "revert to default" would basically restore default colours, trigger enable flag, stuff like that. Things that only occur once.

Even then, it could be limited to the configuration screen you are on, with a bit more work. Eg. you are viewing the Output configuration screen, and want to go back to defaults, so you hit a small button that simply restores what you see right there, to their default values.
Canada #23
Interesting. I'm still trying to get a handle on what this XML stuff is capable of, so perhaps there are aspects of my proposal that were premature.

Quote:

Whilst I am quite keen on your point about easy plug-ins, I don't quite see how this interacts with the VM concept.

Now, making new script machines probably isn't too hard, as it does it anyway for different worlds.

This is really what got me started on the idea. It seemed that a previous limitation was that you could not run multiple scripts because there was only one "script machine" per world. As you say, you could do it for different worlds though, so I concluded: "Let one world have multiple script machines running!". I am under the assumption (though I have never tried), that I could open one world that uses VBScript, and another that uses JScript, hence my theory that if one world could use multiple script machines, they could be in different languages. :)

BTW, I don't really like the term machine either... I just used it for lack of any other term I could think if.

Anyway, I thought: If you're gonna make a seperate namespace for scripting, may as well do it for triggers\aliases\timers\variables. Everything stretched from there. I actually figured that part would be relatively easy for you to program, it simply required sending the input/output to multiple "MUclient engines" instead of just one.

Another reason for including plugin triggers\aliases\timers\variables (Man, need an acronym for that - LOL) is that I assumed there would be some confusion as to which script engine a particular trigger should use. Wrapping them together in the same virtual space seemed the logical conclusion. (I do note your solution now).

Finally, it seemed to me that confining each plugin's settings in it's own space would serve to actually improve the collisions situation, by virtually eliminating the issue, save that one example you poked me with. :)

...and all of this could be accomplished relatively easily by just modifying your input/output interface with your internal Mushclient engine so that the input/output gets sent to multiple places, instead of just one.
Quote:

OK, I suppose if you type "shop" and an alias in VM 1 picks it up, then that word could be echoed in a different colour - cute I suppose, although the idea of switching these VMs maybe for every alias and trigger is a bit daunting - I think you might get a speed penalty.

In my mind, a "VM" will usually be a bundle of aliases and triggers. Maybe this is a bit simplistic, but I thought: Call the trigger matching routine that checks 120 triggers once, or call it twice, first checking 100 triggers, then checking 20 triggers - Is there much difference?
Quote:

Without getting bogged down in whether or not we want virtual machines, or whatever, let's explore this plugin concept a bit more, and see where it takes us.

Yes, Sorry, I know I didn't really do that so far in this message.

How about plugin removal?

Where are MUSHclient variables (created by the script) stored?
If the script uses World.AddAlias or AddTrigger or AddTimer, where are those stored?

Will there be an easy way to "cleanup" all that was added by an included XML file and it's script, should you want it removed? (I ask because I expect my method would store all of that in the plugin XML file, so remove the file, and everything related is gone). (And as a potential plugin programmer, I want to know if I would have to write cleanup routines).

Ok, all of that aside, your proposal does seem workable to me now that I understand it better. Your Prefix idea seems like something a smart programmer should do, though I can't say I'm thrilled at the idea of relying on the programmer to do so. Even myself, I sort of complied, by making all variables in my EQ script start with "EQ", but that's still too generic. (Just checked, I missed using a prefix on one, Doh!)

I'm not sure which proposal was the "harder" way to do things, but since it's your baby, I'll trust that you know best. :)
USA #24
Hmm.. Well first to Magnum..

Since multiple VMs can be setup for different script and even different languages there is no real reason to create extra overhead by using multiple VMs for each include. This would probably eat more time than the text positioning stuff every one keep arguing about and really isn't that necessary.

To Gammons concerns...

Last includes colors override everyone elses... Bad idea. Even if a way is provided when adding a new one to change it's position relative to others in the load process I really don't wan't someone elses file overriding my own world file settings. My suggestion of have to directly import the xml (or maybe even just an option of 'import color tables') would be far better, since it won't touch my own settings unless I specifically ask it to do so.

Collisions.. Simple, don't assign users a unique ID, generate one when the xml is loaded and propagate it through all the connected bits. So all triggers, aliases, etc. would get XXXX_ tagged onto that at world load and any script routines loaded with it would also be tagged the same way, both in the 'loaded' script and the triggers, aliases, etc. that reference them. That way no actually change needs to be made to anyones scripts or triggers, etc. The client just tags the first included bits with 0001_, the second with 0002_, etc. The added time to do this automatically is negligable unless some fool keeps hitting the 'reload' script button or something. Hmm.. Which brings up the fact that you would need to keep track of 'where' each one was loaded from and the number tacked onto it so if a reload is requested they get retagged properly. It seems to me that this is definitely a better way to do things than making everyone attached their own ID to them.

While this doesn't fix alias problems like you 'shop' example, such collisions are likely to happen no matter how you deal with them. It is better to simply warn the user that duplicates of that sort exist, that they could pose a problem and let them handle the adjustments. Otherwise you run the risk of overriding one they need with one that doesn't do anything they find useful. Knowing that the collision exists lets them delete or edit them as needed, but simply loading both and not warning them or overiding one with the other is a bad idea.

Default colors.. I cannot imagine one single instance when I will ever need to 'reset to default' colors or any other settings. Doing so is very likely with triggers and other things to have unexpected and unwanted results and may have the same with the colors (though having that option 'may' be useful as long as you can also 'undo' it if you hit it by accident). I may need to reset a custom color in a script back to that set in my world file or the defaults that MUSHClient uses, but never the entire table. I can't frankly imagine anyone else wanting to either. What is needed is to have a base 'default' color settings and a world file color setting and the ability to do stuff like NoteColorRGB Default, NoteColorRGB World or NoteColorRGB 'Your color', from inside the script where they are most useful. Currently have have to either hunt the value down or guess, neither of which is very helpful.
Amended on Wed 22 May 2002 06:42 PM by Shadowfyr
Australia Forum Administrator #25
Quote:

Last includes colors override everyone elses... Bad idea. Even if a way is provided when adding a new one to change it's position relative to others in the load process I really don't wan't someone elses file overriding my own world file settings.


My proposal was that you would have:

  • General includes (eg. default triggers, settings)
  • Plugins or whatever you call them
  • The main <world .... > </world> attributes


Since the main attributes would come last, by definition, then it would be impossible for a plugin to change (say) the IP address of the world.

I suppose what might happen is that a plugin changes (say) the world.note colour, which you had set up in your earlier include file. One simple solution, and probably the obvious one, is to have your include files last in the list (ie. after the plugins). I think I would have the ability to change the include list order, which in any case you could do by editing the world file.

Quote:

How about plugin removal?

Where are MUSHclient variables (created by the script) stored?
If the script uses World.AddAlias or AddTrigger or AddTimer, where are those stored?


All of those things (triggers, aliases, timers, variables or TATV for short, or should that be VATT?) are stored in "maps" (hash-table lookups) as part of the main world document.

As for plugin removal, read on.




What I propose now as a tentative plan, which I will attempt to do, is allow some sort of plugin concept in include files. This will:

  • Establish a namespace, to avoid collisions for VATT names, although not necessarily their function (eg. you might still have two "shop" aliases).
  • Establish a virtual machine, or scripting engine space, into which the scripts could be placed


The idea is it should be simple to use, so this is what I imagine it to be ...



<plugin name="Magnum's wilderness walker"
        language="vbscript"
        author="Magnum"
>
<description>
This lets you walk through the wilderness blah blah ...
</description>
<use>
To use it type BLAH followed by BLAH
</use>
</plugin>

<!-- This has established the start of a plugin - one per include file I would suggest -->

<triggers>
  <trigger
   custom_colour="1"
   enabled="y"
   expand_variables="y"
   match="@target falls to the ground"
   name="target_falls"
   regexp="y"
   script="OnTargetFalls"
   sequence="100"
  >
  <send>kill @target</send>
  </trigger>
</triggers>

<aliases>
  <alias
   match="gems"
   enabled="y"
   ignore_case="y"
   name="gems"
  >
  <send>insert gems into statue</send>
  </alias>
</aliases>

<script>
<![CDATA[
'
' Using CDATA means you don't have to worry about > signs 
'  in the script.
'

sub OnTargetFalls (a, b, c)
 world.note world.getvariable ("blah")
end sub

]]> 
</script>


OK, what this will do is:

  • Generate a namespace prefix (eg. P1 for plugin 1) automatically.
  • Any labels found in the plugin (and perhaps labels are mandatory?) will be prefixed by the namespace prefix. So we would have:
    • Trigger: P1:OnTargetFalls
    • Alias: P1:gems
    • Variable: P1:blah

    Since the character ":" can't be used as a normal label, you can't "fudge" a forged namespace.
  • Any calls (in scripts) to AddTrigger, GetTriggerInfo, SetVariable etc. will automatically have the namespace prepended, so you will avoid name collisions, without really having to work at it.
  • The contents of each <script> directive will be concatenated to make a "total script string", which will be passed to the (appropriate) script engine for parsing (the script engine specified in the <plugin> tag).
  • Perhaps - if the script won't parse, the entire plugin is discarded. That might be for the best. Otherwise you might have "dangling" triggers and aliases that should do something, but don't.
  • The reason for concatenating scripts snippets is so each (say) trigger can have its own script handler under it, which seems to me to be a neater way of developing the plugin.
  • With a bit of work, if there is a parse error, MUSHclient could work out which line of which include file actually caused the error (effectively un-concatenating the script snippets).


You would still potentially have behaviour collisions (eg. two triggers that match on the same text) however that should be pretty good. With the stuff like author, description, etc. in the <plugin> tag, you could have a simple "installed plugins" window that lists plugins and what they do.

Uninstallation would simply involve removing each VATT that matches the plugin namespace, and discarding the script.
USA #26
Ok. Looks good..

I would still like some way to tell the client within a script to reset a color to the one in the world file or even the default though, currently you can set the color in a script, but not return it to the previous state without knowing the correct color definition. Of course since it uses the custom color table right now that is not much of a problem, but when/if triggers and stuff has a 'color picker' option added this will become a real problem, since unless you leave it as one of the existing custom colors you won't have any obvious default color to return to. For mine I had it set to use the 'normal' color and in that case you still can't tell it to reset to the original color without knowing the specific value to use.
Canada #27
Again, interesting.
Quote:

Establish a namespace, to avoid collisions for VATT names, although not necessarily their function (eg. you might still have two "shop" aliases).
Establish a virtual machine, or scripting engine space, into which the scripts could be placed

Why not make life simpler for yourself, and just store the Plugin's VATT stuff back into the Plugin XML file? (Instead of the main world XML file).

Do that, and you've pretty much met the proposal I had offered.

I don't know what kind of overhead you have in writing this new code. In a way, Establishing these new namespaces is very similar to establishing a new namespace for a different connection/world.

As a final step towards my proposal, you clone the "core" settings I mentioned earlier (from the main/core XML file), and throw them into this new namespace as well, and you've got the thing I was calling a "virtual machine" which is just the label I came up with for "namespace".

So far, you have a private namespace for the script engine, and a private namespace for the VATTs. Why bother assigning them names? Just link the two namespaces together, and you're set. I.E: The triggers from the Plugin VATTs aren't going to conflict with the core VATTs. Have them ONLY use the "linked" script space. You don't need to worry about collisions, since both the VATT information and it's linked Script namespace are independant from the core/world namespace [and any other plugin]. (What's going to collide?) Saves you the hassle of rewriting all your functions to provide prefix labels!

The reason for throwing in the other "core" settings, is that it simplifies things for you, and just helps make the plugin namespace more robust. Each plugin\XML file kind of runs like it's own world file, independant of the others. You would not need to modify any of your functions. (Only the way a namespace is initialized, and interacted with).

I'm sorry, I didn't mean to turn this into a rant to do things my way. It just seems like you are kind of heading down the path anyway, and I guess I can't help but see my full proposal as easier. I've got to remind myself that there are things I don't know, and what I guess "might" be very easy for you, could in fact be somewhat difficult.

The one downside to my proposal, is that if a Plugin/Include is kind of like a whole new world (except sharing the input/output of a root world), then that Plugin's XML file will be altered as it is used... In such a case, you would not want to share your own [modified] plugin file with someone else. (You should instead share a "virgin" plugin).

The other downside, is that you would not have interaction between the namespaces unless you programmed a special interface... but you kind of already have that since it is already possible to interact with another world environment. (Isn't it)?
Amended on Wed 22 May 2002 11:53 PM by Magnum
Australia Forum Administrator #28
Can you clarify:

  • Which colour you are referring to? An ANSI colour? A custom colour? World.note colour?
  • What would you reset it to? The MUSHclient default? The colour found in the latest include file? The colour directly after the world was loaded?
Australia Forum Administrator #29
I appreciate that you are probably trying to make things easier for me, but the fact is that multiple namespaces won't really work like that. The problem is that the scripting interface, that everyone is accustomed to, has a single point of interaction with MUSHclient, namely the "world" object, which is a COM object which internally represents the world document, of which there is only one.

No matter how much you try to move into different engines, or namespaces, there will remain some things of which there can only be one, eg. the output buffer, which is accessed by GetLineInfo.

Here, for instance, is the implementation of world.getvariable, which you see is attached to the world document ...


VARIANT CMUSHclientDoc::GetVariable(LPCTSTR VariableName) 
{
CString strVariableName = VariableName;
CVariable * variable_item;
VARIANT vaResult;

  VariantInit(&vaResult);

  vaResult.vt = VT_NULL;

  // return if bad name, if so return NULL
  if (CheckObjectName (strVariableName))
    return vaResult;

  vaResult.vt = VT_EMPTY;

  // see if variable exists, if not return EMPTY
  if (!m_VariableMap.Lookup (strVariableName, variable_item))
	  return vaResult;

  SetUpVariantString (vaResult, variable_item->strContents);

  return vaResult;

}


Since I would need to keep a world document, to make namespaces work in the way you suggest would mean moving things like getvariable to a different com object (the namespace, or engine, object), thus requiring a lot of script changes.

However my proposed change would merely require that, if a trigger which is part of a plugin calls a script, it sets up a global variable (for the world) which is the current namespace name, then with a one-line addition to getvariable, it will access the correct variable, something like this:


strFullName = strNamespace + strVariableName;


In this case "strNamespace" might be "P1:" or might simply be empty for non-plugins.
Amended on Thu 23 May 2002 12:42 AM by Nick Gammon
Canada #30
Hmm... Ok, I'm trying to get a grasp, but I have to admit, I'm somewhat in the dark when it comes to C programming, and "com" objects.
Quote:

namely the "world" object, which is a COM object which internally represents the world document, of which there is only one.

...particularly when you say that. I don't know what limitations that represents. I can only make educated guesses.

The procedure that takes the most recent line received from a mud, is that inside the COM object, or outside? Same question for the procedure that handles a line sent from the command box?

Triggers can fire on world.note's, right? It's actually possible to emulate what I am talking about, at least as far as triggers are concerned:


MainWorld Trigger: *
MainWorld script: Communicate_everything

Sub Communicate_everything (a, b, WorldLine)
  PluginWorld.Note WorldLine
End Sub
---
PluginWorld Trigger: Whatever you want...
PluginWorld Script: WhatEver

Sub WhatEver (a,b,c)
  'Do Whatever your plugin should do
  MainWorld.Send "Whatever you want to send"
End Sub

You can see in that example, I could set up a dummy world completely independant of the main world, and wouldn't have to worry about conflicts, etc. I would have to do the inter-world communications manually though.

So, it comes down to how you handle incoming information from the mud and the command box. Oh, I've been a bit stupid... I guess how you handle output to go to the mud comes into play as well. Your posted source code is somewhat useful, but what I just mentioned is what actually comes into play.

In order to do what I have been proposing, the answers to the following questions must be yes:

Can you take a line recieved from the mud, and internally send it to the trigger matching routine of a different world?
Can you take the line entered into the command box, and internally send it to the alias matching routine of a different world?
Can you take all "send" data from a world, and internally send it to another world?

Back in my DOS days when I learned programming, you had to do everything yourself... and in this case, I could send "IncomingMUDline", "IncomingCommandLine", and "OutgoingMUDLine" where I want. Do you still have that control?

If so, it's basically just a matter of creating a "dummy" world that only recieves lines from it's mother world, and only sends lines to it's mother world. The client interface is then the project that becomes an issue, as I've posted in previous messages.

...If the answer is "No" to any of those three questions, then I am done with this line of conversation from my end, and you won't have to put up with it anymore. :)

I'm trying to think of how to conclude this, but my mind is in a boggle. I give up, for now... unless you post something that leads me to believe it's still possible. (like those 3 Q's being "Yes").
Canada #31
Quote:

Since I would need to keep a world document, to make namespaces work in the way you suggest would mean moving things like getvariable to a different com object (the namespace, or engine, object), thus requiring a lot of script changes.

Not in that case. It's actually intentional that GetVariable only has direct access to it's own [plugin] world object. (That's what keeps the plugin worlds independant, and makes collisions a non-issue).

As my previous post states, you have to intercept input/output though, mimicking communication from multiple World.Objects

Guessing you have this subroutine, in c though:

Function MatchTrigger (LinetoSearch, WorldObjectMakingRequest)
  Search through all triggers for this world and see if there is a match. Take action accordingly.
End Function

When a line comes in at the mother world, you would make multiple calls to the above routine. Once for the motherworld, and once for each child world, of course passing the appropriate WorldObject in each case.

From there, everything should be directed appropriately.

When a child world wants to send a line to the mud, you intercept it internally and route it to the mother world instead.

Much of the child world would be useless (irrelevant), but that's ok, because much would also be useful. I attempted to break up the useless and useful in an earlier post.
Amended on Thu 23 May 2002 03:56 AM by Magnum
USA #32
>Which colour you are referring to? An ANSI colour? A custom
>colour? World.note colour?

>What would you reset it to? The MUSHclient default? The
>colour found in the latest include file? The colour directly
>after the world was loaded?

Well in my case I found that the prompt line on AoD doesn't include a reset or it was being ignored after a series of notes, so I set the note color to grey, then back to the normal note color. My point is that if you don't use a custom color, but leave it blank or later when you do support an any-color option you run into the same problem I did:

world.NoteColor white
do something...
world.NoteColor ??? - Ooops!

There is currently no way to restore it to what it was when you first connected, unless you specify a custom color. If you don't there is literally nothing to set it back to, unless you know the specific RGB color value that it had before you changed it. This is imho not very helpful. Yes.. having thought about it you could retrieve the current color and then set it to something else, then restore it with the original, but considering the idea of multiple script plugins you run into the possibility that someone elses script will change the color and 'not' restore it. Then any attempt on your part to get the original value so it can be restored is lost. It would be better if there was a way to use a 'default' color (stored in the world file). Later if you impliment another method than the current custom color table this will become an even bigger issue.

Of course you can also set a mushclient variable to include the value so you can recall it, but then you still have to figure out what it is/was before something changes it and set the variable to that value. This is a bit rediculous since it is the 'only' color that doesn't have a preset that you can look up someplace.

The other colors... I don't know about. I assume (maybe incorrectly) that changing those within the script doesn't change the values 'in' the world file, but I imagine that you still can't do something like:

world.customcolourtext (14) = world.customcolourtext (14)

and expect it to do anything. lol IMO there needs to be a way to reset the colors to what they where when the world first loads -without reseting all of them-.

So I would say the answers are -

Any color you can set in a script.

The way they are the moment you click on 'connect', since that -should- be the default setting at that moment. ;)

Why have defaults or world file settings at all if they are permanently lost to the client the moment a script changes them? Which is essentially what happens if you can't tell it 'change this back to the way you had it before' without having to directly edit the colors from the menus.
Canada #33
It's too bad it looks like my proposal is non-workable, ShadowFyr, for that wouldn't be an issue. Each Plugin World could have it's own note colour, independantly stored in it's own WorldObject.
USA #34
>Each Plugin World could have it's own note colour,
>independantly stored in it's own WorldObject.

Yes, but could you tell the program to set it to any of those from a script? Answer - No, since you still wouldn't be able to recall the color that it was set to once you changed it to something else in the script. The problem is not who sets it so much as once set by the script you lose the original setting permanently till you close the world and reopen it. This is inconvenient and your idea would only make things worse if the standard note color could be set to anything execept 'normal' or a custom color. The scripts under your version still wouldn't have any way of knowing 'what' the color was for notes if it was the one that changed the 'local' one any more than it would a global setting. As it is the assumption the client makes in all cases is that there is only one value and that one is the 'current' one. Using multiple name spaces would have meant than none of them knew there own defaults, each others, the main worlds or the basic defaults defined in the main client settings. This would be an improvement?!?
Amended on Thu 23 May 2002 05:45 AM by Shadowfyr
Australia Forum Administrator #35
Quote:

My point is that if you don't use a custom color, but leave it blank or later when you do support an any-color option you run into the same problem I did:


world.NoteColor white
do something...
world.NoteColor ??? - Ooops!


There is currently no way to restore it to what it was when you first connected, unless you specify a custom color.


I presume this is pseudo code, and you really mean NoteColourRGB. Anyway you can do that right now, very simply, like this:


dim oldfore, oldback

  oldfore = world.NoteColourFore  ' what are they?
  oldback = world.NoteColourBack
  world.NoteColourRGB vbRed, vbGreen ' change them
  world.Note "blah blah"  ' do the note
  world.NoteColourRGB oldfore, oldback ' restore them

' or if you really do just mean "notecolour" just do this ...

dim oldcolour

  oldcolour = world.notecolour
  world.notecolour = 3
  world.note "eep"
  world.notecolour = oldcolour  ' restore custom colour #


Anyway, I have added a number of new routines for the main variables (things inside <world> ) which are:

* GetLoadedValue (name) - returns the value of any <world> option directly after loading the world
* GetDefaultValue (name) - returns the MUSHclient defaults for the named option
* GetCurrentValue (name) - returns the current value right now of the named option.

eg.

world.note world.GetDefaultValue ("port") ' 4000
world.note world.GetLoadedValue ("port") ' 5000
world.note world.GetCurrentValue ("port") ' 6000


I haven't yet extended that to things that have multiples, like colours.
Amended on Thu 23 May 2002 06:10 AM by Nick Gammon
Australia Forum Administrator #36
What I have described might sound tedious, for every note you do, but you could encapsulate it, like this:


sub mynote (sText)

dim oldfore, oldback
dim newfore, newback

'
' my preferred colours are in variables
' 
  newfore = world.getvariable ("myforeground")
  newback = world.getvariable ("mybackground")

  oldfore = world.NoteColourFore  ' what are they?
  oldback = world.NoteColourBack
  world.NoteColourName newfore, newback ' change them
  world.Note sText  ' display the text
  world.NoteColourRGB oldfore, oldback ' restore them

end sub


Then in your script (plugin) you would just use:

MyNote "blah"

.. which is actually less typing anyway than:

world.Note "blah"

That's assuming that you want plugins to change the colours from what the user normally has, an assumption I wouldn't necessarily make.
Amended on Thu 23 May 2002 06:12 AM by Nick Gammon
Australia Forum Administrator #37
Quote:

Each Plugin World could have it's own note colour, independantly stored in it's own WorldObject.


You seem to be making the assumption that it can be easily done, or done at all, to make lots of World Objects.

I don't quite see how this would work, without a major rewrite. The world object is the MUSHclient session for a particular world. In a script it is a COM reference to that. I can't really start spawning off different "world objects" like that. There are some things (like count of lines in the history buffer) that really cannot occur twice (with different values, anyway).

So, it would have to be a different COM object, like the world_vm object. Then things that are relevant to the VM would have to move to them, and others not. This is a big change and I don't really see the benefit at the end of the day.
USA #38
Yes I realize you can retrieve the color and then set it back to that. My point is that, once you allow other peoples scripts to plug into your world file, you lose control over this. They can have a script that sets the note color to some specific value and 'assumes' that when they need it at the original color they can simply set it back in some similar fashion, in the mean time your own script gets 'stuck' with the change they made, with no way to restore it to the original color. You could add code to the world.connect script to automatically pull the value, but the point is you shouldn't have too and there may be cases where it may not do what you expect, since you can't be sure what the value is at the time it connects (it could have changed just prior to your connection breaking and not been changed back, etc.). It shouldn't be necessary for us to add some gludge code to our scripts to make sure everything works the way we expect it to, instead of getting messed up by someone that didn't do it the right way.
Australia Forum Administrator #39
Quote:

Triggers can fire on world.note's, right?


No, triggers fire on incoming text from the MUD, aliases fire on what you type, and nothing fires on notes.

Quote:

Can you take a line received from the mud, and internally send it to the trigger matching routine of a different world?


Ah, not exactly. I suppose a trigger could do this:


sub mytrigger (sName, sLine, wildcards)
world.getworld ("my-other-world").send sLine
end sub


This would send it to the other world, and it would then go through the trigger matching routine, however this seems a bit circuitous.


Quote:

Can you take the line entered into the command box, and internally send it to the alias matching routine of a different world?


Not really. I suppose you could use a similar technique to the above, but use "setcommand" instead of send, but it is not really doing that.

Quote:

Can you take all "send" data from a world, and internally send it to another world?


No, I think not.

You are trying to set up multiple virtual machines, aren't you?

The basic MUSHclient COM object is the document, or "world". It is the thing that has:

  • A TCP/IP connection to a MUD site
  • Some attributes, like whether things you type are echoed
  • A scrollback buffer, part of which you see in the output window
  • Some trigggers, aliases, timers, and variables, which are owned by that world
  • A scripting environment, if enabled.


I simply can't see multiple "worlds" that belong to the same "world" being a viable concept, let alone a useful thing to program. Maybe, if I had thought of it early enough, aliases and timers etc. could have been grouped into "groups of things", however I didn't, and to change it now would render lots of existing scripts invalid.

The best I can do, and I think it will work quite well, is to do something like the plugin concept, and also things like "groups of triggers" (eg. all triggers labelled with a group identifier "xyz"). Then you might enable a group, disable a group, delete a group. But basically they would be in the same list.

What I suggest is, rather than going down the path of inventing things like virtual machines, describe how a plugin might be expected to work, and in what way it doesn't work now. For instance, for me the major sticking blocks, right now, from doing a plugin are two things:

  • Possible name collisions for things like trigger labels, alias labels, script names, and variable names
  • The tedium of trying to explain how to take a snippet of VB code an add it to the existing script file, enable scripting, select the appropriate script file, and so on.


My proposed snippet idea addresses both of those. Name collisions would be resolved for triggers etc. while they were loaded, and each snippet would have its own script engine space.

I can't get too excited about different spaces for things like colours, I think it will just be confusing. However, I see what you are saying about notes. What I have done, then, is added two new routines, designed to simplify sending nice coloured note messages, without worrying about saving and restoring colours. Now if every plugin author uses those, the problem pretty-well goes away. If they don't, you can bop them. ;)


'
' Send a note in nominated text/back colours
'
World.ColourNote "red", "blue", "Warning - HP are low"
'
' Change text colour only
'
World.ColourNote "red", "", "Tick almost up"
'
' Note without a newline
'
World.ColourTell "blue", "gray", "Food running "
World.ColourTell "red", "gray", "LOW"


These two routines restore the colours afterwards, so they don't permanently change the environment.
Amended on Fri 24 May 2002 05:34 AM by Nick Gammon
Canada #40
Ok, first off: I have accepted that my long running proposal/rant will not be implemented.

Given that, it might be wise to move the "serious" discussion to a new thread.

Second: (And I mean this!). Do not read this post unless you have time to waste!... or feel like having a laugh at my expense as I prove my tagline is true.

As I said, some very "advanced" programming can sometimes be above my head:

I assume that an "object", is essentially a collection of data. I.E: It is not the functions/subroutines that process the data, but the data itself.

I assume that MUSHclient has a series of functions/subroutines to process "object" data.

I assume that any particular function/subroutine is not replicated for each "WorldObject".

Examples:

Sub LookForMatchingTrigger (Linetoprocess, WorldObject)
  Loop through all the triggers that belong to this WorldObject
    Did the trigger fire?
      React
  End Loop
End Sub

Sub LookForMatchingAlias (Linetoprocess, WorldObject)
  Loop through all the aliases that belong to this WorldObject
    Did the alias fire?
      React
  End Loop
End Sub


Something like the above pseudocode seems more sensible than cloning the actual executable subroutines/functions for each world object... So I assume that is the way it is done inside MUSHclient.

Using the same kinds of assumptions:

Somewhere in MUSHclient is the communication code for exchanging information with the target connection (mud). Text is grouped by "lines".

It seems likely that when a line is recieved, it is "pushed" into the world object's space as a new line (to be buffered), also, the "LookforMatchingTrigger" routine is called, passing along the appropriate World Object name as an argument.

NewLineReceived
-Put it into the appropriate WorldObject space
-LookforMatchingTrigger

The Command Input box is identical for every World that is open. Again, you don't clone this code in memory, do you (for each world)? That would be inefficient, so I'll assume you don't. Instead, it probably uses a single "GUI" interface code, that passes input lines to other functions/subroutines, probably passing the WorldObject as an argument.

InputCommandGUI
(Line is recieved)
LookForMatchingAlias
Was an Alias Matched?
Yes? - Do Nothing (You don't act on an alias AND send the input line to the world)
No? - Send the Line to the world.

Ok. Now, "Send the Line to the world" is probably also a unique function/subroutine, (not cloned in memory for each WorldObject).

SendLineToWorld (Line, WorldObject)
Which World is This?
Write to World's log file (if activated)
Gather communication info needed from that WorldObject's memory. (IP address, etc).
Transmit the line to the appropriate mud.

Zowie! I wonder how much of that I was wrong about...?
Quote:

Correct, when loading colours it sets an internal mask so that only colour sections are processed,

Quote:

Break the "World Details" GUI interface in two:
- Core/Main/Root/Base World Settings.
- World Interaction settings.
Canada #41
(continued)

Changes to MUSHclient:

That happens later (Second quote), but you also build "internal masks" for each of the two groups. (See previous message).

For the "mother" World:
Quote:

On world initialization, you would:

Load Default Colour file
Load Default Alias file
Load Default Trigger file
Load Default Timer file
Load Default Preferences file

Load World File. Override any Defaults if World File has "colliding" values. This is the "CORE" machine.

Does the "mother" world have any "include" [child] files declared? If so...

-Initialize a new world object
-Load the "Mother" world's XML file, apply "Core" mask, only importing "Core" settings. (Now cloned in child world).
-Load the "Child" world's XML file, apply "Core" mask, ignoring "Core" settings.

You now have a mother world, as normal, and a child world with the same "core" settings as it's mother, but with it's own "Interactive" settings. (VATTs, plus some other stuff). I am just talking about the data here, not the GUI or Functions/Subroutines that process the data.

NewLineReceived
-Put it into the appropriate (MotherWorld) WorldObject space
-LookforMatchingTrigger(MotherWorld)
-Does this WorldObject have any children?
Yes? -LookforMatchingTrigger(ChildWorld)

Note, you do NOT plug the received line into the child world's buffer as well... there is no need. (Argh, there is now with some of the new functions you have, like "GetLineNumber" or whatever.)

InputCommandGUI
(Line is recieved)
LookForMatchingAlias(MotherWorld)
Does this WorldObject have any children?
LookForMatchingAlias(ChildWorld)
Did any of the worlds have a matching alias?
Yes? - Do nothing (since alias already acted)
No? Send the line to the mud.

SendLineToWorld (Line, WorldObject)
Which World is This?
Is this a ChildWorld?
Replace Childworld identifier with Motherworld's identifyer
Is this a Motherworld?
Write to MotherWorld's log (if activated)
Gather communication info needed from that World Object's memory. (IP address, etc).
Transmit the line to the appropriate mud.

Some notes:

A Child world does not use a tcp/ip connection (or any connection outside the computer)
A Child world does not need an "interface" like a standard world, except as I mentioned in a previous post, regarding the GUI "settings" dialog box(es), which could be a pain to re-write.
A Child world never recieves lines from anywhere... only the LookupAlias and LookupTriggers are called on it's behalf, which are all that are required.
A Child world has all it's output intercepted, and send to the Motherworld instead.
I realize now, any script function that looks up information from any of the "buffers" (Output, Command) may not work, unless it can be written to look at the Mother World's object instead. :(
The above summerizes most of the "work" I thought would have to be done.

Well, this is completely out of my system now. If you've got the time, feel free to "teach" me the error of my ways. I won't be offended if you've got better things to do. :)

Again, I know now this was too far-fetched to ever work out... Since I put so much time into it already, I felt like wasting a little more time to journal exactly what I was thinking might be worthwile, if only for myself to get it out of my system. Also, if anyone feels like "teaching" me, I could benefit from such knowledge. :)

This time, I really intend to drop it, unless, perhaps, you think what I just wrote was brilliant, and you want to pursue it afterall. LOL! :) :)
Canada #42
Quote:

The world object is the MUSHclient session for a particular world. In a script it is a COM reference to that.

Quote:

I assume that an "object", is essentially a collection of data. I.E: It is not the functions/subroutines that process the data, but the data itself.

Sigh. The folly in my logic.
USA #43
Thanks Gammon. That solves the major problem. It doesn't do much if some one else mangles the colors first, but this was kind of what I thought the note color should have worked like in the first place. It made no practical sense to me for any color change to it to necessarilly be permanent in the first place. ;)

To Magnum -> Yikes... I am beginning to think we are both complete nuts. You with your design suggestions for the client, me for my totally insane ideas about how some guilds might work for AoD. The problem in both cases is that the ideas are not really practical without rewriting a 'big' chunk of the basic code that the systems run on. lol For my code ideas this is frustrating though since any other method would be a major hack, but 'fixing' the problem in AoD's case would require a new attribute(s) for each room and someone to go back and 'set' every single one of them. With about 34 areas, 10-30 rooms each = 340-1020 rooms (probably more), it ain't happening, even if there was someone that could make all the needed changes. Time for both of us to step back and reconsider. ;) lol
Canada #44
Yup, we are both nuts. Sometimes I wonder if we help Nick, or hinder him. :)
Australia Forum Administrator #45
Quote:

I assume that an "object", is essentially a collection of data. I.E: It is not the functions/subroutines that process the data, but the data itself.

I assume that MUSHclient has a series of functions/subroutines to process "object" data.

I assume that any particular function/subroutine is not replicated for each "WorldObject".


Ah, I see where the confusion is coming from. ;)

MUSHclient is written in C++, which encapsulates both the data and the functions into a single thing, called an object. Although the actual executable code is not duplicated, as you surmised, it is, in effect, attached to a particular world. Here is a simplified portion of the MUSHclient world object ...


class CMUSHclientDoc : public CDocument
{

// Attributes
public:
  CChatSocket* m_pSocket;  // TCP/IP connection

// stuff saved to disk

  CString m_server;   // IP address
  CString m_mush_name;  // world name
  CString m_name;  // player name
  CString m_password;  // password

  // normal (ANSI) colours
  COLORREF m_normalcolour [8];
  COLORREF m_boldcolour [8];

  // custom (user-defined) colours for triggers etc.
  COLORREF m_customtext [MAX_CUSTOM];
  COLORREF m_customback [MAX_CUSTOM];

// list of output buffer lines

  CLineList m_LineList;  // linked list of output lines

  __int64 m_nBytesIn;   // bytes received
  __int64 m_nBytesOut;  // bytes sent

// Operations
public:
  BOOL ConnectSocket(void);  // connect to world
  void ProcessPendingRead();  // process new stuff
  void SendMsg(CString strText,  const bool bEchoIt,
       const bool bQueueIt = false);  // send text
  void ReceiveMsg();   // have received a line
  void DisplayMsg(LPCTSTR lpszText, int size, const int flags);  // add to output window
  void ShowStatusLine (const bool bNow = false); // update the status line

// exposed script routines ...

  afx_msg void Note(LPCTSTR Message);
  afx_msg long Send(LPCTSTR Message);
  afx_msg long Connect();
  afx_msg long Disconnect();
  afx_msg long DeleteAlias(LPCTSTR AliasName);
  afx_msg long EnableAlias(LPCTSTR AliasName, BOOL Enabled);

  };


So, say we have a world object, called "WorldObject", instead of your suggestion, to use it:


LookForMatchingTrigger (Linetoprocess, WorldObject);


We actually do:


WorldObject->LookForMatchingTrigger (Linetoprocess);


I suppose it is a minor point in a way, but the point is that the data is really "part of" the COM object (the COM object being the world object), so that the thing you call "world" in a script really points to not only the routines that you use (such as Note) but the data as well.

For a simple example of how those script routines are written, look at this:


long CMUSHclientDoc::GetReceivedBytes() 
{
  return m_nBytesIn;
}


This implements the World.GetReceivedBytes script routine. Notice there is no reference to where the data is coming from (no reference to a world object)? This is because it is implied, the function knows that it is currently referring to the current world object, whichever one that happens to be at the time.

Thus it is not particularly easy to break apart the world data from the world functions.
Australia Forum Administrator #46
Quote:

On world initialization, you would:

Load Default Colour file
Load Default Alias file
Load Default Trigger file
Load Default Timer file
Load Default Preferences file

Load World File.

Override any Defaults if World File has "colliding" values. This is the "CORE" machine.


Actually this is how I envisage it working, except that we will do away with the so-called default files, and replace them with includes. After all, a default file is really preloaded data.

What you describe here is really the core machine I was talking about that the GetLoadedValue script would retrieve.

However I am now planning to implement a subset of the world for the plugins, a subset being the amount reasonably needed to execute a script (eg. its own language) but not things that you can't really have more than one of (eg. default background colour).

So we are perhaps more in agreement than you think. ;)
Canada #47
Nick, thank you for taking the time to teach me about this subject. I've put off learning C++ for a long time, because I've always found it's syntax very unfriendly. (It's lack of the kind of natural language syntax like many other languages, I mean).

I dropped programming 15 years ago just as I was starting to learn about "object" implementation under Turbo Pascal. (And I've just picked up programming now, with the use of MUSHclient).

Continued in new thread:

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=1299&page=999999