Scott Hanselman

Quake Live Review and Rant - Why is this interesting?

March 2, '09 Comments [34] Posted in Gaming
Sponsored By

So I installed and have been playing QuakeLive. Here's the Review part. It's fun. It's Quake. Fast, pretty, twitchy, fun. Quake. Good fun.

Here's the Rant part. I'm having trouble understanding is why this is interesting in any way?

Folks on the 'tubes are saying, "OMG, this is a Browser-based game?"

To say, browser-based game, to me, implies effortless installation. More importantly, it also implies a reason to be in the browser. See the screenshot below? That's the MSI installer I ran as Admin.

InstallingQuakeLive

See this screenshot? That's IE requesting permission to run this plugin. There's a separate MSI if you want to run it in Firefox. I download and installed both installers separately.

image

Here's a sample error message:

** GLW_CreateWindow: could not register window class
Please report the the problem you encountered on the Quake Live forums.
You must reload the web page to make this display go away.

A web (or web-enabled) app that doesn't phone home with errors? Hm. Doesn't seem like a web app to me.

See this screenshot? That's my %appdata% folder with 266 MEGS downloaded. It gets downloaded in the background while you "train." Why do you think they train you for 10 minutes in a single level? It's because they are downloading the other 1/4 gig of content.

QuakeFolder

I'm sorry, but this is a re-imagining of Quake III Arena, compiled as a DLL and running inside my browser. It's the same PAK file concept and format that you (possibly) remember from ten years ago. Yes, 1999.

Yes, there's social aspects, background content delivery, easy multi-player matching, but why is this a DLL living inside the browser's memory space and not an EXE that jumps out of the browser? Do I want something that I think of as a browser plugin downloading 256megs+ of content for me? Why is no one pointing out that the emperor frag-fest has no clothes?

Apparently this is interesting to the young people today because the ones playing Quake Live because they weren't alive when Quake was released originally.

I would rather that a game company like ID spend more time really innovating in the gaming engine space (and I know they are), rather than repackaging the same game in different ways for a decade.*

Quake Live is NOT an interesting game. There are more interesting ways to distribute games that have been working nicely for me since 2003. GuildWars is another GREAT example. It was a <1meg EXE to bootstrap and streamed the levels you needed. There's no reason for QuakeLive to be shoe-horned into a browser plugin.

Now I'm off to delete 256 megs of Quake III from %AppData%\LocalLow\id Software\quakelive\home\baseq3.

End of rant. Move along.

*Quake and its four sequels, Quake II, Quake III Arena, Quake 4, and Enemy Territory: Quake Wars

Technorati Tags:

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Experiencing ALT.NET Seattle 2009 Open Spaces

March 2, '09 Comments [5] Posted in Back to Basics | Learning .NET | Musings | Open Source | Screencasts
Sponsored By

ALT.NETLogo I'm up in Seattle at the ALT.NET Open Space (group DL) and the MVPSummit. "Open Space" is a technique to hold self-organizing conferences. ALT.NET conferences have always been Open Spaces, and if you haven't gone an Open Space conf (of any kind) I recommend you check it out. This is my third (?) ALT.NET conference, and sixth Open Space conference and I always enjoy it more than larger shows.

 Martin Fowler says this about Open Space:

The unusual (and powerful) thing about Open Space is that you don't pre-plan a list of activities and speakers. Instead you provide a basic skeleton of time and space, and the attendees figure out what actually happens. The result is a more participative and energetic event.

What is ALT.NET?

In April of 2007, David Laribee coined the phrase ALT.NET after reading a post by Scott Bellware about the NHibernate Mafia. The core message David was keying off of was the maintainability of a software solution and not the tools involved in creating it.
ALT.NET means many things to many people and the debate will continue about what it means to you.
David proposed ALT.NET signifies:

  1. You’re the type of developer who uses what works while keeping an eye out for a better way.
  2. You reach outside the mainstream to adopt the best of any community: Open Source, Agile, Java, Ruby, etc.
  3. You’re not content with the status quo. Things can always be better expressed, more elegant and simple, more mutable, higher quality, etc.
  4. You know tools are great, but they only take you so far. It’s the principles and knowledge that really matter. The best tools are those that embed the knowledge and encourage the principles (e.g. Resharper.)

Robert Scoble introduced me to Kyte.TV last week after he moved a Twitter conversation we were having out of the constrained space of Twitter and into a live video stream with a chat window. As an experiment I recorded a "Hanselminutes Live" using Kyte and it was pretty fun. Fast forward to ALT.NET a week later and I'd forgotten about this. Then I noticed a number of folks on Twitter saying "wish we were there!" I had my webcam with me so I started streaming the sessions I was attending live using Kyte.

