MMORPG Tutorial Article 6 Logins R US
by Dreamer · 04/14/2005 (12:13 pm) · 35 comments
First make sure you have followed ALL of the other tutorials, starting with this one.
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7514
In it's default setup TGE has a pretty straightforward connection sequence.
#1 Start Server
#2 Connect To Server
#3 Send mission information.
What we need to do is to interrupt this sequence between steps 2 and 3 and insert authentication of some kind.
I am using a local SQLite database for all character information storage, a better solution would be to setup an offsite PHP/MySQL auth server for login information and then store specific character information like level, inventory etc in a local DB on the server side which is updated against another MySQL server every hour or so.
For right now though lets just dump everything into a server side SQLite DB, I am calling mine Dream, you may call it anything you like, just remember to delete it at the end of testing like any .dso
Just a heads up, the UserName and Passwords are both sent and stored as plain text, it would be VERY wise to at least apply a CRC32 to the password before storage and while doing comparisons. But I have chosen to not do this at the moment, since a good CRC tut is a little hard to find and I want to keep these articles on topic as much as possible.
Since we are adjusting alot of stuff server side, and merely ADDING things to the client side lets start with the Server code.
Open up server/scripts/commands.cs and add the following code
Now create a file named dbcommands.cs in server/scripts and add this code to it.
Thats our authentication code serverside now we need to interrupt the login sequence
common/server/client_connection.cs (this will be above starter.fps)
Edit the on connect function
That should be about it for the server side stuff, now on to the client
In client/scripts/client_commands.cs add the following
Now we need to add 2 GUI's and edit the main menu
client/gui/mainmenugui.gui edit to the following
Now add a new file called client/ui/AccountGUI.gui
That should be it, to make all of this work, you need to run 2 instances of TGE one as a dedicated server, and another as a client with the following command
torqueDemo_Debug.bin -connect 127.0.0.1 (or whatever IP if you happen to be using a remote server)
Enjoy!
In our next tutorial we will make use of the Race information and make a proper character creation screen!
Next Tutorial
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7695
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7514
In it's default setup TGE has a pretty straightforward connection sequence.
#1 Start Server
#2 Connect To Server
#3 Send mission information.
What we need to do is to interrupt this sequence between steps 2 and 3 and insert authentication of some kind.
I am using a local SQLite database for all character information storage, a better solution would be to setup an offsite PHP/MySQL auth server for login information and then store specific character information like level, inventory etc in a local DB on the server side which is updated against another MySQL server every hour or so.
For right now though lets just dump everything into a server side SQLite DB, I am calling mine Dream, you may call it anything you like, just remember to delete it at the end of testing like any .dso
Just a heads up, the UserName and Passwords are both sent and stored as plain text, it would be VERY wise to at least apply a CRC32 to the password before storage and while doing comparisons. But I have chosen to not do this at the moment, since a good CRC tut is a little hard to find and I want to keep these articles on topic as much as possible.
Since we are adjusting alot of stuff server side, and merely ADDING things to the client side lets start with the Server code.
Open up server/scripts/commands.cs and add the following code
function serverCmdLoadZone(%client,%AccountName){
echo("\n\nIf we are seeing this the mission should be downloading to the client\n\n");
sendLoadInfoToClient(%client);
%client.guid = 0;
addToServerGuidList( %client.guid );
// Set admin status
if (%client.getAddress() $= "local") {
%client.isAdmin = true;
%client.isSuperAdmin = true;
}else{
%client.isAdmin = false;
%client.isSuperAdmin = false;
}
// Save client preferences on the connection object for later use.
%client.setPlayerName(%name);
%client.score = 0;
%client.name = %AccountName;
%client.Level = getClientLevel(%client);
//
$instantGroup = ServerGroup;
$instantGroup = MissionCleanup;
echo("CADD: " @ %client @ " " @ %client.getAddress());
// Inform the client of all the other clients
%count = ClientGroup.getCount();
for (%cl = 0; %cl < %count; %cl++) {
%other = ClientGroup.getObject(%cl);
if ((%other != %client)) {
// These should be "silent" versions of these messages...
messageClient(%client, 'MsgClientJoin', "",
%other.name,
%other,
%other.sendGuid,
%other.score,
%other.isAIControlled(),
%other.isAdmin,
%other.isSuperAdmin);
}
}
// Inform the client we've joined up
messageClient(%client,
'MsgClientJoin', '\c2Welcome to Dream! %1.',
%client.name,
%client,
%client.sendGuid,
%client.score,
%client.isAiControlled(),
%client.isAdmin,
%client.isSuperAdmin);
// Inform all the other clients of the new guy
messageAllExcept(%client, -1, 'MsgClientJoin', '\c1%1 joined the game.',
%client.name,
%client,
%client.sendGuid,
%client.score,
%client.isAiControlled(),
%client.isAdmin,
%client.isSuperAdmin);
// If the mission is running, go ahead download it to the client
if ($missionRunning){
%client.loadMission();
$Server::PlayerCount++;
}
}Now create a file named dbcommands.cs in server/scripts and add this code to it.
$dbname = "dream";
$SqLite = new SqLiteObject(sqlite);
if ($SqLite == 0){
echo("ERROR: Failed to create SqLiteObject. $SqLiteObject aborted.");
return;
}
// open database
if ($SqLite.openDatabase($dbname) == 0){
echo("ERROR: Failed to open database: " @ $dbname);
echo(" Ensure that the disk is not full or write protected. $SqLiteObject aborted.");
return;
}
//Reports errors on Query Fail
function SqLite::onQueryFailed(%this, %error)
{
echo ("$SqLite Query Error: " @ %error);
}
//Account TABLE functions
function serverCmdValidateAccount(%client,%AccountName,%AccountPassword){
%query = "CREATE TABLE Accounts (Name VARCHAR(20),password VARCHAR(20),Race VARCHAR(20),Level INT(11))";
%result = $SqLite.query(%query, 0);
if (%result == 0)
{
// query failed
echo("Table already exists table creation aborted.");
//
//
//return;
}
if(%AccountName !$="" && %AccountPassword !$=""){
%query ="SELECT * FROM Accounts WHERE Name = \'" @ %AccountName @ "\' AND Password = \'" @ %AccountPassword @ "\'";
echo(%query);
%result = $SqLite.query(%query,0);
if ($SqLite.numRows(%result) > 0){
echo("If we are seeing this then the server has found a match for the Account name and Password");
$SqLite.clearResult(%result);
commandToClient(%client,'ValidateAccountResult','TRUE');
serverCmdLoadZone(%client,%AccountName);
}else{
%query ="SELECT * FROM Accounts WHERE Name = \'" @ %AccountName @ "\'";
%result = $SqLite.query(%query,0);
echo(%query);
if($SqLite.numRows(%result) > 0){
echo("If we are seeing this then the server has found a match for the Account Name but not the Password");
$SqLite.clearResult(%result);
commandToClient(%client,'ValidateAccountResult','WRONGPASSWORD');
return("WRONGPASSWORD");
}else{
echo("If we are seeing this then the server was unable to find a match for the Account name and Password");
$SqLite.clearResult(%result);
commandToClient(%client,'ValidateAccountResult','NEW');
return("NEW");
}
}
}else{
commandToClient(%client,'ValidateAccountResult','NULL');
}
}
function serverCmdCreateAccount(%client,%AccountName,%AccountPassword,%Race){
if(%Race $=""){
%Race ="Glowy";
}
%query ="INSERT INTO Accounts VALUES (\'" @ %AccountName @ "\',\'" @ %AccountPassword @ "\',\'"@%Race@"\',\'1\')";
echo(%query);
%result = $SqLite.query(%query,0);
if(%result != 0){
echo("If we are seeing this then the server was able to insert the Account name, Password and Race");
$SqLite.clearResult(%result);
commandToClient(%client,'ValidateAccountResult','TRUE');
}else{
echo("If we are seeing this then the server was not able to insert the Account name, Password and Race");
$SqLite.clearResult(%result);
commandToClient(%client,'ValidateAccountResult','FALSE');
}
}
function getPlayerBody(%name){
%query = "SELECT * from Accounts WHERE Name = \'"@%name@"\'";
echo(%query);
%result = $SqLite.query(%query,0);
echo(%result);
if(%result){
%Race = $SqLite.getColumn(%result, "Race");
$SqLite.clearResult(%result);
return(%Race);
}else{
echo("Uhoh! This player has no body, guess we can just choose one... Might want to figure out Why this happened?");
return("Glowy"); //Well we gotta make 'em somethin now don't we
}
}Thats our authentication code serverside now we need to interrupt the login sequence
common/server/client_connection.cs (this will be above starter.fps)
Edit the on connect function
function GameConnection::onConnect( %client, %name, %password)
{
// Send down the connection error info, the client is
// responsible for displaying this message if a connection
// error occured.
messageClient(%client,'MsgConnectionError',"",$Pref::Server::ConnectionError);
//Validate the client
echo("Attempting to Authenticate Client " @ %client);
serverCmdValidateAccount(%client,%name,%password);
}That should be about it for the server side stuff, now on to the client
In client/scripts/client_commands.cs add the following
//This function handles client side Authentication
function LoginAccount(){
echo($Pref::Player::Name @" Is attempting to login, using password " @ $Pref::Player::Password);
echo("We are already connected, attempting to Authenticate");
CommandToServer('ValidateAccount',$Pref::Player::Name,$Pref::Player::Password);
}
}
//This function handles the clients part in creating accounts
function CreateAccount(){
if($Pref::Player::Name !$="" && $Pref::Player::Password !$= "" && $Pref::Player::Race !$=""){
echo("Attempting to create " @ $Pref::Player::Name @", using password " @ $Pref::Player::Password);
CommandToServer('CreateAccount',$Pref::Player::Name,$Pref::Player::Password,$Pref::Player::Race);
}else{
MessageBoxOK("Error!","Your User Name and/or Password and/or Body are empty");
}
}
function ChooseRace(){
echo("We are attempting to discern race for a new account");
if(RaceBoxOrc.getValue()){
echo("Looks like an Orc to me!");
$Pref::Player::Race = "Orc";
}else{
if(RaceBoxHuman.getValue()){
echo("Looks like a Human to me!");
$Pref::Player::Race = "Human";
}else{
if(RaceBoxGlowy.getValue()){
echo("Looks like a Glowy to me!");
$Pref::Player::Race = "Glowy";
}else{
echo("Looks like nothin to me!");
$Pref::Player::Race = "None";
}
}
}
if($Pref::Player::Race !$="None"){
echo("Ok so we choose a race now lets confirm it, just to be sure");
MessageBoxYesNO("Race","You have choosen to be "@$Pref::Player::Race,"Canvas.PopDialog(ChooseRaceGUI);CreateAccount();","");
}else{
echo("They didn't wanna be nuthin?");
MessageBoxOK("Error!","Please choose a race!");
}
echo("Huh? Wuh? What am I doing here? Looks like we fell through!");
}
//This function handles validate account responses from the server
function clientCmdValidateAccountResult(%Result){
%errmsg = "";
$Result = detag(%Result);
//The following could be done much more cleanly if switch and case statements functioned properly
if(strpos(%Result,"TRUE") != -1){
Canvas.PopDialog(AccountGUI);
MessageBoxOK("Success!","You have now successfully logged in!","LoginAccount();");
}else{
if(strpos(%Result,"NEW") != -1 ){
if($Pref::Player::Name !$="" && $Pref::Player::Password !$= ""){
MessageBoxYesNo( "New Account?","Do you wish to create a new account?","Canvas.PushDialog(ChooseRaceGUI);", "");
}else{
MessageBoxOK("Error!","Your User Name and/or Password are empty");
}
}else{
if(strpos(%Result,"WRONGPASSWORD") != -1){
MessageBoxOK("Error!","Your Password is incorrect");
}else{
if(%Result !$= ""){
%errmsg = "The server returned an invalid response\nThe response was " @ %Result;
}else{
%errmsg = "The server returned a NULL response!";
}
MessageBoxOK("Error!",%errmsg);
}
if(strpos(%Result,"NULL") != -1){
MessageBoxOK("Error!","Your User Name and/or Password are empty");
}
}
}
}Now we need to add 2 GUI's and edit the main menu
client/gui/mainmenugui.gui edit to the following
new GuiButtonCtrl() {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "top";
position = "36 264";
extent = "110 20";
minExtent = "8 8";
visible = "1";
command = "Canvas.PushDialog(AccountGUI);";
text = "Login";
groupNum = "-1";
buttonType = "PushButton";
helpTag = "0";
};Now add a new file called client/ui/AccountGUI.gui
new GuiControl(AccountGUI) {
profile = "GuiDefaultProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "0 0";
extent = "320 240";
minExtent = "8 8";
visible = "1";
helpTag = "0";
new GuiWindowCtrl() {
profile = "GuiWindowProfile";
horizSizing = "center";
vertSizing = "center";
position = "131 10";
extent = "377 303";
minExtent = "8 8";
visible = "1";
helpTag = "0";
text ="Account Info";
maxLength = "255";
resizeWidth = "0";
resizeHeight = "0";
canMove = "1";
canClose = "1";
canMinimize = "0";
canMaximize = "0";
minSize = "25 25";
closeCommand = "Canvas.popDialog(AccountGUI);";
new GuiTextCtrl() {
profile = "GuiTextProfile";
horizSizing = "right";
vertSizing = "top";
position = "36 30";
extent = "63 18";
minExtent = "8 8";
visible = "1";
text = "User Name:";
maxLength = "255";
helpTag = "0";
};
new GuiTextEditCtrl() {
profile = "GuiTextEditProfile";
horizSizing = "right";
vertSizing = "top";
position = "36 50";
extent = "134 18";
minExtent = "8 8";
visible = "1";
variable = "$Pref::Player::Name";
maxLength = "255";
historySize = "1";
password = "0";
tabComplete = "0";
sinkAllKeyEvents = "0";
helpTag = "0";
};
new GuiTextCtrl() {
profile = "GuiTextProfile";
horizSizing = "right";
vertSizing = "top";
position = "36 70";
extent = "63 18";
minExtent = "8 8";
visible = "1";
text = "Password:";
maxLength = "255";
helpTag = "0";
};
new GuiTextEditCtrl() {
profile = "GuiTextEditProfile";
horizSizing = "right";
vertSizing = "top";
position = "36 90";
extent = "134 18";
minExtent = "8 8";
visible = "1";
variable = "$Pref::Player::Password";
maxLength = "255";
historySize = "1";
password = "1";
tabComplete = "0";
sinkAllKeyEvents = "0";
helpTag = "0";
};
new GuiButtonCtrl() {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "top";
position = "36 110";
extent = "134 18";
minExtent = "8 8";
visible = "1";
command = "LoginAccount();";
text = "Login";
groupNum = "-1";
buttonType = "PushButton";
helpTag = "0";
};
};
};
//--- OBJECT WRITE BEGIN ---
new GuiWindowCtrl(ChooseRaceGUI) {
profile = "GuiWindowProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "0 0";
extent = "640 480";
minExtent = "8 2";
visible = "1";
maxLength = "255";
resizeWidth = "1";
resizeHeight = "1";
canMove = "1";
canClose = "1";
canMinimize = "1";
canMaximize = "1";
minSize = "50 50";
new GuiRadioCtrl(RaceBoxOrc) {
profile = "GuiRadioProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "208 106";
extent = "140 30";
minExtent = "8 2";
visible = "1";
text = "Orc";
groupNum = "1";
buttonType = "RadioButton";
};
new GuiRadioCtrl(RaceBoxHuman) {
profile = "GuiRadioProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "208 136";
extent = "140 30";
minExtent = "8 2";
visible = "1";
text = "Human";
groupNum = "1";
buttonType = "RadioButton";
};
new GuiRadioCtrl(RaceBoxGlowy) {
profile = "GuiRadioProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "208 164";
extent = "140 30";
minExtent = "8 2";
visible = "1";
text = "Glowy";
groupNum = "1";
buttonType = "RadioButton";
};
new GuiButtonCtrl() {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "211 203";
extent = "140 30";
minExtent = "8 2";
visible = "1";
command = "ChooseRace();";
text = "Race";
groupNum = "11";
buttonType = "PushButton";
};
};That should be it, to make all of this work, you need to run 2 instances of TGE one as a dedicated server, and another as a client with the following command
torqueDemo_Debug.bin -connect 127.0.0.1 (or whatever IP if you happen to be using a remote server)
Enjoy!
In our next tutorial we will make use of the Race information and make a proper character creation screen!
Next Tutorial
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7695
#22
Thanks man, sorry for being so much of a pain :)
04/26/2005 (1:00 pm)
Well I think I fixed the memory problem, I was useing a custom player.cs file and was setting up multi characters so I copyed the player.cs from torque 1.3 sdk and everything seems fine now. Just dont know why it waited untill the AIPlayer.cs ran before it crashed unless it was something to do with the player blocks. Ill check that out latter :)Thanks man, sorry for being so much of a pain :)
#23
04/26/2005 (3:54 pm)
humm just thout i would ask how long it take to get a resorce posted? any way you can e-mail thim 2 me? :) recaci@yahoo.com
#24
In short it's a bit of a crap shoot, but the anticipation is fun, it also allows me time to tinker with the code a little to get out things I may not have wanted to share (for instance my DB is password protected and I had left that in plain site on this one, so I removed it just in time), and add things I find kewl and new, all before I have to go about the work of supporting the resource.
04/26/2005 (5:00 pm)
On average for me at least it's around 7-10 days from the time I submit a resource until it actually posts, however tuts 1-5 took 2 weeks to post, they were all submited on various days and wound up all hitting on the same day oddly enough 4 and 5 hit, then 1,2,3 kinda wierd...In short it's a bit of a crap shoot, but the anticipation is fun, it also allows me time to tinker with the code a little to get out things I may not have wanted to share (for instance my DB is password protected and I had left that in plain site on this one, so I removed it just in time), and add things I find kewl and new, all before I have to go about the work of supporting the resource.
#25
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7695
04/27/2005 (12:30 pm)
#7 is up!www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7695
#26
I'm still kind of newb at C (and Torque Script) but I've spent a year working on every inch of the game process in developing an MMORPGFPS using Torque.
I have a bug, but really, I think it's just a misunderstanding of the network code.
I am using MySQL for my character storage (and soon to add a flat file system per "zone" to handle loot and spawn tables thanks to you).
So, posting my code probably won't help, but it's not too far from yours.
Here is my problem.
In common\server\scripts\clientConnections.cd in function GameConnection::onConnect the only command besides the client message is serverCmdValidateAccount(%client,%name,%password); after following your tutorial.
I understand that serverCmdValidateAccount begins the authentication proccess for the client and then calls serverCmdLoadZone which sends sendLoadInfoToClient, etc. to get the ball rolling for the client.
The Oddest thing is that I have remmed out the serverCmdLoadZone in serverCmdValidateAccount and the mission still loads on the client and the game is up.
How is this possible?
I thought that sendLoadInfoToClient in serverCmdLoadZone is responsible for telling the client to start loading he mission and load up the PlayGui etc.
I just remmed the line so that the server would not load the level to the client after the authentication was verified just for debugging purposes.
But it still loads on the client.
Have I missed the entire plot?
To miss something this big on the logon process could be the demise of my game (if lack of fundage doesn't get me first).
Any help is greatly appreciated.
Ari Rule (Only dev left on team)
*btw, all of your tutorials work great on stock 1.3 (at least until I break em) :)
05/05/2005 (4:40 am)
Hi Dreamer,I'm still kind of newb at C (and Torque Script) but I've spent a year working on every inch of the game process in developing an MMORPGFPS using Torque.
I have a bug, but really, I think it's just a misunderstanding of the network code.
I am using MySQL for my character storage (and soon to add a flat file system per "zone" to handle loot and spawn tables thanks to you).
So, posting my code probably won't help, but it's not too far from yours.
Here is my problem.
In common\server\scripts\clientConnections.cd in function GameConnection::onConnect the only command besides the client message is serverCmdValidateAccount(%client,%name,%password); after following your tutorial.
I understand that serverCmdValidateAccount begins the authentication proccess for the client and then calls serverCmdLoadZone which sends sendLoadInfoToClient, etc. to get the ball rolling for the client.
The Oddest thing is that I have remmed out the serverCmdLoadZone in serverCmdValidateAccount and the mission still loads on the client and the game is up.
How is this possible?
I thought that sendLoadInfoToClient in serverCmdLoadZone is responsible for telling the client to start loading he mission and load up the PlayGui etc.
I just remmed the line so that the server would not load the level to the client after the authentication was verified just for debugging purposes.
But it still loads on the client.
Have I missed the entire plot?
To miss something this big on the logon process could be the demise of my game (if lack of fundage doesn't get me first).
Any help is greatly appreciated.
Ari Rule (Only dev left on team)
*btw, all of your tutorials work great on stock 1.3 (at least until I break em) :)
#27
05/05/2005 (6:57 am)
That issue doesn't replicate over here, when I comment out the line you speak of I just get a normal connection success message, but the zone won't load. Just to verify, you DID delete all your .dso's after changing the source right?
#28
The results you got is what I expected but don't get, my zone still loads and that's just not possible.
Guess it's roll back time, thanks for verifying, glad I didn't miss the plot.
Ari Rule
*edit:
OK... Found the problem.
The shortcut I was using to the dedicated server was pathed to an old rev.
I use several SDK folders for the dev process and when I copy or move stuff the shortcut paths are not updated.
3 days of stress because of a bad path in a shortcut...
Tempted to get "Newb" tatooed on me so I don't forget the simple stuff.
Or K.I.S.S. (Keep it simple stupid).
Imagine changing code, deleting the .dso files, and nothing changed!
I thought I was losing my mind.
Now the login is working awesome with MySQL and I integrated your AccountGUI into the Join menu so that master servers can be used (think server clusters of zones and 1 login server connected to a master server per cluster).
Torque allows multiple master servers for failover.
Now, right before serverCmdLoadZone gets called I need to have it ask the SQL database which zone the character is in and tell the client to connect to that zone.
There is a tutorial here on GG that explains forcing a client to connect to another server.
This will result in 2 different server modes, login server(connects to a master server) and zone server.
This is why I have to use MySQL on a dedicated and not SQLite.
All of my servers except the masters have to communicate to the central MySQL server for character info.
However using a flat file system is killer for loading and saving per zone stats.
You have been tons of help, thank you.
And with the rate you are moving along, I doubt you're a dreamer. :)
I'll post my changes for MySQL and the Network Join as soon as I know it's clean.
05/05/2005 (8:20 pm)
Thanks for getting back to me, yeah I deleted the .dso files.The results you got is what I expected but don't get, my zone still loads and that's just not possible.
Guess it's roll back time, thanks for verifying, glad I didn't miss the plot.
Ari Rule
*edit:
OK... Found the problem.
The shortcut I was using to the dedicated server was pathed to an old rev.
I use several SDK folders for the dev process and when I copy or move stuff the shortcut paths are not updated.
3 days of stress because of a bad path in a shortcut...
Tempted to get "Newb" tatooed on me so I don't forget the simple stuff.
Or K.I.S.S. (Keep it simple stupid).
Imagine changing code, deleting the .dso files, and nothing changed!
I thought I was losing my mind.
Now the login is working awesome with MySQL and I integrated your AccountGUI into the Join menu so that master servers can be used (think server clusters of zones and 1 login server connected to a master server per cluster).
Torque allows multiple master servers for failover.
Now, right before serverCmdLoadZone gets called I need to have it ask the SQL database which zone the character is in and tell the client to connect to that zone.
There is a tutorial here on GG that explains forcing a client to connect to another server.
This will result in 2 different server modes, login server(connects to a master server) and zone server.
This is why I have to use MySQL on a dedicated and not SQLite.
All of my servers except the masters have to communicate to the central MySQL server for character info.
However using a flat file system is killer for loading and saving per zone stats.
You have been tons of help, thank you.
And with the rate you are moving along, I doubt you're a dreamer. :)
I'll post my changes for MySQL and the Network Join as soon as I know it's clean.
#29
Correct me if I'm wrong, but the following lines are not needed.
The basic structure looks very similar to the RTS Lobby, I love it.
Also the line in serverCmdLoadZone
should be moved into GameConnection::onConnect where the serverCmdValidateAccount command was, or else the server's player count gets botched when users fail to logon and then disconnect.
For those using master servers this will result in the client filtering out servers because of player counts like -1, -2, etc.
This is a crummy fix as you cannot tell if the players are actually playing or logging in, but at least the count doesn't wind up as a negative number.
Ari Rule
05/06/2005 (10:33 pm)
In the tutorial in clientConnection.csfunction GameConnection::onConnect( %client, %name, %password)
{
// Send down the connection error info, the client is
// responsible for displaying this message if a connection
// error occured.
messageClient(%client,'MsgConnectionError',"",$Pref::Server::ConnectionError);
//Validate the client
echo("Attempting to Authenticate Client " @ %client);
serverCmdValidateAccount(%client,%name,%password);
}Correct me if I'm wrong, but the following lines are not needed.
//Validate the client
echo("Attempting to Authenticate Client " @ %client);
serverCmdValidateAccount(%client,%name,%password);The basic structure looks very similar to the RTS Lobby, I love it.
Also the line in serverCmdLoadZone
$Server::PlayerCount++;
should be moved into GameConnection::onConnect where the serverCmdValidateAccount command was, or else the server's player count gets botched when users fail to logon and then disconnect.
For those using master servers this will result in the client filtering out servers because of player counts like -1, -2, etc.
This is a crummy fix as you cannot tell if the players are actually playing or logging in, but at least the count doesn't wind up as a negative number.
Ari Rule
#30
Anyways thanks for the props, and tips, fixes and I'm glad to hear my code was able to help ya!
*Update* Hey cool, you fixed a bug I've been trying to track for quite awhile now! That's awesome thanks.
05/07/2005 (6:48 am)
@Ari, yeah you're probably right... When I first set out to get the Logins working properly I put serverCmdValidateAccount, in alot of places trying to get it to break the flow at the right time. When I finally had a winner, I just kinda left things in place, and moved on to my next thing... So yeah there is probably quite a bit of artifact code laying around, that I will someday need to clean up...Anyways thanks for the props, and tips, fixes and I'm glad to hear my code was able to help ya!
*Update* Hey cool, you fixed a bug I've been trying to track for quite awhile now! That's awesome thanks.
#31
Guess I'll try again. :)
I wanted to use Join Server GUI instead of Dreamer's Account GUI.
The Join Server GUI allows querying of master servers and local networks.
This was very important to me because I am using cluster servers that connect to various zone servers rather than a single stand alone server.
I won't go into how to create a cluster server right now, but it's not too difficult.
It seems that Dreamer and many other people are creating online RPGs where DMs/GMs host their own servers for players to connect to.
Using a TGE Master Server would seem essential so that players could see a list of games/worlds being hosted by various DMs/GMs and choose which one they wanted to join.
I haven't been getting alot of sleep lately, so I'm going to give it my best shot.
Please, please give me feedback if I make any mistakes.
I have this working in my project, but my system may be to complex to post right now, like I said sleep is a factor.
This code is a mod for the above tutorial by Dreamer.
Let's get started.
Start by making the changes I posted above.
In SDK\example\starter.fps\client\scripts\serverConnection.cs replace the function GameConnection::onConnectionAccepted with the one below.
In SDK\example\starter.fps\client\scripts\client.cs replace function clientCmdValidateAccountResult with the one below.
Next, in SDK\example\starter.fps\client\ui\joinServerGui.gui just above the line:
Personally, I added a few other buttons and features but this should be a good start.
Basically what we have changed here is the intial logon process.
Instead of using mygame.exe -connect 127.0.0.1 click the Join button from the main menu.
Refresh the list of servers (Master or LAN).
Then select the server you wish to connect to and click Connect.
If your authentication credentials are correct the zone will begin to load.
There are some issues that I will try to cover soon, such as not allowing clients to send the server any commands until authentication is complete, not using the $Pref::Player::Password to store the password, and adding a CRC to the authentication.
And maybe, just maybe, after I get some sleep, I'll try and explain how solve the plain text password issue.
Please, please make comments on this.
I would like to see Dreamer's tutorial series become the nexus of MMORPG subjects on GG since it's such a popular subject.
Ari Rule
"Copyright it, patent it, or be part of something much bigger."
*Edit: I think in function clientCmdValidateAccountResult there may be some more disconnects needed during null responses and errors.
I'll update when I can, unless some one else is kind enough to do it for me. :)
05/29/2005 (7:01 pm)
I guess my post never went through awhile back.Guess I'll try again. :)
I wanted to use Join Server GUI instead of Dreamer's Account GUI.
The Join Server GUI allows querying of master servers and local networks.
This was very important to me because I am using cluster servers that connect to various zone servers rather than a single stand alone server.
I won't go into how to create a cluster server right now, but it's not too difficult.
It seems that Dreamer and many other people are creating online RPGs where DMs/GMs host their own servers for players to connect to.
Using a TGE Master Server would seem essential so that players could see a list of games/worlds being hosted by various DMs/GMs and choose which one they wanted to join.
I haven't been getting alot of sleep lately, so I'm going to give it my best shot.
Please, please give me feedback if I make any mistakes.
I have this working in my project, but my system may be to complex to post right now, like I said sleep is a factor.
This code is a mod for the above tutorial by Dreamer.
Let's get started.
Start by making the changes I posted above.
In SDK\example\starter.fps\client\scripts\serverConnection.cs replace the function GameConnection::onConnectionAccepted with the one below.
function GameConnection::onConnectionAccepted(%this)
{
// Called on the new connection object after connect() succeeds.
LagIcon.setVisible(false);
LoginAccount(); //BrokeAss MMO (Please, no jokes.)
}In SDK\example\starter.fps\client\scripts\client.cs replace function clientCmdValidateAccountResult with the one below.
//This function handles validate account responses from the server
function clientCmdValidateAccountResult(%Result){
%errmsg = "";
$Result = detag(%Result);
//The following could be done much more cleanly if switch and case statements functioned properly
if(strpos(%Result,"TRUE") != -1){
//Canvas.PopDialog(AccountGUI); //BrokeAss MMO
//MessageBoxOK("Success!","You have now successfully logged in!); //BrokeAss MMO
}else{
if(strpos(%Result,"NEW") != -1 ){
if($Pref::Player::Name !$="" && $Pref::Player::Password !$= ""){
MessageBoxYesNo( "New Account?","Do you wish to create a new account?","Canvas.PushDialog(ChooseRaceGUI);", "");
}else{
MessageBoxOK("Error!","Your User Name and/or Password are empty");
}
}else{
if(strpos(%Result,"WRONGPASSWORD") != -1){
MessageBoxOK("Error!","Your Password is incorrect");
// Delete the connection if it's still there. //BrokeAss MMO
if (isObject(ServerConnection)) //BrokeAss MMO
ServerConnection.delete(); //BrokeAss MMO
}else{
if(%Result !$= ""){
%errmsg = "The server returned an invalid response\nThe response was " @ %Result;
}else{
%errmsg = "The server returned a NULL response!";
}
MessageBoxOK("Error!",%errmsg);
}
if(strpos(%Result,"NULL") != -1){
MessageBoxOK("Error!","Your User Name and/or Password are empty");
}
}
}
}Next, in SDK\example\starter.fps\client\ui\joinServerGui.gui just above the line:
new GuiButtonCtrl(JS_refreshServer) {Addnew GuiTextCtrl() {
profile = "GuiTextProfile";
horizSizing = "right";
vertSizing = "top";
position = "13 34";
extent = "53 18";
minExtent = "8 8";
visible = "1";
lockMouse = "0";
text = "Password:";
maxLength = "255";
helpTag = "0";
};
new GuiTextEditCtrl(passentry) {
profile = "GuiTextEditProfile";
horizSizing = "right";
vertSizing = "top";
position = "80 34";
extent = "134 18";
minExtent = "8 8";
visible = "1";
variable = "$Pref::Player::Password";
lockMouse = "0";
maxLength = "255";
historySize = "1";
password = "1";
tabComplete = "0";
sinkAllKeyEvents = "0";
helpTag = "0";
};Personally, I added a few other buttons and features but this should be a good start.
Basically what we have changed here is the intial logon process.
Instead of using mygame.exe -connect 127.0.0.1 click the Join button from the main menu.
Refresh the list of servers (Master or LAN).
Then select the server you wish to connect to and click Connect.
If your authentication credentials are correct the zone will begin to load.
There are some issues that I will try to cover soon, such as not allowing clients to send the server any commands until authentication is complete, not using the $Pref::Player::Password to store the password, and adding a CRC to the authentication.
And maybe, just maybe, after I get some sleep, I'll try and explain how solve the plain text password issue.
Please, please make comments on this.
I would like to see Dreamer's tutorial series become the nexus of MMORPG subjects on GG since it's such a popular subject.
Ari Rule
"Copyright it, patent it, or be part of something much bigger."
*Edit: I think in function clientCmdValidateAccountResult there may be some more disconnects needed during null responses and errors.
I'll update when I can, unless some one else is kind enough to do it for me. :)
#32
05/29/2005 (7:35 pm)
Hey kewl Ari, take it and run, I'm making this mod a part of the advanced series, if you have anymore lemme know.
#33
Btw, if you want to email me a non-disclosure agreement, I'll sign it and FedEX it to you.
I want to see what you've got so far. :)
To get the Join Server GUI to auto refresh the list of servers from a master when it is opened:
In SDK\example\starter.fps\client\ui\joinServerGui.gui replace the function JoinServerGui::onWake with the one below.
Ari Rule
"If a card game goes down in the park, I want in!" Christopher Walken, King of New York.
05/29/2005 (7:45 pm)
I have tons, I'll be adding as often as I can.Btw, if you want to email me a non-disclosure agreement, I'll sign it and FedEX it to you.
I want to see what you've got so far. :)
To get the Join Server GUI to auto refresh the list of servers from a master when it is opened:
In SDK\example\starter.fps\client\ui\joinServerGui.gui replace the function JoinServerGui::onWake with the one below.
function JoinServerGui::onWake()
{
// Double check the status. Tried setting this the control
// inactive to start with, but that didn't seem to work.
JS_joinServer.setActive(JS_serverList.rowCount() > 0);
JoinServerGui.query(); //BrokeAss MMO
}Ari Rule
"If a card game goes down in the park, I want in!" Christopher Walken, King of New York.
#34
your contributions are so VERY usefull and helpfull, please do not stop writing/publishing them, it helps a LOT for beginners like me !
Cheers !
12/12/2006 (5:56 am)
BrokenAss, Dreamer,your contributions are so VERY usefull and helpfull, please do not stop writing/publishing them, it helps a LOT for beginners like me !
Cheers !
#35
Whenever a client is connecting supplying wrong accountdata or getting disconnected the before the mission is started, the $Server::PlayerCount variable gets negative.
This is causing the client to hide the dedicated server the nexttime, cause the engine is hiding servers with playercount in the negatives (its matching <0 condition).
A dirty fix for that is a catch-function like this:
In common/server/scripts/clientConnection.cs
find the function
function GameConnection::onDrop(%client, %reason)
There you see the line
$Server::PlayerCount--;
This is decreasing the playercount, but we will put it under condition from now on.
Delete this line and add this instead:
if ($Server::PlayerCount>0)
{
$Server::PlayerCount--;
}
So the whole function looks like this:
function GameConnection::onDrop(%client, %reason)
{
%client.onClientLeaveGame();
removeFromServerGuidList( %client.guid );
messageAllExcept(%client, -1, 'MsgClientDrop', '\c1%1 has left the game.', %client.name, %client);
removeTaggedString(%client.name);
echo("CDROP: " @ %client @ " " @ %client.getAddress());
if ($Server::PlayerCount>0)
{
$Server::PlayerCount--;
}
// Reset the server if everyone has left the game
if( $Server::PlayerCount == 0 && $Server::Dedicated)
schedule(0, 0, "resetServerDefaults");
}
This change avoids the server::playercount getting decreased when it reaches null (zero).
If some1 has better fix then this, feel free... :)
12/14/2006 (8:36 am)
Just did this tutorial and stumbled over the "PlayerCount"-problem.Whenever a client is connecting supplying wrong accountdata or getting disconnected the before the mission is started, the $Server::PlayerCount variable gets negative.
This is causing the client to hide the dedicated server the nexttime, cause the engine is hiding servers with playercount in the negatives (its matching <0 condition).
A dirty fix for that is a catch-function like this:
In common/server/scripts/clientConnection.cs
find the function
function GameConnection::onDrop(%client, %reason)
There you see the line
$Server::PlayerCount--;
This is decreasing the playercount, but we will put it under condition from now on.
Delete this line and add this instead:
if ($Server::PlayerCount>0)
{
$Server::PlayerCount--;
}
So the whole function looks like this:
function GameConnection::onDrop(%client, %reason)
{
%client.onClientLeaveGame();
removeFromServerGuidList( %client.guid );
messageAllExcept(%client, -1, 'MsgClientDrop', '\c1%1 has left the game.', %client.name, %client);
removeTaggedString(%client.name);
echo("CDROP: " @ %client @ " " @ %client.getAddress());
if ($Server::PlayerCount>0)
{
$Server::PlayerCount--;
}
// Reset the server if everyone has left the game
if( $Server::PlayerCount == 0 && $Server::Dedicated)
schedule(0, 0, "resetServerDefaults");
}
This change avoids the server::playercount getting decreased when it reaches null (zero).
If some1 has better fix then this, feel free... :)
Torque Owner Wayne Eversole
Default Studio Name
Thanks