Part I Recap
In the previous installment, I covered a lot of ground very rapidly with minimal explanation.
I'll try to improve on that in this and future articles. I doubt I will cover
all the things I could; there is way too much going on and I hate writing documents!
A few people that tried the new and improved emotions bar seemed to like it. I didn't explain
how to modify the the xml - I left it up to you to figure that out on your own. I'm starting
to see that these mods could be useful to a larger audience if properly packaged. A little polish
here and there and some documentation would bring a release up to snuff. Maybe I'll get around to
that some day.
Onward
Now that I am working on multiple mods, I clearly need to re-use and share a lot of code between them.
I have been wrapping up functionality into a nice happy easy to use .as (ActionScript)
file that simplifies building a Flash mod. I'm releasing this code so that you can include it in
your Flash mods. This is a work in progress that I'll be updating and adding to.
See the sidebar for a quick link to download the code. I guarantee that
by the time you download it, it'll be obsolete. I'm not going to bother explaining the "convoluted" callback
style it uses to get business done - it works around ActionScript's lousy XML parser.
I thought it would be fun to make an avatar respond to commands from other users. Instantly, a
dozen or so practical uses for a bot came to mind. Designers could use a bot to hawk their wares
while they were "offline". The bot could be commanded to model clothing on the fly so the
prospective buyer could see what it would look like on an actual avatar. Bots could be used for
information purposes - spamming the area with notices about events. Clearly, a bot could be annoying
as hell too. Oh what fun!
FlashIM
Chat history and IMs are handled by FlashIM.swf. There is one instance of FlashIM always running for
chat history mode. When IMs come in, There spawns new instances of FlashIM to handle each seperately.
The There client doesn't keep track of spawned instances very well though. As you are probably aware,
if you send a second IM request to someone you are already IMing with a second IM window gets spawned. Lousy.
Incidentally FlashIM is also the only flash app in There that appears to support keyboard focus.
Obviously, I'm going to have to rewrite FlashIM. I don't particularly care for the one in There anyway.
The windows don't resize and there is no way to send chat text from an text input bypassing the the direct-to-balloon
interface. Great, I can have my fancy bot and a new and improved UI for IM and Chat! The question
is, can I send text from the FlashIM window out to a balloon?
Step 1 - Sending Text to a Balloon
It turns out addChatText doesn’t work from FlashIM. The callbacks are dependent on
being called from specific Flash apps. Thankfully, http://127.0.0.1:999 exposes a way to
simulate an FSCommand. Good thing for work-arounds. I can call
http://localhost:9999/GuiService/simulateFsCommand and trick There into thinking I am issuing my command from
another running Flash app. Sport.
Now all I need to do is grab the runtime name of the emotionsbar
(since I know addChatText works from it). Every time There is run, it generates a new unique name for
all the Flash apps running. A quick call to http://localhost:9999/GuiService/listFlashMovies will give me
a list of all the running apps (like FlashIM, a global instance of EmotionsBar is always running).
_sEmoteBarName = "";
ThereGetMovieFullName( "emotionsBar", 1 );
function ThereGetMovieFullNameOnLoad( sLongName, iCallBackNdx ) {
switch( iCallBackNdx ) {
case 1: {
_sEmoteBarName = sLongName;
break;
}
}
}
I can use the name of the EmotionsBar to make a simulated call to addChatText. Along the way I found a “bug” –
FsCommandArgs expects you to be exact. If you forget to add the param name (in this case text=) it will crash
There hard core.
// good business
//
ThereSimulateFSCommand( _sEmoteBarName, "addChatText", "text=Hello World" );
// bad business - bye bye there.exe
//
ThereSimulateFSCommand( _sEmoteBarName, "addChatText", "" );
Rad, it looks like I’ll be able to make a bot using this "trick".
Step 2 - User Interface
I'm going to need a nice UI that supports Chats, IM and bot configuration. Since FlashIM appears to be the
only flash app in There that allows for keyboard focus, I'll probably want to add all sorts of good stuff here
in the future that needs text input. For example, I'd love to build a front end to the emotionsbar so it can be
customized without touching the XML. Time to throw some tabs on the UI.
There are two things about the chat in There that bother me: 1. I can't type into a text box before I send off text and
2. I can't resize the damn IM/Chat History window. The first problem is already solved now that I know I can
send text to a balloon. The second problem could be a little bit trickier.
When a Flash app is loaded up in There, it needs to communicate with There and tell it two things: 1. how much vertical
and horizontal real estate it could potentially use (the stage), and 2. the current height and width of the app.
Let's look inside one of my wrappers:
function ThereInit(iStageWidth, iStageHeight, iWidth, iHeight) {
getURL("FSCommand:setStageWidthHeight", "width=" + iStageWidth + "&height=" + iStageHeight);
getURL("FSCommand:setTextureBitDepth", "depth=32");
getURL("FSCommand:setWidthHeight", "width=" + iWidth + "&height=" + iHeight);
}
It's possible to change these values on the fly! Resizing a window at run-time turns out to be not so tricky
after all. I have no idea why There's window doesn't resize. The only challenge is to dynamically
expand the graphics in the UI on the fly. Cake. (See Figure 1 and Figure 2).
My first UI was mocked up in Photoshop and brought into Flash. Boy was that a mistake. Flash and its
wonderful vector graphics engine butchered the hell out of my pixel-perfect bitmaps. It just didn't look right at
runtime (see Figure 3). I was forced to scrap all my resize work and go build my UI in Flash (See Figure 4).
Armed with the knowledge that I could resize the vector graphics on the fly as easily as bitmaps, I went ahead
of shelved the resizing for now.
Step 3 - Snagin' Incoming Text
The next problem to solve is how to grab incoming text. This turned out to be not much of a problem
at all. Ripping apart the data coming in couldn't be easier. FlashIM uses a very simple XML
"protocol". Fortunately there is no need to scour through the code, following all the loops and whatnot to
reverse engineer the XML. There is an xml file (im.xml) that they are using in debug mode right in the
directory for all to see. Of course, end users are never in debug mode. This file shouldn't
even be here - someone should give the the build manager a whipping, or a talking to.
The XML structure is very simple, and has a pseudo-SOAP feel to it. Curiously, the color tag is not
currently being used.
<Answer>
<Result>1</Result>
<version>2</version>
<messageData>
<msg>
<color>0x00000</color>
<authorName>Someone</authorName>
<text>Hello World</text>
</msg>
</messageData>
</Answer>
To get a batch of new messages call http://localhost:9999/VersionedXmlSvc/CommMessageData.
The call expects an Oid parameter, which is either 1 for Chat History, or the unique id of the current IM session.
You can pull the ID for the current window's instance right from one of the "global variables" set on the
There client.
_iGroupID = _root.There_GroupId;
A first time call looks like this:
// get the chat history
//
http://localhost:9999/VersionedXmlSvc/CommMessageData?Oid=1
// get the IMs for the current instance
//
http://localhost:9999/VersionedXmlSvc/CommMessageData?Oid=_iGroupID
After the first call, you need to pass the LastVer parameter to get the next batch of new messages.
This value can be ripped from the result XML's version tag (see xml above) and handed off to the next call.
// ripped from the Answer XML
//
_sLastVersion = oVersionNode.firstChild.nodeValue;
// get the next batch of messages for chat history
//
http://localhost:9999/VersionedXmlSvc/CommMessageData?Oid=1&LastVer=_sLastVersion
// get thenext batch of IMs for the current instance
//
http://localhost:9999/VersionedXmlSvc/CommMessageData?Oid=_iGroupID&LastVer=_sLastVersion
Getting all this hooked up to the UI was cake (See Figure 5). In spite of Flash being a lousy
dev tool, I'm making quick progress.
Step 4 - A Lil' Nitpicking
I'll be the first to admit my code is not the best: it's filthy, unoptimized and not production quality.
After digging around in There's Flash code I realized that it's also a tad bit filthy and unoptimized.
It's easy to dig in someone else's code, find flaws with it and be super-critical. I'm not one to complain
without offering a solution, so I thought I'd do a little free code review for the poor over-worked There devs.
I'm not going to review all there Flash code, that's for sure. I'm going to focus just on just a few things
I found digging around. A proper code review would reveal much more. The suggested techniques can
be applied to all the code.
Issue
There's Flash code has debug code strewn over it, which isn't being used in end-user scenarios.
Solution
Use a post-processor to strip out all debug code before doing a release build. I'm sure There wants
us running and testing a streamlined and optimized version of their code.
Issue
for loops are touching properties.
for (x = 0; x < tagList.length; x++)
Solution
Caching the length in a var outside the loop keeps Flash from constantly running code that touches properties,
which is filthier and slower than the variable access.
iLen = tagList.length;
for (x = 0; x < iLen; x++)
Issue
The XML object is not ignoring whitespace.
Solution
My god, Flash's XML support is the worst thing I've ever seen. By not setting ignoreWhite to true,
the node indexes are discontinous. It also forces a lot of unnecessary tests for nodeType.
For example, with ignoreWhite set, the following code (which is in FlashIM) goes away completely!
if (subTags[y].nodeType != 1)
{
// NO code here!? HAHAHA
}
else
{
// lots of code here
}
Also, the non-intuitive and error prone discontinuous node access becomes logical. This:
lastVersionMyBody = answerTag.childNodes[3].firstChild.nodeValue;
tagList = answerTag.childNodes[5].childNodes;
becomes this:
lastVersionMyBody = answerTag.childNodes[1].firstChild.nodeValue;
tagList = answerTag.childNodes[2].childNodes;
Which reflects the the way the XML looks in reality, not in some strange nonsensical parallel Macromedia reality.
I could go on and on and on, but I'll stop here. Now back to our regularly scheduled program...
Step 5 - Putting It All Together
Now that I have my own IM/Chat history UI going, I wanted to add some spice to it. I whipped up a quick
XML config file that lets me tweak a slew of options. I can set the text font, size and color for
messages and URLs. The bot will look at textstyle color and override the text message font color.
When There supports their own color tag it will work with the system.
<config>
<bot name="korrupt" macro=""/>
<textstyle font="arial" size="9" color="790000" underline="0" bold="0"/>
<urlstyle font="arial" size="9" color="0000FF" underline="1" bold="0"/>
<namestyle delimiter=":" font="arial" size="9" color="FF0000" underline="1" bold="0"/>
<sysmsgstyle font="arial" size="9" color="C1C1C1" underline="0" bold="0"/>
</config>
With incoming and outgoing text fully working in chat history and IM modes, it's time to get the bot to
do something. The bot needs to scan all the incoming text and look for a trigger. A user
settable bot name acts as the perfect trigger. The bot will look at all the text after the trigger and
see if there are any valid commands. If a user tries to perform a command they are not authorized to do, the bot
won't obey them. The authorization list is also stored in the config XML.
Joe: korrupt?
Korrupt: You said my name, but didn't give me a command.
Korrupt: Type [korrupt help] for a list of commands.
Joe: korrupt help
Korrupt: I support the following commands: help, dance, quote, shutdown.
Joe: korrupt die!
Korrupt: [Joe] commanded me to [die]. I don't know how to [die].
Joe: korrupt dance
Korrupt: [Joe] commanded me to [dance]. 'bodywave'
Joe: korrupt quote
Korrupt: [Joe] commanded me to [quote]. No Sane man will dance. - Cicero (106-43 B.C.)
Joe: korrupt shutdown
Korrupt: [Joe] commanded me to [shutdown]. You are not my master. I won't obey you.
Currently, all of the bot's commands are hard-coded into the Flash. It will be a simple matter to hook
this up to the emotionsbar.xml and other sources to make it open-ended and fully end-user configurable.
I'd also like to get the bot working with other running Flash apps and other external sources.
.-> trigger basic response -> therebot.xml
|
[ ThereBot ] -> trigger macro -> emotionsbar.xml
|
`-> show product info -> products.xml -> [ user's web site ] <-> [ auction system ]
|
`-> sell products -> [ secure trade ]
|
`-> model products -> [ change me ]
Wrapping Up
I won't be giving out the bot quite yet. It has too many bugs right now and isn't complete.
Most of the UI isn't hooked up yet (as you can see in the screenshots).
As an experiment, I took the bot into Karuna plaza last night (9/22). It worked, but it had some spam
issues that I think annoyed people in the conversation a bit. Someone tried to shut him down, but
since they weren't on the "admin" list, it refused to obey. To my amusement, a user
spammed the hell out of the bot forcing it to dance over and over and over. Too bad I didn't
get any screen shots.
- korrupt
|