I'm playing through Half-Life 2 for the second time now. I had a blast playing through the first time, but I think I missed a bunch of details. So this time through, I'm really taking my time and taking notice of the details. For instance, in Dr. Kleiner's lab, I noticed a sticky that mentioned finding more watermelon for Lamaar (his pet head crab). It got me to wondering. Do head crabs like watermelon? If I bring a watermelon to Dr. Kleiner, do I get a fun easter egg? I'll have to find out!


I love this game.

Game Over: Half-Life 2

I beat Half-Life 2 the other night. It took me just over a week. It was a great game, and even though it had some quirks (mostly in the licensing department), I'm very satisfied with the game. Now I've moved on to playing Counter-Strike : Source. This is the only way (currently) to play multiplayer using the Source engine from Valve. In Counter-Strike : Source (CSS), you and a squad of fellow players are teamed up against another, similar squad of players online. One group plays as the terrorist team, and has the objective of bombing a target, taking hostages, or simply killing the other team. The other team plays as the counter-terrorists. This group is tasked with diffusing the bomb, rescueing hostages, or killing the other team. Each group has a unique set of weapons and equipment available that can be purchased. Rounds last about three minutes each, and money is awarded after each round. Surviving a round earns you the right to keep everything you bought in the previous one. Once out, you can chat with the other players and watch as the action continues to roll. With a three minute maximum round time, it doesn't take long to be back in the thick of things.


I've learned a couple of things in my first few nights of playing CSS. First, I suck at it! You get to choose the name other players know you as, and I chose EasyTarget. It seems to fit. Most rounds I take headshot with in the first minute. Occassionally I get lucky and get a kill or two in a round, but I'm usually heavier in the death column. Luckily, it is a team effort, so my team can win even if I die in the first minute. The second thing I learned is that I can't play this game for three hours and expect to be able to fall asleep immediately. I stopped playing last night around 3am, and I tried to dive right into bed. Big mistake. I was still all twitchy and amped up from playing. As you play, your eyes dart around the fielf of view, looking for targets and trying to find the vantage point that the sniper is going to ace you from. Your heart rate rises a couple of notches. That lingered for a good while after the game. I'm not sure when I finally fell asleep, but it certainly wasn't right away.


If you are an avid Counter-Strike player, and you have some notes for me, please leave a comment. Or, if you just want to kill an EasyTarget, jump online and start looking for me. I'll be the dead guy three steps from the starting spot.

Good Feeling

It is such a good feeling when you finally solve a problem that has been bugging you for a while. That's how I feel right now. I've been trying to get my remoting components moved over to another machine that will act as our dedicated testing / development environment. I kept running into strange errors and it never worked properly. Tonight, I finally found the solution! I won't go into the details, but it boiled down to uninstalling everything, and reinstalling from scratch. Now it is working like I expect! Now I get to go take a nap and get ready for our mini-game / quiz in the morning.

Serial Ports

Serial Ports are a common tool we use to communicate with devices at work. Usually, if a device has a serial interface, you can breath a sigh of relief that you are going to have success with communicating with it. There are still some things to watch out for though. Getting the baud rate, parity, data bits, and stop bits configured properly is critical. I helped a colleague at work today with just such a problem. He was trying to receive data in his .NET client from a bar code scanner that we had configured in the Descarte OmniServer OPC server. He would get the initial data change event when the OPC item was created, but nothing after that. I wrote the prototype he was basing it on, and I knew that it worked, as I had used the same barcode scanner in testing it. So we started poking around. First, we brought up HyperTerm and looked to see if we were getting any data. Sure enough, data was coming across. Then we went back to the OmniServer configuration to be sure that our topic was configured to use COM1, and that the device was using the right protocol, in this case an Intermek protocol. It was, but still, no data. So then I got to thinking, "I wonder if HyperTerm is using a different configuration". That was it! In HyperTerm, we were setup for 9600 baud 7-e-1 configuration. In OmniServer, we were looking at 9600 baud 8-n-1. Big difference! The barcode scanner was sending 7 data bits, but our OPC server was looking for 8 data bits. That's why it never sent a data change event. We got it reconfigured and it worked beautifully from there.

