{"id":361,"date":"2008-09-01T02:11:48","date_gmt":"2008-09-01T06:11:48","guid":{"rendered":"http:\/\/www.rakkar.org\/blog\/?p=361"},"modified":"2008-09-01T02:11:48","modified_gmt":"2008-09-01T06:11:48","slug":"lobby-2-almost-done","status":"publish","type":"post","link":"https:\/\/rakkar.org\/blog\/index.php\/2008\/09\/01\/lobby-2-almost-done\/","title":{"rendered":"Lobby 2 almost done"},"content":{"rendered":"<p>\t\t\t\tI&#8217;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:<\/p>\n<p>virtual void RegisterAccount(LobbyDBSpec::CreateUser_Data *input, SystemAddress lobbyServerAddr);<br \/>\nvirtual void UpdateAccount(LobbyDBSpec::UpdateUser_Data *input);<br \/>\nvirtual void ChangeHandle(const char *newHandle);<\/p>\n<p>You would get back a callback with a result code, such as<\/p>\n<p>virtual void RegisterAccount_Result(LobbyClientCBResult result){};<br \/>\nvirtual void UpdateAccount_Result(LobbyClientCBResult result){};<br \/>\nvirtual void ChangeHandle_Result(LobbyClientCBResult result){};<\/p>\n<p>The mistakes I made were:<\/p>\n<p>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&#8217;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.<br \/>\n2. 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.<br \/>\n3. You often didn&#8217;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?<br \/>\n4. 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.<\/p>\n<p>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.<\/p>\n<p>In my own defense, a certain platform I have been programming on makes mistakes 2 and 3 as well.<\/p>\n<p>Anyway, I&#8217;ve rewritten the system with those mistakes in mind.<\/p>\n<p>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.<br \/>\n2. Result codes are specific. There&#8217;s a small set of shared result codes, such as:<\/p>\n<p>L2RC_SUCCESS,<br \/>\nL2RC_DATABASE_CONSTRAINT_FAILURE,<br \/>\nL2RC_PROFANITY_FILTER_CHECK_FAILED,<\/p>\n<p>Besides that, the result codes directly apply to operations. So you know not only what codes you will get, but what codes you won&#8217;t get.<\/p>\n<p>{REC_ENTER_ROOM_UNKNOWN_TITLE, &#8220;Failed to enter a room. Unknown title (Programmer error).&#8221;},<br \/>\n{REC_ENTER_ROOM_CURRENTLY_IN_QUICK_JOIN, &#8220;Failed to enter a room. You are currently in quick join. Leave quick join first.&#8221;},<br \/>\n{REC_ENTER_ROOM_CURRENTLY_IN_A_ROOM, &#8220;Failed to enter a room. You are already in a room.&#8221;},<\/p>\n<p>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:<\/p>\n<p>struct System_GetTitleRequiredAge : public Lobby2Message<br \/>\n{<br \/>\n\t__L2_MSG_BASE_IMPL(System_GetTitleRequiredAge)<br \/>\n\t\tvirtual bool RequiresAdmin(void) const {return false;}<br \/>\n\tvirtual bool CancelOnDisconnect(void) const {return true;}<br \/>\n\tvirtual void Serialize( bool writeToBitstream, bool serializeOutput, RakNet::BitStream *bitStream );<br \/>\n\tvirtual bool PrevalidateInput(void) {return true;}<\/p>\n<p>\t\/\/ Input parameters<br \/>\n\tRakNet::RakString titleName;<\/p>\n<p>\t\/\/ Output parameters<br \/>\n\tint requiredAge;<br \/>\n};<\/p>\n<p>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.<\/p>\n<p>The C++ is essentially done at this point, except for clean-up and documentation. This system is much more complete than the old system:<\/p>\n<p>1. System_CreateDatabase (Admin command)<br \/>\n2. System_DestroyDatabase (Admin command)<br \/>\n3. System_CreateTitle (Admin command)<br \/>\n4. System_DestroyTitle (Admin command)<br \/>\n5. System_GetTitleRequiredAge<br \/>\n6. System_GetTitleBinaryData<br \/>\n7. System_RegisterProfanity (Admin command)<br \/>\n8. System_BanUser (Admin command)<br \/>\n9. System_UnbanUser (Admin command)<br \/>\n10. CDKey_Add (Admin command)<br \/>\n11. CDKey_GetStatus (Admin command)<br \/>\n12. CDKey_Use (Admin command)<br \/>\n13. CDKey_FlagStolen (Admin command)<br \/>\n14. Client_Login<br \/>\n15. Client_Logoff<br \/>\n16. Client_RegisterAccount<br \/>\n17. System_SetEmailAddressValidated (Admin command)<br \/>\n18. Client_ValidateHandle<br \/>\n19. Client_DeleteAccount<br \/>\n20. System_PruneAccounts<br \/>\n21. Client_GetEmailAddress<br \/>\n22. Client_GetPasswordRecoveryQuestionByHandle<br \/>\n23. Client_GetPasswordRecoveryAnswerWithQuestion<br \/>\n24. Client_ChangeHandle<br \/>\n25. Client_UpdateAccount<br \/>\n26. Client_StartIgnore<br \/>\n27. Client_StopIgnore<br \/>\n28. Client_GetIgnoreList<br \/>\n29. Friends_SendInvite<br \/>\n30. Friends_AcceptInvite<br \/>\n31. Friends_RejectInvite<br \/>\n32. Friends_GetInvites<br \/>\n33. Friends_GetStatus<br \/>\n34. Friends_Remove<br \/>\n35. RecentUsers_Add<br \/>\n36. RecentUsers_Get<br \/>\n37. Emails_Send<br \/>\n38. Emails_Get<br \/>\n39. Emails_Delete<br \/>\n40. Emails_SetStatus<br \/>\n41. Emails_SetWasRead<br \/>\n42. Ranking_SubmitMatch<br \/>\n43. Ranking_GetMatches<br \/>\n44. Ranking_GetMatchBinaryData<br \/>\n45. Ranking_GetTotalScore<br \/>\n46. Ranking_WipeScoresForPlayer<br \/>\n47. Ranking_WipeMatches<br \/>\n48. Ranking_PruneMatches<br \/>\n49. Ranking_UpdateRating<br \/>\n50. Ranking_WipeRatings<br \/>\n51. Ranking_GetRating<br \/>\n52. Clans_Create<br \/>\n53. Clans_SetProperties<br \/>\n54. Clans_GetProperties<br \/>\n55. Clans_SetMyMemberProperties<br \/>\n56. Clans_GrantLeader<br \/>\n57. Clans_SetSubleaderStatus<br \/>\n58. Clans_SetMemberRank<br \/>\n59. Clans_GetMemberProperties<br \/>\n60. Clans_ChangeHandle<br \/>\n61. Clans_Leave<br \/>\n62. Clans_Get<br \/>\n63. Clans_SendJoinInvitation<br \/>\n64. Clans_WithdrawJoinInvitation<br \/>\n65. Clans_AcceptJoinInvitation<br \/>\n66. Clans_RejectJoinInvitation<br \/>\n67. Clans_DownloadInvitationList<br \/>\n68. Clans_SendJoinRequest<br \/>\n69. Clans_WithdrawJoinRequest<br \/>\n70. Clans_AcceptJoinRequest<br \/>\n71. Clans_RejectJoinRequest<br \/>\n72. Clans_DownloadRequestList<br \/>\n73. Clans_KickAndBlacklistUser<br \/>\n74. Clans_UnblacklistUser<br \/>\n75. Clans_GetBlacklist<br \/>\n76. Clans_GetMembers<br \/>\n77. Clans_CreateBoard<br \/>\n78. Clans_DestroyBoard<br \/>\n79. Clans_CreateNewTopic<br \/>\n80. Clans_ReplyToTopic<br \/>\n81. Clans_RemovePost<br \/>\n82. Clans_GetBoards<br \/>\n83. Clans_GetTopics<br \/>\n84. Clans_GetPosts<br \/>\n85. Notification_Client_IgnoreStatus (Admin command)<br \/>\n86. Notification_Friends_StatusChange (Admin command)<br \/>\n87. Notification_Friends_ChangedHandle (Admin command)<br \/>\n88. Notification_Friends_CreatedClan (Admin command)<br \/>\n89. Notification_Emails_Received (Admin command)<br \/>\n90. Notification_Clans_GrantLeader (Admin command)<br \/>\n91. Notification_Clans_SetSubleaderStatus (Admin command)<br \/>\n92. Notification_Clans_SetMemberRank (Admin command)<br \/>\n93. Notification_Clans_ChangeHandle (Admin command)<br \/>\n94. Notification_Clans_Leave (Admin command)<br \/>\n95. Notification_Clans_PendingJoinStatus (Admin command)<br \/>\n96. Notification_Clans_NewClanMember (Admin command)<br \/>\n97. Notification_Clans_KickAndBlacklistUser (Admin command)<br \/>\n98. Notification_Clans_UnblacklistUser (Admin command)<\/p>\n<p>Now I&#8217;m just waiting for the DB programmer to write all the queries.\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[],"_links":{"self":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/posts\/361"}],"collection":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=361"}],"version-history":[{"count":0,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/posts\/361\/revisions"}],"wp:attachment":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=361"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=361"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=361"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}