Putting it all Together (FSM part 2)Now we can load and save files, we can finish off our Finite State Machine. Here is a diagram of how our Finite State Machine will operate: Here is a list of the different states: US_ENTERUSERNAME
US_ENTERPASSWORD
US_CREATEPASSWORD1
US_CREATEPASSWORD2
US_TALKING
Here is the code for all of that: void CUser::Execute(AString& str) { // check for 0 length string size of 1 for nul terminator if(str.GetLength() <= 1) { return; } switch(state) { case US_NONE: break; case US_TALKING: CmdSpeak(str); break; case US_ENTERUSERNAME: // Check that length is small enough (don't want it too big) // An exercise - make length configurable in mud.xml // (remember to then make length only affect new user names, // we don't want making it smaller stopping people logging in) if(str.GetLength() < 64) { userdata = GETSERVER->datafile.GrabUserByName(str); if(userdata != NULL) // does the user exit { SendText(A_STR("Welcome Back ") + str + A_STR("!\r\nEnter Password:\r\n")); state = US_ENTERPASSWORD; } else // userdoesn'tn't exit - create and ask for password { SendText(A_STR("User ") + str + A_STR(" Does Not Exist - Creating\r\nEnter Password:\r\n")); userdata = new TiXmlElement("user"); userdata->SetAttribute("name", str.GetString()); state = US_CREATEPASSWORD1; } } else { SendText(A_STR("Too Long! Try again...\r\n")); } break; case US_ENTERPASSWORD: // Check the password matches if(str == AString(userdata->Attribute("pass")) ) { // Now we can start talking state = US_TALKING; username = AString(userdata->Attribute("name")); SendText(A_STR("Logged In!\r\n\r\n")); GETSERVER->Broadcast(A_STR("Please welcome ") + username + A_STR(" to the server!\r\n\r\n")); } else { SendText(A_STR("Password does not match - try again...\r\n")); } break; case US_CREATEPASSWORD1: if(str.GetLength() < 64) { userdata->SetAttribute("pass", str.GetString()); state = US_CREATEPASSWORD2; SendText(A_STR("Confirm Password:\r\n")); } else { SendText(A_STR("Too Long! Try again...\r\n")); } break; case US_CREATEPASSWORD2: if(str == AString(userdata->Attribute("pass")) ) { // User account created successfuly - add to database // This transfers the object to tinyxml GETSERVER->datafile.UserListElement->LinkEndChild(userdata); // Now we can start talking state = US_TALKING; username = AString(userdata->Attribute("name")); SendText(A_STR("Account Created! Logged In!\r\n")); GETSERVER->Broadcast(A_STR("Please welcome the new user, ") + username + A_STR(", to the server!\r\n\r\n")); } else // No Match { SendText(A_STR("Password does not match - try again...\r\n")); } break; } } Before we finish up, I'll describe what owns the XML element where, as this could be somewhat confusing. In US_ENTERUSERNAME, the XML variable is set to NULL, and it knows nothing about the XML element. If it goes into US_ENTERPASSWORD, then it gets a pointer to an XML element that is owned by, and constructed and deconstructed by TinyXML. However if it is a new user account being created, the XML element we create is owned by us, and not by TinyXML. We are responsible for deleting that element. That is – unless we register it with TinyXML, which will make TinyXML in charge of deleting it when it's done, and also, for saving it to the XML file. You can see it being registered in the code that handles US_CREATEPASSWORD2. If the user is in the talking state, we are guaranteed that TinyXML owns the XML element (unless we made some sort of coding mistake). Final NotesIt has been a long time since I started writing this article, and even longer since I released the first one. Real life makes it quite hard to have time to do these things. Apologies to all those who have sat around waiting for it, and thank you to the many people who emailed me telling me to get on with it. Most importantly, in this time, I have come to use the STL (standard template library) almost religiously (whereas previously I used it rarely, if ever). I highly recommend learning how to use it. Anyway, you now have a functional chat server. It is now possible to continue programming your MUD server on your own, and I highly recommend doing so. Later tutorials are not going to be based on the code as much, so are unlikely to break anything. When I say "functional", I do not recommend using it for any sort of public server. There are some huge security holes. Most importantly, we don't check for things like quote marks in user names, which, when saved directly to TinyXML could very easily muck up the XML file. I will address these security problems in a later article. Until next time, Happy coding, Andrew Russell. |
|