Game of the Moment: Half-Life 2

That's right, it finally shipped! Half-Life 2 is now sitting on store shelves, just begging to go home with you. I've been looking forward to this game for a loooong time. It was originally scheduled to be released in September of 2003. That's right, 2003. Well over a year late, I had a Simon gift card I had been sitting on waiting for the release of the game. I got the card last Christmas, so I guess I can consider this a very belated Christmas gift.


So how is it? AWESOME! It is totally engrossing. I sat down at 7pm last night and didn't stop playing until 2am. That means I'm a bit of a sleepy boy today, but it was time well spent in my mind. I'm not going to give a full review of the game, as the game media have tons of coverage already. However, there were a couple of things that struck me right off the get go.


First, lets talk about Steam. Valve developed Steam as a platform for distributing their games, hosting on-line games, and communicating news and updates to players. Steam is an application that runs in your system tray, and you can use it to launch your games, start a multiplayer game, chat with friends, or buy new Vavle games. When you buy a game on Steam, it is downloaded to your computer. If you login to Steam from someone else's computer, your games are automatically downloaded. So you can play anywhere that has a web connection. This is great if you play at a lot of computer labs, cyber cafes, or libraries, as you don't have to carry your media (CD / DVD) around with you everywhere you go. There is a drawback to this though: you must be online to play. Once you've signed in, you can check a box for offline play, but you still may need to get online from time to time. This means that you are at the mercy of your ISP and the Valve content servers when you want to play. We have a pretty fast computer at home, and the load times were still really long between chapters due to the latency from authenticating with the Valve content server. This really took away from the emersiveness of the game. It isn't any fun to be in the middle of a heated chase, and then have a two minute pause while the game gets permission to load the next level.


I decided to purchase the game from a brick and mortar store rather than from Steam. I wanted to have the physical media so I wouldn't have to wait all day to download it. I had also hoped that it would allow me to play the game without connecting to steam. No such luck there. Even though I was able to run through the entire install without connecting, I had to login to Steam before I could play the game. When you login to steam, you give your CD-Key and it gets associated with your Steam account. Then you get a set of keys for unlocking the game files. The entire install process took a full hour. Installing the files took about 20 minutes, and getting the keys and unlocking the content, and then downloading new video drivers took the rest of the time. The installer is pretty vanilla too, nothing to keep your interest for that hour. Be content with starting the install, and then finding something else to do for a while.


Which brings me to another point: purchasing the game. Like I said, I wanted the physical media for install, and I got that. I bought the Collector's Edition of the game, which included a DVD containing Half-Life 2, Half-Life : Source, and Counter-Strike : Source, as well as a t-shirt and Prima game book. The price ran $87. To get the regular edition at the store is $55. On steam, the regular (Bronze) edition is $49.99, the silver edition (which includes Half-Life : Source) is $59.99, and the gold edition is $89.99. The gold edition will get you some posters, a hat, a t-shirt, a sound track, and various other stuff sent in the mail. I wanted to the Collector's Edition so I could get Half-Life : Source, which is the original game upgraded to use the new game engine. Now that I've been through the process, I think I would have been better off buying the game on-line on steam. I was interested in the game, but not so much in the extra swag. I could have saved $25 by going on Steam and getting the Silver edition, and I still would have had everything I wanted. Plus, with our cable internet connection, I could have just started the download at night and play the next day. Oh well, live and learn. So if you are considering purchasing Half-Life 2, and you have a fairly speedy web connection, I would definitely recommend purchasing from Steam. If you have a slow web connection, go ahead and get the store copy. Be warned that the $55 version in the store has five CDs rather than a DVD though.