Nate Kohari and Ben Scheirman also started recording. Here's the extremely raw video we ended up with. We're still learning, so there's audio and video problems, so set your expectations LOW.

PhotosFromALT.NETScott - ALT.NET Recorded .NET Sessions

Ben - Recorded ALT.NET Sessions

Nate - Recorded ALT.NET Sessions

You can also click the "Shows tab in the embedded interface below:

  Blog posts about ALT.NET Seattle 2009:

Enjoy!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Hanselminutes Podcast 151 - Fit and Fitness with Ward Cunningham and James Shore

February 28, '09 Comments [7] Posted in Podcast | Tools
Sponsored By

WardCunninghamMy one-hundred-and-fifty-first podcast is up. Ward Cunningham is the creator of the Wiki, and the creator of the "Fit" testing framework. James Shore is the coordinator of the Fit project, an agile coach and the author of The Art of Agile Development.

JamesShoreYou may have heard the terms "Fit" and "Fitnesse" bandied about by the software engineering literati. What are they? Are they useful? Are they used at all? Does your testing strategy need some fitnesse? The creator of Fit and the coordinator of the Fit project chat with Scott and answer the hard questions. Is Fit Dead?

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Genome is a sponsor for this show!

Welcome to powerful, mature object-relational mapping in the .NET world. Genome lets you use the full benefits of LINQ with all major database platforms (Microsoft SQL Server, Oracle and IBM DB2). Genome: supporting real-world enterprise application development since 2002.

Telerik is a sponsor for this show!

Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET and Windows Forms. Enjoy the versatility of our new-generation Reporting Tool. Dive into our online community. Visit www.telerik.com.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

Technorati Tags: ,,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Splitting DateTime - Unit Testing ASP.NET MVC Custom Model Binders

February 26, '09 Comments [27] Posted in ASP.NET | ASP.NET MVC
Sponsored By

I've got this form for users to create an event. One of the fields is a DateTime, like this:

image

And that's kind of lame as it's hard to type in a Date and a Time at the same time. It's also no fun. Most sites would have those separated, and ideally use some kind of Calendar Picker with jQuery or something.

image

But when you post a form like this back to a Controller Action, it doesn't exactly line up neatly with a System.DateTime object. There's no clean way to get partials like this and combine them into a single DateTime. The "ViewModel" in doesn't match the Model itself. I could certainly make my method take two DateTimes, along with the other fields, then put them together later. It could get mess though, if I split things up even more, like some travel sites do with Month, Date, Year each in separate boxes, then Hours, Minutes, and Seconds off in their own.

image

I figured this might be a decent place for a custom "DateAndTimeModelBinder" after my last attempt at a Model Binder was near-universally panned. ;)

Here's my thoughts, and I'm interested in your thoughts as well, Dear Reader.

DateAndTimeModelBinder

First, usage. You can either put this Custom Model Binder in charge of all your DateTimes by registering it in the Global.asax:

ModelBinders.Binders[typeof(DateTime)] = 
new DateAndTimeModelBinder() { Date = "Date", Time = "Time" };

The strings there are the suffixes of the fields in your View that will be holding the Date and the Time. There are other options in there like Hour, Minute, you get the idea.

Instead of my View having a date in one field:

<label for="EventDate">Event Date:</label>
<%= Html.TextBox("EventDate", Model.Dinner.EventDate) %>

I split it up, and add my chosen suffixes:

<label for="EventDate">Event Date:</label>
<%= Html.TextBox("EventDate.Date", Model.Dinner.EventDate.ToShortDateString()) %>
<%= Html.TextBox("EventDate.Time", Model.Dinner.EventDate.ToShortTimeString()) %>

Now, when the Form is POST'ed back, no one is the wiser, and the model is unchanged:

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinnerToCreate) {
//The two fields are now inside dinnerCreate.EventDate
// and model validation runs as before...
}

That's the general idea. You can also just put the attribute on a specific parameter, like this:

public ActionResult Edit(int id, 
[DateAndTime("year", "mo", "day", "hh","mm","secondsorhwatever")]
DateTime foo) {
...yada yada yada...
}

It's so nice, that I give it the Works On My Machine Seal:

Here's the code, so far. It's longish. I'm interested in your opinions on how to make it clearer, cleaner and DRYer (without breaking the tests!)

NOTE: If you're reading this via RSS, the code will be syntax highlighted and easier to read if you visit this post on my site directly.

public class DateAndTimeModelBinder : IModelBinder
{
public DateAndTimeModelBinder() { }

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}

//Maybe we're lucky and they just want a DateTime the regular way.
DateTime? dateTimeAttempt = GetA<DateTime>(bindingContext, "DateTime");
if (dateTimeAttempt != null)
{
return dateTimeAttempt.Value;
}

