Archive for September, 2008

Morgage bailout in two paragraphs

Monday, September 22nd, 2008

2002:

200 million a year banker: “Hey, lets give credit to anyone that wants to buy a house, regardless of income or credit. We can sell these loans as securities, and leverage it out 30 to 1.”
Irresponsible buyer: “Wow, I can buy a mansion even though I work at 7-11”
Average taxpayer: “These housing prices are insane. I’m going to keep renting.”

2008:

Irresponsible buyer: “Hey, my 200K house isn’t worth a million dollars now that nobody wants to pay for it. Cya.”
200 million a year banker: “The economy is fucked unless the taxpayers pay for our bad loans.”
George Bush: “Have a trillion dollars.”
Average taxpayer: “WTF?”

Contractor pay calculator

Friday, September 12th, 2008

It’s been coming up a lot lately on how much I should ask for to work as a contractor, given a certain pay value as an employee. Every time I do this in my head, an hour later I kick myself because I realize I asked for too little. So I wrote a calculator in Javascript that will do all the calculations for you.

Contractor pay calculator

I’ll leave it up to the reader to write the inverse operation 🙂

Jeopardy done

Wednesday, September 10th, 2008

First major game to show the RakNet logo prominently. There’s another AAA game out there that uses RakNet but they don’t show the logo.
http://www.us.playstation.com/PS3/Games/JEOPARDY

Unfortunately, they edited the middleware splash screen out of the video on the site.

The most difficult part of contracting on this project was working with 3rd party code. This is one of those cases where it would have been faster to write your own systems from scratch. In the end it did well though and the game is pretty much bug-free.

Lobby 2 almost done

Monday, September 1st, 2008

I’m almost done writing the new lobby system (Lobby2) for RakNet. The original design had a huge list of functions in a single class such as:

virtual void RegisterAccount(LobbyDBSpec::CreateUser_Data *input, SystemAddress lobbyServerAddr);
virtual void UpdateAccount(LobbyDBSpec::UpdateUser_Data *input);
virtual void ChangeHandle(const char *newHandle);

You would get back a callback with a result code, such as

virtual void RegisterAccount_Result(LobbyClientCBResult result){};
virtual void UpdateAccount_Result(LobbyClientCBResult result){};
virtual void ChangeHandle_Result(LobbyClientCBResult result){};

The mistakes I made were:

1. The database was designed to be more like a file system, storing the results of commands executed in C++, rather than processing the commands themselves. The problem was this was a huge amount of code. Sometimes it wasn’t really possible to synchronize the C++ and the database. Complex cases such as not allowing clan commands unless you were the clan leader were not handled well.
2. Result codes were generic and reused between commands. The problem was that this was imprecise. Sometimes more than one result code could apply, and you were never really sure which result codes could be returned.
3. You often didn’t have sufficient context about what the result is referring to. For example, if I got a change handle callback that failed because the name was already in use, what name had I been trying to change to?
4. The only way to extend the system was to derive a new class, understand the flow of the whole system, and write a huge amount of code.

On the positive side, having everything in one class did lead to good encapsulation and efficiency. All the systems could work together very smoothly with maximum performance efficiency.

In my own defense, a certain platform I have been programming on makes mistakes 2 and 3 as well.

Anyway, I’ve rewritten the system with those mistakes in mind.

1. The database performs the actual functionality, with almost no operative code in C++. This is more scalable, easier to extend, and can handle cases of any complexity.
2. Result codes are specific. There’s a small set of shared result codes, such as:

L2RC_SUCCESS,
L2RC_DATABASE_CONSTRAINT_FAILURE,
L2RC_PROFANITY_FILTER_CHECK_FAILED,

Besides that, the result codes directly apply to operations. So you know not only what codes you will get, but what codes you won’t get.

{REC_ENTER_ROOM_UNKNOWN_TITLE, “Failed to enter a room. Unknown title (Programmer error).”},
{REC_ENTER_ROOM_CURRENTLY_IN_QUICK_JOIN, “Failed to enter a room. You are currently in quick join. Leave quick join first.”},
{REC_ENTER_ROOM_CURRENTLY_IN_A_ROOM, “Failed to enter a room. You are already in a room.”},

3. What used to be function parameters are now structures. Structures have in and out parameters. The same structure is used to send the command, and to notify the user of the result of that command. So the system to a large extent is stateless, because the relevant state data is stored in the packet itself. This is less bandwidth efficient but much easier to use:

struct System_GetTitleRequiredAge : public Lobby2Message
{
__L2_MSG_BASE_IMPL(System_GetTitleRequiredAge)
virtual bool RequiresAdmin(void) const {return false;}
virtual bool CancelOnDisconnect(void) const {return true;}
virtual void Serialize( bool writeToBitstream, bool serializeOutput, RakNet::BitStream *bitStream );
virtual bool PrevalidateInput(void) {return true;}

// Input parameters
RakNet::RakString titleName;

// Output parameters
int requiredAge;
};

4. Because commands are encapsulated in a single structure, all you have to do to extend the system is write a new command and add that command to the class factory (which is one line of code). You can also override existing commands by passing your own class factory.

The C++ is essentially done at this point, except for clean-up and documentation. This system is much more complete than the old system:

1. System_CreateDatabase (Admin command)
2. System_DestroyDatabase (Admin command)
3. System_CreateTitle (Admin command)
4. System_DestroyTitle (Admin command)
5. System_GetTitleRequiredAge
6. System_GetTitleBinaryData
7. System_RegisterProfanity (Admin command)
8. System_BanUser (Admin command)
9. System_UnbanUser (Admin command)
10. CDKey_Add (Admin command)
11. CDKey_GetStatus (Admin command)
12. CDKey_Use (Admin command)
13. CDKey_FlagStolen (Admin command)
14. Client_Login
15. Client_Logoff
16. Client_RegisterAccount
17. System_SetEmailAddressValidated (Admin command)
18. Client_ValidateHandle
19. Client_DeleteAccount
20. System_PruneAccounts
21. Client_GetEmailAddress
22. Client_GetPasswordRecoveryQuestionByHandle
23. Client_GetPasswordRecoveryAnswerWithQuestion
24. Client_ChangeHandle
25. Client_UpdateAccount
26. Client_StartIgnore
27. Client_StopIgnore
28. Client_GetIgnoreList
29. Friends_SendInvite
30. Friends_AcceptInvite
31. Friends_RejectInvite
32. Friends_GetInvites
33. Friends_GetStatus
34. Friends_Remove
35. RecentUsers_Add
36. RecentUsers_Get
37. Emails_Send
38. Emails_Get
39. Emails_Delete
40. Emails_SetStatus
41. Emails_SetWasRead
42. Ranking_SubmitMatch
43. Ranking_GetMatches
44. Ranking_GetMatchBinaryData
45. Ranking_GetTotalScore
46. Ranking_WipeScoresForPlayer
47. Ranking_WipeMatches
48. Ranking_PruneMatches
49. Ranking_UpdateRating
50. Ranking_WipeRatings
51. Ranking_GetRating
52. Clans_Create
53. Clans_SetProperties
54. Clans_GetProperties
55. Clans_SetMyMemberProperties
56. Clans_GrantLeader
57. Clans_SetSubleaderStatus
58. Clans_SetMemberRank
59. Clans_GetMemberProperties
60. Clans_ChangeHandle
61. Clans_Leave
62. Clans_Get
63. Clans_SendJoinInvitation
64. Clans_WithdrawJoinInvitation
65. Clans_AcceptJoinInvitation
66. Clans_RejectJoinInvitation
67. Clans_DownloadInvitationList
68. Clans_SendJoinRequest
69. Clans_WithdrawJoinRequest
70. Clans_AcceptJoinRequest
71. Clans_RejectJoinRequest
72. Clans_DownloadRequestList
73. Clans_KickAndBlacklistUser
74. Clans_UnblacklistUser
75. Clans_GetBlacklist
76. Clans_GetMembers
77. Clans_CreateBoard
78. Clans_DestroyBoard
79. Clans_CreateNewTopic
80. Clans_ReplyToTopic
81. Clans_RemovePost
82. Clans_GetBoards
83. Clans_GetTopics
84. Clans_GetPosts
85. Notification_Client_IgnoreStatus (Admin command)
86. Notification_Friends_StatusChange (Admin command)
87. Notification_Friends_ChangedHandle (Admin command)
88. Notification_Friends_CreatedClan (Admin command)
89. Notification_Emails_Received (Admin command)
90. Notification_Clans_GrantLeader (Admin command)
91. Notification_Clans_SetSubleaderStatus (Admin command)
92. Notification_Clans_SetMemberRank (Admin command)
93. Notification_Clans_ChangeHandle (Admin command)
94. Notification_Clans_Leave (Admin command)
95. Notification_Clans_PendingJoinStatus (Admin command)
96. Notification_Clans_NewClanMember (Admin command)
97. Notification_Clans_KickAndBlacklistUser (Admin command)
98. Notification_Clans_UnblacklistUser (Admin command)

Now I’m just waiting for the DB programmer to write all the queries.