Graphically, the game is pretty amazing. I don't have the worlds most incredible graphics card at home, I have the base Dell configuration (NVidia GeForce 4 440). It works well for everything we need, but doesn't support all of the fancy rendering stuff that the $500 cards out on the market now do. That means I can't take advantage of all of the special effects in the game, but the game itself plays very smoothly. There is a little chop right after a level loads, and when the action gets fast and frantic, but overall it plays very well on my machine. And some effects are still pretty stunning, even on my older graphics card. The water rippling effect in the game is mesmerizing. And the facial animations of the characters lends a lot to their performance in the game. They aren't the stiff manequins with a hinged jaw anymore. They now smile, frown, raise eyebrows, blink, and do everything you might expect a real person to do.


At the very beginning of the game, I was reminded of Schindler's List. You enter City 17, a no name chunk of city that appears to be in a police state. Civil Patrol is constantly monitoring your actions, and doling out scheduled beatings to the citizens. Propaganda is blared from video screens mounted throughout the city, coercing everyone to give up their sense of instinct, and just give in to the Combine. The Combine is an alien force that has somehow overtaken the planet. A puppet human government is in place, headed by the Administrator, Dr. Breene. Dr. Breene coordinated the surrender of earth to the combine, and as reward they have appointed him the head cheese of the puppet human government. All of the people not in Civil Patrol uniforms are wearing blue denims, much like a prison uniform. As you arrive on the train into town, you see that the people are stripped of their luggage, and shuffled into a line for questioning. Out on the streets, conversations are forbidden in the open, so people huddle together in whatever shelter they can find. All of the doors on the apartments have been ripped off to allow for easier surveilance, and raids are a routine of life. You learn from some of the inhabitants of City 17 that the Combine forces have been working to limit the number of births, and that drugs have been added to the water supply to cause amnesia. Many people can't remember how long they have been in the city, if they have a family, or even who they are. It's a very bleak outlook for mankind.


And this is where the game picks up. You learn of an underground railroad of sorts that shuttles people out of the city. From there, a resistance is forming to combat the Combine. The resistance is headed by your old group of pals from the Black Mesa facility, and when they learn of your arrival, they are thrilled to learn that their messiah has returned. It seems that your story from the first game has taken on a somewhat mythical proportion.


I won't spoil the rest of the game for you, but there are some moments that are just amazing. I'm totally hooked, and I think anyone could enjoy this game.

Poker Night




Poker night with the boys was a lot of fun. We had five players tonight. I was hoping for more, but it turned out to be a great time. Anthony, Dave, Jason, Tim, and I all got together around the table for a couple of games. The first game had money on it. The pot was a whopping $25. Not much, but enough to make it interesting. The first game ran from a little after 8pm to a little after 10. Jason was the big winner, Anthony second, I took third, then Tim, and Dave took the boobie prize (a Canadian penny). We played a second game just for fun, with almost the same result. It was a great time, and I hope that we get to do it again soon. This was my first opportunity to use my birthday gift, which was a poker table topper. Dave brought by his poker chip set, which is a nice set of real casino style chips (not the dishwasher safe interlocking plastic pieces, but real composite chips with metal inserts). It was a great time among friends, and I hope we do it again soon.

Fast Week