//If they haven't set Month,Day,Year OR Date, set "date" and get ready for an attempt
if (this.MonthDayYearSet == false && this.DateSet == false)
{
this.Date = "Date";
}

//If they haven't set Hour, Minute, Second OR Time, set "time" and get ready for an attempt
if (this.HourMinuteSecondSet == false && this.TimeSet == false)
{
this.Time = "Time";
}

//Did they want the Date *and* Time?
DateTime? dateAttempt = GetA<DateTime>(bindingContext, this.Date);
DateTime? timeAttempt = GetA<DateTime>(bindingContext, this.Time);

//Maybe they wanted the Time via parts
if (this.HourMinuteSecondSet)
{
timeAttempt = new DateTime(
DateTime.MinValue.Year, DateTime.MinValue.Month, DateTime.MinValue.Day,
GetA<int>(bindingContext, this.Hour).Value,
GetA<int>(bindingContext, this.Minute).Value,
GetA<int>(bindingContext, this.Second).Value);
}

//Maybe they wanted the Date via parts
if (this.MonthDayYearSet)
{
dateAttempt = new DateTime(
GetA<int>(bindingContext, this.Year).Value,
GetA<int>(bindingContext, this.Month).Value,
GetA<int>(bindingContext, this.Day).Value,
DateTime.MinValue.Hour, DateTime.MinValue.Minute, DateTime.MinValue.Second);
}

//If we got both parts, assemble them!
if (dateAttempt != null && timeAttempt != null)
{
return new DateTime(dateAttempt.Value.Year,
dateAttempt.Value.Month,
dateAttempt.Value.Day,
timeAttempt.Value.Hour,
timeAttempt.Value.Minute,
timeAttempt.Value.Second);
}
//Only got one half? Return as much as we have!
return dateAttempt ?? timeAttempt;
}

private Nullable<T> GetA<T>(ModelBindingContext bindingContext, string key) where T : struct
{
if (String.IsNullOrEmpty(key)) return null;
ValueProviderResult valueResult;
//Try it with the prefix...
bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName + "." + key, out valueResult);
//Didn't work? Try without the prefix if needed...
if (valueResult == null && bindingContext.FallbackToEmptyPrefix == true)
{
bindingContext.ValueProvider.TryGetValue(key, out valueResult);
}
if (valueResult == null)
{
return null;
}
return (Nullable<T>)valueResult.ConvertTo(typeof(T));
}
public string Date { get; set; }
public string Time { get; set; }

public string Month { get; set; }
public string Day { get; set; }
public string Year { get; set; }

public string Hour { get; set; }
public string Minute { get; set; }
public string Second { get; set; }

public bool DateSet { get { return !String.IsNullOrEmpty(Date); } }
public bool MonthDayYearSet { get { return !(String.IsNullOrEmpty(Month) && String.IsNullOrEmpty(Day) && String.IsNullOrEmpty(Year)); } }

public bool TimeSet { get { return !String.IsNullOrEmpty(Time); } }
public bool HourMinuteSecondSet { get { return !(String.IsNullOrEmpty(Hour) && String.IsNullOrEmpty(Minute) && String.IsNullOrEmpty(Second)); } }

}

public class DateAndTimeAttribute : CustomModelBinderAttribute
{
private IModelBinder _binder;

// The user cares about a full date structure and full
// time structure, or one or the other.
public DateAndTimeAttribute(string date, string time)
{
_binder = new DateAndTimeModelBinder
{
Date = date,
Time = time
};
}

// The user wants to capture the date and time (or only one)
// as individual portions.
public DateAndTimeAttribute(string year, string month, string day,
string hour, string minute, string second)
{
_binder = new DateAndTimeModelBinder
{
Day = day,
Month = month,
Year = year,
Hour = hour,
Minute = minute,
Second = second
};
}

// The user wants to capture the date and time (or only one)
// as individual portions.
public DateAndTimeAttribute(string date, string time,
string year, string month, string day,
string hour, string minute, string second)
{
_binder = new DateAndTimeModelBinder
{
Day = day,
Month = month,
Year = year,
Hour = hour,
Minute = minute,
Second = second,
Date = date,
Time = time
};
}

public override IModelBinder GetBinder() { return _binder; }
}

Testing the Custom Model Binder

It works for Dates or Times, also. If you just want a Time, you'll get a MinDate, and if you just want a Date, you'll get a Date at midnight.

Here's just two of the tests. Note I was able to test this custom Model Binder without any mocking (thanks Phil!)

Some custom model binders do require mocking if they go digging around in the HttpContext or other concrete places. In this case, I just needed to poke around in the Form, so it was cleaner to use the existing ValueProvider.

