Addendum 5/5/03: Previously I stated below that the bugs mentioned in this class session are fixed in version 4.1 b20. Well, you fix one bug and wouldn't you know it, you create another. :/ So, version 4.1 b25 is now the latest, most-fixed version. :D
The scheduled topic for this session was databases, but first we spent a lot of time finishing up material from the previous class, involving the action queue...
Magine: hi :)
ZebLith: hello magine :)
Magine: i need to take a few moments, i just had a beverage spill
here :D
ZebLith: oops! >_< okies, no problem. ^^
Magine: back
ZebLith: welcome back :)
Magine: never set drinks by the computer, heheh
Magine: well, we left off last time where i was explaining about
deleting items from a list, within a loop
Magine: so let me finish that before we start on the official topic
of today's session
Magine: the example [to delete items that begin with "a" from a
list] i gave was this:
RESETLIST $mylist;Magine: but, as i said, that would not work right because the list would be changing as you went thru the loop
@ct=@itemcount[$mylist];
FOR i 1 @ct {
@n=@nextnum[$mylist];
$x=$litem[$mylist,@n];
if @eq[$left[$x,1],"a"] {
FREEITEM $mylist @n }};
QSTATE 0;Magine: so what happens here is this,
RESETLIST $mylist;
@ct=@itemcount[$mylist];
FOR i 1 @ct {
@n=@nextnum[$mylist];
$x=$litem[$mylist,@n];
IF @eq[$left[$x,1],"a"] {
ENQUEUE { FREEITEM $mylist @n }}};
QSTATE 1;
QSTATE 0;[Mags]: Counting 1
FOR i 1 10 {
ENQUEUE {
SAY $fmt["Counting %s",@i] }};
QSTATE 1;
Magine: ok, now add a second enqueue
command in there
Magine: so it should read:
QSTATE 0;
FOR i 1 10 {
ENQUEUE {
SAY $fmt["Counting up %s",@i] } };
FOR i 10 1 -1 {
ENQUEUE {
SAY $fmt["Counting down %s",@i] } };
QSTATE 1
| Oops! A bug in Magsbot (now fixed in version 4.1 b25) caused some incorrect results with the examples during class. Rather than cause confusion by presenting those bugs as they happened and then trying to explain why, I've cheated a bit and changed the log to show what the bot should have done, and what the newer, repaired version will do. The green text below is the "revised" version of the chat log, that show the correct result. |
[Mags]: Counting up 1
[Mags]: Counting up 2
[Mags]: Counting up 3
[Mags]: Counting up 4
[Mags]: Counting up 5
[Mags]: Counting up 6
[Mags]: Counting up 7
[Mags]: Counting up 8
[Mags]: Counting up 9
[Mags]: Counting up 10
[Mags]: Counting down 10
[Mags]: Counting down 9
[Mags]: Counting down 8
[Mags]: Counting down 7
[Mags]: Counting down 6
[Mags]: Counting down 5
[Mags]: Counting down 4
[Mags]: Counting down 3
[Mags]: Counting down 2
[Mags]: Counting down 1
In this case, the SAY commands still executed sequentially, in the same order they were enqueue'd, because they all were placed in the same queue.
If, however, the code was changed to this:
QSTATE 0;...then the result would be as follows, because each loop would place the SAY commands in a different queue, causing the commands to be executed simultaneously:
FOR i 1 10 {
ENQUEUE {
SAY $fmt["Counting up %s",@i] } };
@q=@queue+1;
FOR i 10 1 -1 {
ENQUEUE @q {
SAY $fmt["Counting down %s",@i] } };
QSTATE 1
[Mags]: Counting up 1
[Mags]: Counting down 10
[Mags]: Counting up 2
[Mags]: Counting down 9
[Mags]: Counting up 3
[Mags]: Counting down 8
[Mags]: Counting up 4
[Mags]: Counting down 7
[Mags]: Counting up 5
[Mags]: Counting down 6
[Mags]: Counting up 6
[Mags]: Counting down 5
[Mags]: Counting up 7
[Mags]: Counting down 4
[Mags]: Counting up 8
[Mags]: Counting down 3
[Mags]: Counting up 9
[Mags]: Counting down 2
[Mags]: Counting up 10
[Mags]: Counting down 1
Magine: but you see what happened?
Magine: even though you added items [in the order] 1,2,3...9,10,10,9,8,...1
Magine: the bot executed the items at the same time
Magine: because two different queues were created
Magine: the first queue got SAY 1..10 and the second queue
got SAY 10..1
Magine: but both queues executed "at once"
Magine: so let me explain a few details about queue numbers
Magine: first of all, the number you specify will only be used
if a queue with that id already exists,
Magine: but if a queue with that number doesn't already exist,
Magine: then the number you specify will be ignored and queue ID
will be automatically generated instead
Magine: also, if you give -1 for a queue ID
Magine: like ENQUEUE -1 { rem do something }
Magine: then a new queue will always be created.
Magine: and finally, you can find what the queue ID really is,
using the @queue function,
Magine: OUTSIDE OF the enqueue command.
Magine: the @queue function tells you the queue ID of the most
recently created queue.
Magine: like:
enqueue { rem do something };Magine: let's add that to our example
report $fmt["queue number=%s",@queue];
QSTATE 0;As you can see below, the first loop puts all its commands into queue 0, and the second loop puts all its commands into queue 1.
FOR i 1 10 {
ENQUEUE {
SAY $fmt["Counting up %s",@i] } };
REPORT $fmt["queue=%s",@queue];
@q=@queue+1;
FOR i 10 1 -1 {
ENQUEUE @q {
SAY $fmt["Counting down %s",@i] } };
REPORT $fmt["queue=%s",@queue];
QSTATE 1
[Mags]: queue=0
[Mags]: queue=1
[Mags]: Counting up 1
[Mags]: Counting down 10
[Mags]: Counting up 2
[Mags]: Counting down 9
[Mags]: Counting up 3
[Mags]: Counting down 8
[Mags]: Counting up 4
[Mags]: Counting down 7
[Mags]: Counting up 5
[Mags]: Counting down 6
[Mags]: Counting up 6
[Mags]: Counting down 5
[Mags]: Counting up 7
[Mags]: Counting down 4
[Mags]: Counting up 8
[Mags]: Counting down 3
[Mags]: Counting up 9
[Mags]: Counting down 2
[Mags]: Counting up 10
[Mags]: Counting down 1
But what if you made these changes?
QSTATE 0;If you did, the result would be this:
FOR i 1 10 {
ENQUEUE -1 {
SAY $fmt["Counting up %s",@i] } };
REPORT $fmt["queue=%s",@queue];
FOR i 10 1 -1 {
ENQUEUE -1 {
SAY $fmt["Counting down %s",@i] } };
REPORT $fmt["queue=%s",@queue];
QSTATE 1
queue=9
queue=19
[Mags]: Counting up 1
[Mags]: Counting up 2
[Mags]: Counting up 3
[Mags]: Counting up 4
[Mags]: Counting up 5
[Mags]: Counting up 6
[Mags]: Counting up 7
[Mags]: Counting up 8
[Mags]: Counting up 9
[Mags]: Counting up 10
[Mags]: Counting down 10
[Mags]: Counting down 9
[Mags]: Counting down 8
[Mags]: Counting down 7
[Mags]: Counting down 6
[Mags]: Counting down 5
[Mags]: Counting down 4
[Mags]: Counting down 3
[Mags]: Counting down 2
[Mags]: Counting down 1
Note the queue numbers 9 and 19...what is that all about? :D Well, the -1 forces a brand new queue to be created for each ENQUEUE command, therefore the first loop creates queues 0 to 9, so the first REPORT gives you the number of the last queue created by the first loop, which is 9; then the second loop creates queues 10 to 19, so the second REPORT gives you the number of the last queue created by the second loop, which is 19.
Magine: what happened with the -1 in there was,
ZebLith: yeah, I saw that. :)
Magine: that *every* enqueue command created a new queue
Magine: so that i ended up with 20 separate queues!
ZebLith: ooooh O_O
Magine: which all executed "at once"!
Magine: the first loop put
Magine: say 1 in queue 0
Magine: say 2 in queue 1
Magine: say 3 in queue 2
Magine: and so on
Magine: and the second loop continued,
Magine: putting
Magine: say 10 in queue 11
Magine: say 9 in queue 12
Magine: say 8 in queue 13 etc
Magine: so that when queue processing was turned on again
Magine: all 20 queues went at once!
Magine: but since the bot can, of course, only say one thing at
a time,
Magine: it came out appearing sequential
Magine: queue 0 executed ("say 1"), queue 1 executed ("say 2")...etc
Magine: all the way to
Magine: queue 18 executed ("say 19") ... queue 19 executed ("say
20")
Magine: which points out another issue
Magine: about how queues are executed in parallel.
Magine: of course the program can't really do two different things
at once,
Magine: (even your computer can't...even when it seems to, it's
really just rapidly switching between tasks, doing one program instruction
at a time.)
Magine: but in the same way that your computer appears to be doing
several things at once by "timesharing"
Magine: (that is, doing one thing from each program at a time)
Magine: magsbot can appear to be doing several things at once by
taking one item from each action queue at a time.
Magine: so if you have, for instance, 2 queues going
Magine: magsbot will execute one item from the first queue, then
one item from the second queue, then back to the first, etc. until they
are both empty
Magine: does that make sense?
ZebLith: yes, perfect sense. anything else?
(At this point I explained some things about the bug that resulted in improper results, but I've left that out of the log.)
One thing about queues I forgot to mention in class and will add here, is that although items added to a queue are normally broken up into individual actions so they can be executed one at a time and allow alternating between executing items in other queues, any Magsbot commands that have clauses in { braces } are executed as if they were a single command, even if they contain many commands within them. In other words, a command like IF, FOR, WHILE etc. will execute non-stop, and items from other queues will not be executed until everything in the { } clause is finished. Thus if you want many commands to execute simultaneously, you should put the enqueue command within the loop, not the other way around. If you did this (moving the ENQUEUEs outside of the FOR loops):
QSTATE 0;...it would have no practical value because you would only be putting one item in each queue. The entire contents of each FOR loop would be treated as a single item. Two queues would be created, but each one would only have one item, so the effect would be no different than one queue with two items, executing sequentially. That is, the second FOR loop in the second queue, wouldn't execute until the first FOR loop in the first queue was completely finished, so the ENQUEUE-ing would have no effect.
ENQUEUE {
FOR i 1 10 {
SAY $fmt["Counting up %s",@i] } };
REPORT $fmt["queue=%s",@queue];
@q=@queue+1;
ENQUEUE @q {
FOR i 10 1 -1 {
SAY $fmt["Counting down %s",@i] } };
REPORT $fmt["queue=%s",@queue];
QSTATE 1
| And darned if I didn't discover a different bug when I tested the code above! This time the problem was with the FOR loop: when put inside an ENQUEUE command, the increment of the FOR loop (that's the optional forth argument, where you specify how much to add to the counter variable each loop, like FOR i 1 10 2 to count by twos), which is assumed to be 1 if it's omitted, was being treated as 0, so the loop would continue indefinitely...or until the user pressed F12 to break out of it. This bug is also fixed in version 4.1 b25. |
Magine: well, we only have a half hour left for the topic that was
originally scheduled for today, databases, so maybe i should put that off
til next time and add another class :D
Magine: is everything clear concerning queues?
ZebLith: yeah, queues are clear. but, if you don't mind adding
another hour to class, I really would like to learn about databases today.
Unless you have something else to do of course.
Magine: ok, i guess we can do that
Magine: so...databases...
Magine: i'm not really an expert on database stuff in itself,
Magine: but as far as how magsbot implements database use,
Magine: it connects to databases using an ADO (active database
objects) component
Magine: so you can use any kind of database that ADO supports.
Magine: you can find some information about ADO connection settings
for different kinds of databases at http://www.able-consulting.com/ADO_Conn.htm
ZebLith: okay, loading it up.
Magine: once you find the right connection setting for the kind
of database you want to use,
Magine: you would use the @dbopen function in magsbot to create
a handle to that database.
Magine: like
Magine: @@mydatabase=@dbopen[$Provider,$Connection,$Table]
Magine: that would create a global variable @mydatabase that you
would then use with the other magsbot database functions and commands.
Magine: or, if you want to see the data in the magsbot database
window,
Magine: you would use the DBPROVIDER, DBCONNECT and DBTABLE commands
Magine: that is, the DBPROVIDER and DBCONNECT commands specify
the database to use in the magsbot database window,
Magine: while the database handle created with the @dbopen function,
Magine: is used to manipulate the database within a magsbot program.
Magine: for example,
Magine: suppose you want to use a MS Access database on your computer...
Magine: looking on the ADO page there,
you can see that for an "OLE DB Provider for Microsoft Jet"
Magine: (MS Jet = the driver for Access)
Magine: on the web page it says
oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _Magine: so for magsbot you would use all that for the connect string.
"Data Source=c:\somepath\myDb.mdb;" & _
"User Id=admin;" & _
"Password="
DBPROVIDER "";(note there are no line breaks within the quoted part of the above example)
DBCONNECT "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\somepath\myDb.mdb;User Id=admin;Password="
DBTABLE "mytable"
Magine: of course you would substitude your own database filename
for myDB.mdb and your table name for mytable.
Magine: now for an example, the magsbot zip package contains a
MS Access database named mydatabase.mdb
Magine: and the correct settings are already in magsbot.ini, but
they are turned off.
Magine: to turn them back on, edit magsbot.ini and find the [Database]
section
Magine: and remove the semicolon ; from in front of
;Provider=Microsoft.Jet.OLEDB.4.0Magine: you may also find Provider= and Connect= (with no string specified), and if so just delete those lines.
;Connect=Data Source=MyDatabase.mdb;Persist Security Info=False
;Table=MyTable
; set ADO according to the version of ADO you are usingMagine: for example for ADO 2.1 you would put ADO=2 in the [Database] section of magsbot.ini
; 0=1.5
; 1=2.0
; 2=2.1
; 3=2.5
; 4=2.6
; 5=2.7
ADO=5

(Note: make sure it says MyTable in the Table box.)
Magine: got it working?
Magine: and BTW, for the benefit of those reading the class log,
Magine: if you don't have ADO on your computer, or if you want
a more recent version,
Magine: you can get it free at http://www.microsoft.com/data/download.htm
Magine: but if you have windows XP then you probably already have
ADO
ZebLith: hmm, I got "The application requested an operation on
an object with a reference to a closed or invalid Connection object."
bleh... lemme look back through this.
Magine: ok
Magine: did you make the changes in magsbot.ini, so in the [Database]
section you should have:
Provider=Microsoft.Jet.OLEDB.4.0Magine: exactly that, since we are now using the example database included in the magsbot zip
Connect=Data Source=MyDatabase.mdb;Persist Security Info=False
Table=MyTable
Provider=Magine: and for connect, put
Connect=Driver={Microsoft Access Driver (*.mdb)};Dbq=MyDatabase.mdb;Uid=admin;Pwd=Magine: (my awb word wrapped that, but it should be all on the same line)
Table=MyTableZebLith: that did it! :D
@@MyDatabase=@dbopen["","Driver={Microsoft Access Driver (*.mdb)};Dbq=MyDatabase.mdb;Uid=admin;Pwd=",MyTable];Magine: name the button "Open MyDatabase" or something like that
DBCLOSE @MyDatabaseMagine: and if you want, you can create a row at the top of the behavior table
dataset @MyDatabase {Magine: there
dbtable MyTable;
dbactive 1;
if @dbfindfirst[$fmt["Name='%s'",$avname]]=0 {
dbappend };
dbedit;
dbfield Name=$avname;
dbfield_ ID=@atr[avatar_citizen];
dbfield_ LastVisit=@now;
dbpost };

Magine: then click Set again and make sure the data reappears
ZebLith: it did.
Magine: ok, then click your Open MyDatabase button again
ZebLith: okay
Magine: and let's leave [the world] and return, in order to test
the AVATARADD line we added
Magine: so
Much chaos ensued at this point, as ZebLith's bot was giving him a lot of error messages for no reason we could determine at first. After prolonged messing around (which I've omitted here since it's not very instructive or easy to follow), during which we verified that ZebLith had the right code and the right lines in the .ini file, restarted the bot, tested this and that, I finally concluded that although the database file appeared to be opening correctly, in fact a non-existent file was appearing to be opened (and with no error message from the database driver to reveal the problem!), due probably to a Windows 98 incompatibility with the Access driver.
Magine: maybe it's a version thing
Magine: win 98 you're running?
ZebLith: yep, first edition. maybe next time I should move
my files over to my brother's computer, which has winME. the only
reason we use 98 here is become mother insists upon it.
...
Magine: i guess it must be a version problem, i can't see anything
else it could be
Magine: i think what is happening is
Magine: that the @dbopen function is not really opening the right
file
Magine: maybe it's trying to create a new file
...
Magine: so i think what is happening, is that somehow win98 doesn't
recognize the file
Magine: so you get some kind of not-really-opened database
ZebLith: lol, with win98 i wouldn't doubt it.
Magine: maybe if you put the entire path for the database name,
instead of just MyDatabase.mdb
ZebLith: well, I've held you here for far longer than planned.
>_< Family is getting irrate about how I've been on for almost
four hours... Perhaps we can test this theory next time, and I'll
try moving everything onto a winME computer?
Magine: ok
Magine: email me when you get a chance and tell me how it goes
:)
Magine: magine@turtleflight.com
Magine: this will make for a strange class log :D
ZebLith: okay. thanks again for all your help, I really appreciate
it :)
Magine: yw, glad you could make it :)
Magine: see ya later...
ZebLith: *waves*
to be continued... :D