Has it really been a week since i posted anything? Wow, time has been flying by lately. I spent three days this week off-site at a couple of different customer locations. I'm still working on the same stuff: upgrading a mainframe application, porting a VB / COM+ / InTouch application to .NET, and various other tasks. I've also started a leadership initiative at work that will hopefully see me take on slightly different responsibilities, and lead me in new and interesting directions. This weekend I'm going to host a poker night. One of my birthday gifts was a poker table topper, and I'm anxious to try it out. We play poker (Texas Hold 'Em) every day at lunch, and I really enjoy it. We don't play for money, but it is still fun to try to figure out what other people are playing with, and trying to win all the chips. Poker has become quite popular again with all of the World Poker Tour and Celebrity Poker shows on TV now. It is a fun game, and it requires equal parts math, people skills, and luck. I'll post an update after the poker night with an update of how it went!

It Works!

I've won my fight with .NET Remoting! Thank goodness for Ingo Rammer and Google, as those two resources provided the answers to all of the "gotchas" that bit me on this. My final solution involves a shared base class assembly, which defines the abstract base class for each of the objects exposed by my web service. Next, there is the server implementation, which is composed of a server activated class factory serving up the client activated remoted objects. And finally, there is the client piece, which first gets an instance of the class factory, and uses that reference to get instances of the client activated objects.


First, let's talk about the shared assembly. This is the easiest one. I needed to create, at a minimum, two abstract classes in my shared assembly. The first abstract class describes my class factory.

public abstract class ClassFactoryBase : MarshalByRefObject
{
   public abstract GetCAO();
}

So far so good. Now comes the first gotcha I experienced. My client activated object is exposing the functionality of an existing COM object on the server. So my first idea was to simply implement interface exposed by this COM object. This was a bad idea, and caused remoting to barf big time. So instead, my base class for the client activated implements the interface, but doesn't put it in the declaration.

public abstract class CAOBase : MarshalByRefObject
{
   public abstract bool COMObjectMethod();
}

All of this code went into a file I named Shared.cs, and I compiled it to an assembly, Shared.dll.
Next, I needed to implement the factory and the client activated object.

public class ClassFactory : ClassFactoryBase
{
   // Generic constructor required for Remoting
   public ClassFactory()
   {
   }

   public override GetCAO()
   {
      return new CAO();
   }
}

public class CAO : CAOBase
{
   internal Interop.ComObject comroot;

   // Generic constructor required for Remoting
   public CAO()
   {
      comroot = new Interop.ComObject();
   }

   public override COMObjectMethod()
   {
      return comroot.COMObjectMethod()
   }
}

Here, the ClassFactory creates a new instance of the CAO object on demand and passes it back. The CAO object creates an instance of the COM object to be exposed. The methods of the CAO object then pass through the COM object layer. These classes I put in a file called server.cs and compiled to an assembly named Server.dll

I wanted to use IIS as the host for my remote classes, so I needed to do a couple of things to enable this. First, I created a virtual directory in the IIS admin. In that virtual directory, I created a bin directory. I copied my Shared.dll and Server.dll into the bin directory, along with the interop.ComObject.dll. Finally, I created a web.config file and placed this in the root of the virtual directory. The web.config looked like this:

<configuration>
   <system.runtime.remoting>
      <application>
         <service>
            <wellknown
               mode="SingleCall"
               type="ClassFactory,Server"
               objectUri="ClassFactoryURI.soap" />
            </service>
         </application>
      </system.runtime.remoting>
   <system.web>
</configuration>

This was another stumbling block for me. Originally, I had excluded the ".soap" extension from my objectURI. I had read something that indicated that it was unnecessary when hosting your object in IIS. This was dead wrong. Unless your object URI ends with ".soap" or ".rem", IIS will not pass the method calls on to the remoted object. This took me a while to figure out, so don't make the same mistake. Fortunately, everything else was cake from this point, as IIS takes care of all of the nastiness of load balancing, connection pooling, and security for connecting to your remote object.

Finally, it's time to implement the client. Here is my quick and dirty client code:

public class Client
{
   public static void Main( string[] args )
   {
      ClassFactoryBase factory =
         (ClassFactoryBase)Activator.GetObject(
            typeof(ClassFactoryBase),
            "http://remotinghostserver/VirtualDirectory/ClassFactoryURI.soap" );
      CAOBase cao = factory.GetCAO();
      if ( cao.COMObjectMethod() )
      {
         MessageBox.Show( "Success!" )
      }
   }
}

This code goes in Client.cs, compiles to Client.exe, and is deployed with only the Client.exe and Shared.dll. I didn't even need a config file! This is because I'm using the Activator.GetObject method to create an instance of my object. Why am I doing this? Well, another option would be to use soapsuds.exe to generate the metadata for my remoted object and reference this when compiling my client. When done this way, the client can simply use the new keyword when appropriate remoting configuration information is in the app.config file. Very convenient for the code. Unfortunately, soapsuds.exe is broken. For some reason, when you host your remoting classes in IIS, soapsuds makes mistakes when attempting to generate the metadata. The result is that you will successfully expose your class factory, but will get a type case exception when attempting to get an instance of your CAO. This is seriously bad news for the solution I needed to make. Another option would be to use share interfaces (as opposed to share base abstract classes). When using share interfaces, you can again configure the app.config file to allow for the new keyword to be used. However, there is another gotcha here. When using shared interfaces, the CAOs instances can not be passed as arguments to other methods on other remote objects. For our solution, several of the CAOs need to interact with each other. Using abstract base classes allows us to pass these references as arguments to our other remote objects. The only drawback here is that we are forced to use the Activator.GetObject() call to instantiate our remote object rather than the new keyword. It's a small price to pay I think.


So what do I do from here? The next thing I need to do is verify that the object lifetime is being managed properly. I don't want to strand a bunch of instances of my remote objects on the server. So my next effort will be to investigate CAO object lifetime leases. Once I get there and interesting information to pass on, I'll be sure to post it here.

Game Room

Today, we piled the family in the Bravada and went on a little adventure. Our first stop was to the Lowes where we needed to return fireplace doors. Jenn had ordered a nice set of fireplace doors from the Lowes website. They were special ordered, and we waiting anxiously for almost a month for them to arrive. To our major disappointment, the website failed to notify us that these doors were for installation on a MASONRY FIREPLACE ONLY! Crap! We have a prefab fireplace, and there was no way it was going to work. So today was our day to trudge into Lowes to return the doors. We were afraid it was going to be a big hassle, as they were special order. Lowes impressed us again with their customer service. They took it back and gave us a credit on our credit card, no questions asked. We so prefer going to Lowes over Home Depot for their service. The local Home Depot has terrible service. We went there one time to price carpet, and it was as if we were asking the guy to give blood or something. No one wanted to help.


I'm rambling. Back to the adventure. Our next stop was to eat some lunch (Burger King), and then over to Watson's. Jenn had read that Watsons was at one time a vendor for fireplace doors for our particular model fireplace. Turns out, they stopped selling them a couple of years ago, but we still had fun wandering around the show room. Then, we got into trouble. We saw a display room, a game room, that had everything we wanted for the basement. There was a pool table, a card table, a bar, an electric fireplace, a wall table, bar stools, chairs, and gaming equipment. It had the works. And it was only $7,500! We were sooooo tempted to break with our budget and buy it all, but reason got the better of us. It is getting close to Christmas, and we had already set our Christmas budget, which was nowhere near the cost of the room. We're still paying on the car, and we are overpaying on the house payment so we can get rid of our PMI. Our budget is really tight right now, and to get the game room would have been fun, but it would have been murder on our money situation. Still, we drove home trying to think of ways we could round up that kind of money in a hurry. There was another room there that was priced $1,000 lower, missing just the fireplace and wall table. That would have been just as good. We mulled over getting extra jobs, asking for extra hours, garage sale.... how could we do it.


By the time we got home we realized that there were other things on our agenda that we needed to look after first. Sure, it would a TON of fun to get a fully equipped game room. However, I've needed a new office suite since we moved back from Maryland. We also want to get a sunroom / deck for our backyard. That is something that we budgeted for when we talked with our financial advisor. I just got a nice table topper for my birthday too. That will be great as a card table when we want to have folks over to play. We'd still love to do it, but I think we're going to have to put it off for at least another couple of years.

Movie Review: The Incredibles

Tonight I got a special birthday treat. My mom came down to watch Corbin so Jenn and I could go out on a date! We went to see the movie "The Incredibles", which is the latest Disney / Pixar film. It was great! It is really amazing to see how far animation has come. Jenn remarked to me how she was watching an old Bugs Bunny cartoon with Corbin a couple of days ago, and the difference from Bugs Bunny to the Incredibles was...well, incredible! A movie is more than just pretty pictures though, and this one had a pretty good story. The plot surrounds a family of super heros trying to get by as average citizens in the world. A series of lawsuits against the supers has led to them all entering a sort of protection program where they must live out their lives without displaying their powers. Mr. Incredible and ElastiGirl got married and had a family. Mr. Incredible, aka Bob Parr, is going through something of a mid-life crisis. He longs for the excitement of doing what he was meant to do: saving those in need. He and one of his super buddies sneak off once a week to listen to the police scanner, and occassionally sneak in a little action. Meanwhile, Elastigirl, aka Helen Parr, is trying to keep up with the demands of raising the three children. I won't ruin the movie for you by spelling out all of the plot points, but the film does a great job in finding the humor in day to day family life. Each of the children have powers that are an allegory to the typical behavior for kids. It was neat to see super heros that had human traits and problems. The movie had plenty of action, humor, and all around fun. If you have the opportunity, I would recommend that this be the next movie you see.

My Continuing Fight with .NET Remoting

So I went for the sleep option around 3:15am. I should have picked option two (coffee) because my brain was still way too active to let me fall asleep. I kept thinking about different options to try. In any case, I'm a couple of steps close to getting my solution to work. For one of our systems, we have a set of COM objects that are hosted in COM+. These objects are exported through COM+ / DCOM to several clients across a LAN / WAN environment. The clients create an instance of a broker object exposed through COM+, and then use that broker to create instances of other server business objects (SBOs). Each of these instances is intended to hold state (they connect to a variety of ERP and database systems, so opening and closing them often has a high transaction cost). Our objective with this project is two-fold. First, the client that activates these distributed components is Wonderware's InTouch 7.x. The license costs for our customer are getting a little high, so they are looking to replace the InTouch client with a .NET client written in C#. I had originally considered using XML Web Services hosted in IIS to wrap the functionality of the SBOs. This was before I learned that the SBOs held state. XML Web Services is a stateless architecture, so it wouldn't due for accomplishing our task. So that is why I chose to go the .NET Remoting route. I'm still going to use IIS to host the remoting component, as it will save me time by acting as the hosting control, provide security, and also take care of channel and load management.


So after banging my head on the wall last night, I got this far: I have a Client Activated Object (CAO) that is hosted by IIS. I've written the web.config file to expose the CAO. I used "soapsuds.exe -nowp -ia:MyRemoteCode -oa:MyRemoteCode_Proxy.dll" to generate a meta-data proxy for the remote object. I created a client that registers the remote object, and then creates an instance of it. So far so good. Here is where the trouble starts though. I create a second remoteable class that also inherits from MarshalByRefObject. This one is instantiated by making a method call on the first remoted object. However, when the client calls "firstRemoteObject.GetSecondRemoteObject()" I get a type mismatch error on the return type. Evidently, the meta-data generated by soapsuds.exe doesn't exactly match the typing information passed back by the remote call. So this is where I am stuck tonight. I spent the day working on another project, so I haven't been able to touch it yet. If I can stay concious long enough I may try again tonight. If I get it solved, I'll post sample code.

CAOs hosted by IIS that implement COM Interop Interfaces

Wow, did I have an intrigueing assignment today. I have a client that is going to be developed in C# (Microsoft .NET technology). This client is going to remotely access another .NET assembly on a server machine, across the network. The server side assembly is going to be client activated (CAO), and the CAO needs to expose the functionality of a COM component through interop. I thought I could just create a class that inherits from MarshalByRefObject and implements the interface of the interop component.... but NOOOOOO! Couldn't be that simple could it? So instead, I'm sitting here at 2am still banging out the intricacies of creating and passing references around across application domains. So far, I've learned that implenting the interface for an interop component is seriously bad news. Forget about remoting your object after you've done that. Next, don't every install the .NET Framework 2.0 beta on your primary development box. It did me the courtesy of modifying all of my IIS virtual directories to automatically select the 2.0 revision rather than the stable 1.1 revision. So now I need to manually select 1.1 for all existing and newly created virtual directories. Thanks .NET 2.0. So now my stickler is trying to figure out how to get two remoted objects that are hosted in the same application domain to interact. Not a simple task so far. More on this wonderful story once I get a cup of coffee... or sleep.

Election Day

Today is election day! I spent two hours waiting in line to cast my votes, but I'm glad that I did. So if you haven't done so already, go out there and cast your vote! I don't care if you are for my candidate or against, I would just like to see every American do their civic duty and vote.

 
Jade Mason