User Login In MMO Application

Oayhan
Oayhan ✭✭
edited August 2011 in Photon Server
I have two database tables, one with the user login information and the other with the coordinates and other specific information of character associated with user accounts. I will require a user to login before viewing their characters. Which class on mmo server code should I look in for this?

As far as I can understand the mmo code, it should be in Game class and probably in Initialize method or even before Initialize method. But I still wanted a confirmation before I start any pointless work.

Second question regarding the same issue is how to handle retrieval of coordinates and information. I've thought of using IDs from character table as actorID so I can write to same row when the user quits but I can't find it in MmoActor class.

If you have any examples on login functions that you can provide, or even a road map on what to do, it will be greatly appreciated. Thanks in advance.
«1

Comments

  • The mmo demo does not cover a login process.
    For authentication I would do the following:

    Server side:
    1) Create a new Login operation class
    2) Create a new IOperationHandler implementation for unauthorized peers that handles the Login operation
    3) Assign this new operation handler as current operation handler in the MmoPeer constructor: this.SetCurrentOperationHandler(unauthorizedOperationHandler);
    4) After successful authentication switch the peer's operation handler to MmoPeer: peer.SetCurrentOperationHandler(peer);

    Client side:
    1) Create a new IGameLogicStrategy implementation for the new state that exists between connected and worldEntered: Authorized.
    2) At WaitingForConnect.OnPeerStatusCallback do not call enter world, instead send the login operation here.
    3) At connected.OnOperationReturn handle the login response, switch to state authorized and call enter world.
    4) move the original operation response handling from connected.OnOperationReturn to the authorized class
    5) At WorldEntered.OnEventReceive event code WorldExited do not switch to state connected, switch to state authorized instead.

    Regarding your second question: You can add an ID to the MmoActor if you want. Also, the actor's representation is a MmoItem which has an Id - so you could also use row ids for the actor avatar items.

    Hope that helps.
  • Oayhan
    Oayhan ✭✭
    Hi,

    I've been busy with another project for some time, but I started working on this again. I'm trying to mimic implementation of Move operation to my Login operation and implementation of IOperationHandler from MmoPeer to UnauthorizedPeerHandler but I have some questions on these.

    1) In UnauthorizedPeerHandler class, do I have to add every operation (other than Login as I only want to use it) as an attribute to InvalidOperation method? I've recently learnt how attributes work, so I'm not very experienced in using them and I don't have any idea how Operations work server side.

    2) Like I said I can't understand how Operation class work exactly but as far as i can see, all the classes that implement Operation only has getter and setter methods. So I created a username and password field with get and set functions but where should I call my database functions?
  • 1) The OperationAttribute is more advanced, you only need it you if you set the conditional compilation symbol "OperationDispatcher" (which will then use the OperationDispatcher class that dispatches by OperationAttribute).

    2) You dispatch your operation at "OnOperationRequest". Add a "case" for your operation code and call your method there. Inside this method you create your operation to map the OperationRequest.Params Dictionary to class properties. The property values then contain the query parameters that you need, like the username. This is where you call the DB.
  • Oayhan
    Oayhan ✭✭
    I think I've managed to get the handler changing part in server right but I have some questions about it. As you can see on the second function, I check my database using linq and then if succesful, I first change the handler and send back a response. Is this the correct behaviour? Does the order of these two operations (changing handler and sending back response) matter?
    private OperationResponse OperationLogin(Peer peer, OperationRequest operationRequest)
            {
                var operation = new Login(operationRequest);
               
                if(!operation.IsValid)
                {
                    return new OperationResponse(operationRequest, (int)ErrorCode.InvalidOperationParameter, operation.GetErrorMessage());
                }
    
                operation.LoginUsername = (string)operationRequest.Params[(short)ParameterCode.LoginUsername];
                operation.LoginPassword = (string)operationRequest.Params[(short)ParameterCode.LoginPassword];
    
                return UserLoginOperation(peer, operation);
            }
    
            private OperationResponse UserLoginOperation(Peer peer, Login operation)
            {
                TablesDataContext db = new TablesDataContext();
                List<User> users = (from k in db.Users
                                              where
                                                  k.USERNAME.Equals(operation.LoginUsername) &&
                                                  k.PASSWORD.Equals(operation.LoginPassword)
                                              select k).ToList();
    
                if (users.Count == 0)
                {
                    return operation.GetOperationResponse((int)ErrorCode.Ok, "Wrong password or username");
                }
                else
                {
                    peer.SetCurrentOperationHandler(new AuthorizedPeerHandler());
                    OperationResponse response = operation.GetOperationResponse(MethodReturnValue.Ok);
                    peer.PublishOperationResponse(response);
                    return null;
                }
            }
    
  • Looks good. The order does not matter since all operation requests are executed one after the other. So the next operation request will always use the new operation handler.
  • Oayhan
    Oayhan ✭✭
    edit:deleted question, as it was a really bad mistake I made at the start of code :P
  • Oayhan
    Oayhan ✭✭
    Thanks to your help I got a working login process using your island mmo but I have one more question about it. I want to use several scripts throughout scene associated with login proccess. For example I want to use a login GUI script that has a reference of MmoEngine in it but the MmoEngine and its StateStrategy etc.. doesn't have a reference to this script(as it would mean a lot of code changing passing reference through MmoEngine, Game and StateStrategy classes). How can I fetch the OperationResult of my Login operation from this GUI script? Is the only way passing the GUI script's reference through all these process? Or is it possible to have two scripts listen to same peer? Thanks in advance.
  • you can only have one peer listener.
    As a short cut you could write an IPhotonPeerListener wrapper that forwards the login response to your login GUI.
    The way the mmo demo does it for other callbacks is to have the strategy call Game.OnLoginResponse which then calls the IGameListener.OnLoginResponse .. which is the MMOEngine class. This class could then forward it with Transform.SendMessage.
  • Oayhan
    Oayhan ✭✭
    Hi again, I'm using different attributes in my character that are kept in server side. I want to pass these attributes to client when it is done with authorization. Currently all of my operation responses were in the form of:

    return new OperationResponse(request,(int)ErrorCode.CharacterNotFound,"CharacterNotFound");

    I know from some of the photon applications, that the datas from operation responses are grabbed as hashtables, but how can I send a hashtable in an operation response?
  • there are certain overloads for OperationResponse which you can make use of.
    Usually you gonna create a dictionary on the server
    Dictionary<string, string> data = new Dictionary<string, string>();
    data.Add("username", username.ToString());
    data.Add("playerLevel", playerLevel.ToString());
    
    response = new OperationResponse(request, errorCode, errorMessage, channel, reliability, returnValues, encrypt);
    

    not 100% sure about the order of the params at the moment but intellisense should help you outa there (I am on my mobile atm^^)
    And you would change returnValues to data in this case.
    On the client you will find the data dictionary as hash where you can grab it. The hash is called returnValues and its containing a hashtable with the keys you've added.
  • Oayhan
    Oayhan ✭✭
    I'm still trying to develop this, I've completed character creation etc.. But I'm struggling on getting the interest areas, character properties etc.. I used different handlers through the process and change the handler to the default of MmoDemo before I load the actual Island level. On the Island level I make a call to server (which I copied from Islands example):
                 InterestArea camera;
                TryGetCamera(0, out camera);
                camera.ResetViewDistance();
                Avatar.SetInterestAreaAttached(true);
                Avatar.EnterWorld();
                SetConnected();
    

    For some reason I get these two error messages:

    unexpected operation error InvalidOperation from operation SetViewDistance in state Connected message= InvalidOperation: SetViewDistance

    unexpected operation error InvalidOperation from operation SetProperties in state Connected message= InvalidOperation: SetProperties

    But then it says:
    entered world Unity3d-Island

    So I assume the EnterWorld operation is recieved by the handler but SetViewDistance and SetProperties aren't handled? What could cause this? Thanks in advance.
  • yep looks like they aren't handled - a possible cause could be that you may or may not change the state before sending the operation to the server? Thats important when working with states. Other than that make sure you've registered a handler in your StateMaster depending on what kinda implementation do you use.
  • Oayhan
    Oayhan ✭✭
    I've checked again and I do change the handler before calling EnterWorld, otherwise I would recieve and InvalidOperation for that wouldn't I?

    After checking the EnterWorld operation on server, I've noticed that it changes the handler again to MmoActor handler and that is where the two Invalid Operations are handled but in the MmoDemo example

    this call
    camera.ResetViewDistance();
    

    is above this call
    Avatar.EnterWorld();
    

    That doesn't make sense to me but in the demo it actually works. Does anyone have an explanation for this? :)
  • I havn't taken a deep look into the mmo demo as i am just using small portions of it. And if you have done modifications to that it would be hard to explain it, so i can only guess. Have you checked the Serverlogs? Probably you are changing the operationhandler on the client, but not on the server or vice-versa?
  • Oayhan
    Oayhan ✭✭
    I did check both the server and client, the required handlers are changed as expected. My main problem here is the difficulty of understanding MmoDemo I guess.So let me ask a question; what is the required call for a client to pass to EnteredWorld status from Connected status? At first I though it was:
               InterestArea camera;
               TryGetCamera(0, out camera);
               camera.ResetViewDistance();
               Avatar.SetInterestAreaAttached(true);
               Avatar.EnterWorld();
               SetConnected();
    

    from what I can understand this checks for a camera in scene, uses it to initialize InterestArea called "camera". After that it first calls operation SetViewDistance by using camera.ResetViewDistance();. And after that it finally calls Enter World operation.

    The part that I don't understand is, SetViewDistance is handled by MmoActor handler, but the default handler of demo is MmoPeer and the handler change is made in the EnterWorld operation. In my opinion there are two options here:

    Either this isn't the script to initiate the world and something else is called before this, or I fail to see a different call that changes the handler before these call.

    I know that my problem is a bit complex after all these changes made to the code but thanks anyway for trying to help.
  • Oayhan
    Oayhan ✭✭
    I tried with two clients now and it does enter the world as it shows 2 online but there is still a problem with interest areas and I can't see the other client.
  • unexpected operation error InvalidOperation from operation SetViewDistance in state Connected message= InvalidOperation: SetViewDistance

    unexpected operation error InvalidOperation from operation SetProperties in state Connected message= InvalidOperation: SetProperties

    The error says that your client is still in state Connected. The operations above require state WorldEntered.
    State connected waits for operation response of EnterWorld before transitioning to enter world.
    So you actually send operation SetViewDistance and SetProperties before you are entering the world.
  • Oayhan
    Oayhan ✭✭
    I think the problem is on server side because it returns InvalidOperation error, if the problem was on statestrategy the error would be something like unexpected operation response, right? Anyway I'm now trying to buildup from MmoDemo again but this time i'll add smaller pieces to code and see where the problem is.
  • If the client is still in state connected the server will not have the MmoActor as operation handler, so it's logical that the server returns errors for operations that only MmoActor can handle.
  • Oayhan
    Oayhan ✭✭
    I came to that conclusion too but what I can't understand is these lines of code
    InterestArea camera;
    TryGetCamera(0, out camera);
    camera.ResetViewDistance();
    Avatar.SetInterestAreaAttached(true);
    SetConnected();
    Avatar.EnterWorld();
    

    ResetViewDistance() contains an operation SetViewDistance to server and the EnterWorld is called after setting the state strategy to connected(these are in onpeerstatuscallback of waitingforconnect). From what I can understand here the state strategy will be changed to WorldEntered after an operation response is recieved, so there shouldn't be any way to handle SetViewDistance. But for some reason it works in your demo and not in mine :)

    I feel that if I can understand the logic in this piece of code, I can solve my problem. I'm sorry for telling the same thing over and over but I'm stuck on this part for a few days and can't get any progress :/
  • Ok, so this is in WaitForConnect:
    public void OnPeerStatusCallback(Game game, StatusCode returnCode)
            {
                switch (returnCode)
                {
                    case StatusCode.Connect:
                        {
                            InterestArea camera;
                            game.TryGetCamera(0, out camera);
                            camera.ResetViewDistance();
                            game.Avatar.SetInterestAreaAttached(true);
                            game.SetConnected();
                            game.Avatar.EnterWorld();
                            break;
                        }
    

    if you follow SetViewDistance you will eventually find class Operations:
    public static void SetViewDistance(Game game, float[] viewDistanceEnter, float[] viewDistanceExit)
            {
                var data = new Hashtable
                    {
                        { (byte)ParameterCode.ViewDistanceEnter, viewDistanceEnter }, { (byte)ParameterCode.ViewDistanceExit, viewDistanceExit } 
                    };
                game.SendOperation(OperationCode.SetViewDistance, data, true, Settings.ItemChannel);
            }
    

    but don't stop here!
    What does game.SendOperation do?
     this.stateStrategy.SendOperation(this, operationCode, parameter, sendReliable, channelId);
    

    It calls the strategy to send.
    This is the implementation of WaitingForConnect:
     public void SendOperation(Game game, OperationCode operationCode, Hashtable parameter, bool sendReliable, byte channelId)
            {
            }
    

    So the lines above SetConnected are supposed to init things locally, not on the server.

    I assume you moved the EnterWorld code to state connected since you have a new state "loggedIn" before that? Make sure that at this state you allow the EnterWorld operation only.
  • Oayhan
    Oayhan ✭✭
    Ah, you're right, I've looked at all the wrong places for a week now :) I've tried it now and it works properly when the operations are not sent. Thanks for the help, really appreciated.
  • Glad it works now :) Thanks.
  • Oayhan
    Oayhan ✭✭
    Hi again, I'm here with another question :) I'm trying to use different attributes on players. I've modified MmoItem class and added my attributes on its constructor. I've traced the steps for adding an avatar item in EnterWorld operation. It calls
    actor.AddItem(avatar);
    
    which is an MmoActor instance,

    inside MmoActor class
    base.AddItem(item)
    
    is called. I can't see the base class (i think Actor class is included in a dll), so how can I override this method without writing everything all over?
  • I am not 100% sure if this works in C# but usually you can override the base method add the things you want and then call base to proceed.
    So basically:
    public override baseClassMethodName
    {
        // do your stuff here
        ......
    
       // call base
       base.baseClassMethodName();
    }
    
    
  • sebako wrote:
    I am not 100% sure if this works in C# but usually you can override the base method add the things you want and then call base to proceed.
    So basically:
    public override baseClassMethodName
    {
        // do your stuff here
        ......
    
       // call base
       base.baseClassMethodName();
    }
    
    
    I think, the better approach would be:
    public override baseClassMethodName
    {
       // call base
       base.baseClassMethodName();
        ......
    
        // do your stuff here
    }
    

    The reason therefor is: you can react in your code to changes, made by the base class' implementation, but the base-class of your class should not need to know anything about your class.
  • Oayhan
    Oayhan ✭✭
    My question was mostly on how it's handled currently then how to add on to it. I don't even know what base.AddItem does because I can't see Actor class. Suppose I have an extra int that I'd like to pass as an attribute of that actor. What is the best way to do it?
  • Actor.AddItem adds the item to a dictionary.
    Actor.AddItem(MmoItem) just checks for a item.Owner null reference.
    You can change the method to AddItem(MmoItem, int) if you like and just update the usages.
    Just make sure you find all the references, because otherwise AddItem(Item) is called which throws an ArgumentException.
  • Hi, I'm still having some trouble in sending attributes. I can't find where the event is for attributes is sent when a player is spawned/seen. I've found on the client side it's recieved as EventCode.ItemProperties or EventCode.ItemPropertiesSet. I've tried adding an extra parameter to a few functions that sends these event but it didn't work out. Can you point me to where I should look? Thanks in advance.
  • Initial properties are set in MyItem.EnterWorld.