[TestMethod]
public void Date_Can_Be_Pulled_Via_Provided_Month_Day_Year()
{
var dict = new ValueProviderDictionary(null) {
{ "foo.month", new ValueProviderResult("2","2",null) },
{ "foo.day", new ValueProviderResult("12", "12", null) },
{ "foo.year", new ValueProviderResult("1964", "1964", null) }
};

var bindingContext = new ModelBindingContext() { ModelName = "foo", ValueProvider = dict};

DateAndTimeModelBinder b = new DateAndTimeModelBinder() { Month = "month", Day = "day", Year = "year" };

DateTime result = (DateTime)b.BindModel(null, bindingContext);
Assert.AreEqual(DateTime.Parse("1964-02-12 12:00:00 am"), result);
}

[TestMethod]
public void DateTime_Can_Be_Pulled_Via_Provided_Month_Day_Year_Hour_Minute_Second_Alternate_Names()
{
var dict = new ValueProviderDictionary(null) {
{ "foo.month1", new ValueProviderResult("2","2",null) },
{ "foo.day1", new ValueProviderResult("12", "12", null) },
{ "foo.year1", new ValueProviderResult("1964", "1964", null) },
{ "foo.hour1", new ValueProviderResult("13","13",null) },
{ "foo.minute1", new ValueProviderResult("44", "44", null) },
{ "foo.second1", new ValueProviderResult("01", "01", null) }
};

var bindingContext = new ModelBindingContext() { ModelName = "foo", ValueProvider = dict };

DateAndTimeModelBinder b = new DateAndTimeModelBinder() { Month = "month1", Day = "day1", Year = "year1", Hour = "hour1", Minute = "minute1", Second = "second1" };

DateTime result = (DateTime)b.BindModel(null, bindingContext);
Assert.AreEqual(DateTime.Parse("1964-02-12 13:44:01"), result);
}

Thanks to LeviB for his help. Your thoughts?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

IE6 Warning - Stop Living In The Past - Get off of IE6

February 24, '09 Comments [83] Posted in ASP.NET | Musings
Sponsored By

Here's a chart showing ONLY Internet Explorer visits to my blog over the last few weeks:

17.96% of visitors to my site are on IE6

I'm bummed to see that nearly 20% (17.96%, in fact) of my visitors are using IE6. (Interesting that 8% are already using IE8!)

There's a great website that's attempting to deal with this and get folks off of IE6, called http://www.stoplivinginthepast.com. It all started like this and spread over Norway like wildfire. Hopefully it'll spread over the rest of the world and we can all add one less browser we need to test against.

There's lots of ways you can add a warning to your website or blog. The EASIEST way would be to add some HTML like this: (modified from examples posted here)

<!--[if lte IE 6]>
<style type="text/css">
#ie6msg{border:3px solid #c33; margin:8px 0; background:#fcc; color:#000;}
#ie6msg h4{margin:8px; padding:0;}
#ie6msg p{margin:8px; padding:0;}
#ie6msg p a.getie7{font-weight:bold; color:#006;}
#ie6msg p a.ie6expl{font-weight:bold; color:#006;}
</style>
<div id="ie6msg">
<h4>Did you know that your browser is out of date?</h4>
<p>To get the best possible experience using my website I recommend that you upgrade your browser to a newer version. The current version is <a class="getie7" href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer 7</a> or <a class="getie7" href="http://www.microsoft.com/windows/Internet-explorer/beta/default.aspx">Internet Explorer 8 (Beta)</a>. The upgrade is free. If you’re using a PC at work you should contact your IT-administrator. Either way, I'd personally like to encourage you to stop using IE6 and try a more secure and Web Standards-friendly browser.</p>
<p>You could also try some other popular browsers like <a class="ie6expl" href="http://mozilla.com">FireFox</a> or <a class="ie6expl" href="http://www.opera.com">Opera</a>.</p>
</div>
<![endif]-->

There are many plug-ins for different blog engines posted there as well. However, adding text like this is easy because it uses some built in crazy detection code that is unique to Internet Explorer (and oft-maligned) using HTML comments. Note the first line of the code has this weird thing: <!--[if lte IE 6]> called a conditional comment. This is easy to do, just add the text above to your blog's template. There's no server-side requirement at all.

Just a picture of what you'd see if you were running IE6

If you're using DasBlog for your blog engine, as I am, just add the text above to the top of your hometemplate.blogtemplate file and you're all set.

The downside is that it's roughly 1000 bytes, but it's temporary. Another technique would be to sniff the user's browser on the server side and only emit this text when they are running IE6 or below.

Now, it's your turn!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.