void OnServerCharacterDelete(NetworkMessage netMsg)
    {
        print("OnServerCharacterDelete " + netMsg.conn);
        var msg = netMsg.ReadMessage <CharacterDeleteMsg>();

        // can only delete while in lobby (aka after handshake and not ingame)
        if (lobby.ContainsKey(netMsg.conn))
        {
            var account = lobby[netMsg.conn];
            var chars   = Database.CharactersForAccount(account);

            // validate index
            if (0 <= msg.index && msg.index < chars.Count)
            {
                // delete the character
                print("delete character: " + msg.index);
                Database.CharacterDelete(account, chars[msg.index]);

                // send the new character list to client
                var msgchars = new CharactersAvailableMsg();
                msgchars.characters = Database.CharactersForAccount(account).ToArray();
                netMsg.conn.Send(CharactersAvailableMsg.MsgId, msgchars);
            }
            else
            {
                print("invalid character index: " + account + " " + msg.index);
                ClientSendPopup(netMsg.conn, "invalid character index", false);
            }
        }
        else
        {
            print("CharacterDelete: not in lobby: " + netMsg.conn);
            ClientSendPopup(netMsg.conn, "CharacterDelete: not in lobby", true);
        }
    }
Example #2
0
    void OnServerLogin(NetworkMessage netMsg)
    {
        print("OnServerLogin " + netMsg.conn);
        var msg = netMsg.ReadMessage <LoginMsg>();

        // not too long?
        if (msg.id.Length <= accountMaxLength)
        {
            // only contains letters, number and underscore and not empty (+)?
            // (important for database safety etc.)
            if (Regex.IsMatch(msg.id, @"^[a-zA-Z0-9_]+$"))
            {
                // validate account info
                if (Database.IsValidAccount(msg.id, msg.pw))
                {
                    // not in lobby and not in world yet?
                    if (!AccountLoggedIn(msg.id))
                    {
                        print("login successful: " + msg.id);

                        // add to logged in accounts
                        lobby[netMsg.conn] = msg.id;

                        // send available characters to client
                        var chars    = Database.CharactersForAccount(msg.id);
                        var msgchars = new CharactersAvailableMsg {
                            characterNames   = chars.Keys.ToArray(),
                            characterClasses = chars.Values.ToArray()
                        };
                        netMsg.conn.Send(CharactersAvailableMsg.MsgId, msgchars);
                    }
                    else
                    {
                        print("account already logged in: " + msg.id);
                        ClientSendPopup(netMsg.conn, "already logged in", true);

                        // note: we should disconnect the client here, but we can't as
                        // long as unity has no "SendAllAndThenDisconnect" function,
                        // because then the error message would never be sent.
                        //netMsg.conn.Disconnect();
                    }
                }
                else
                {
                    print("invalid account or password for: " + msg.id);
                    ClientSendPopup(netMsg.conn, "invalid account", true);
                }
            }
            else
            {
                print("account invalid: " + msg.id);
                ClientSendPopup(netMsg.conn, "forbidden account name", true);
            }
        }
        else
        {
            print("account too long: " + msg.id);
            ClientSendPopup(netMsg.conn, "account too long", true);
        }
    }
Example #3
0
    void OnClientCharactersAvailable(NetworkMessage netMsg)
    {
        charactersAvailableMsg = netMsg.ReadMessage <CharactersAvailableMsg>();
        print("characters available:" + charactersAvailableMsg.characters.Length);

        // set state
        state = NetworkState.Lobby;

        // clear previous previews in any case
        ClearPreviews();

        // load previews for 3D character selection
        for (int i = 0; i < charactersAvailableMsg.characters.Length; ++i)
        {
            CharactersAvailableMsg.CharacterPreview character = charactersAvailableMsg.characters[i];

            // find the prefab for that class
            Player prefab = GetPlayerClasses().Find(p => p.name == character.className);
            if (prefab != null)
            {
                LoadPreview(prefab.gameObject, selectionLocations[i], i, character);
            }
            else
            {
                Debug.LogWarning("Character Selection: no prefab found for class " + character.className);
            }
        }

        // setup camera
        Camera.main.transform.position = selectionCameraLocation.position;
        Camera.main.transform.rotation = selectionCameraLocation.rotation;

        // addon system hooks
        Utils.InvokeMany(typeof(NetworkManagerMMO), this, "OnClientCharactersAvailable_", charactersAvailableMsg);
    }
Example #4
0
    void OnClientCharactersAvailable(NetworkConnection conn, CharactersAvailableMsg message)
    {
        charactersAvailableMsg = message;
        print("存档中可用角色:" + charactersAvailableMsg.characters.Length);

        // set state
        state = NetworkState.Lobby;

        // clear previous previews in any case
        ClearPreviews();

        // load previews for 3D character selection
        for (int i = 0; i < charactersAvailableMsg.characters.Length; ++i)
        {
            CharactersAvailableMsg.CharacterPreview character = charactersAvailableMsg.characters[i];

            // find the prefab for that class
            Player prefab = GetPlayerClasses().Find(p => p.name == character.className);
            if (prefab != null)
            {
                LoadPreview(prefab.gameObject, selectionLocations[i], i, character);
            }
            else
            {
                Debug.LogWarning("没有找到对应类名的预制体 " + character.className);
            }
        }

        // setup camera
        Camera.main.transform.position = selectionCameraLocation.position;
        Camera.main.transform.rotation = selectionCameraLocation.rotation;
    }
Example #5
0
    // handshake: character selection //////////////////////////////////////////
    void OnClientCharactersAvailable(NetworkMessage netMsg)
    {
        charactersAvailableMsg = netMsg.ReadMessage<CharactersAvailableMsg>();
        print("characters available:" + charactersAvailableMsg.characters.Length);

        // addon system hooks
        Utils.InvokeMany(typeof(NetworkManagerMMO), this, "OnClientCharactersAvailable_", charactersAvailableMsg);
    }
Example #6
0
    void OnServerLogin(NetworkMessage netMsg)
    {
        print("OnServerLogin " + netMsg.conn);
        LoginMsg message = netMsg.ReadMessage <LoginMsg>();

        // correct version?
        if (message.version == Application.version)
        {
            // allowed account name?
            if (IsAllowedAccountName(message.account))
            {
                // validate account info
                if (Database.IsValidAccount(message.account, message.password))
                {
                    // not in lobby and not in world yet?
                    if (!AccountLoggedIn(message.account))
                    {
                        print("login successful: " + message.account);

                        // add to logged in accounts
                        lobby[netMsg.conn] = message.account;

                        // send necessary data to client
                        CharactersAvailableMsg reply = MakeCharactersAvailableMessage(message.account);
                        netMsg.conn.Send(CharactersAvailableMsg.MsgId, reply);

                        // addon system hooks
                        Utils.InvokeMany(typeof(NetworkManagerMMO), this, "OnServerLogin_", message);
                    }
                    else
                    {
                        print("account already logged in: " + message.account);
                        ClientSendPopup(netMsg.conn, "already logged in", true);

                        // note: we should disconnect the client here, but we can't as
                        // long as unity has no "SendAllAndThenDisconnect" function,
                        // because then the error message would never be sent.
                        //netMsg.conn.Disconnect();
                    }
                }
                else
                {
                    print("invalid account or password for: " + message.account);
                    ClientSendPopup(netMsg.conn, "invalid account", true);
                }
            }
            else
            {
                print("account name not allowed: " + message.account);
                ClientSendPopup(netMsg.conn, "account name not allowed", true);
            }
        }
        else
        {
            print("version mismatch: " + message.account + " expected:" + Application.version + " received: " + message.version);
            ClientSendPopup(netMsg.conn, "outdated version", true);
        }
    }
    CharactersAvailableMsg MakeCharactersAvailableMessage(string account)
    {
        List <string> names      = Database.singleton.CharactersForAccount(account);
        List <Player> characters = new List <Player>();

        foreach (string character in names)
        {
            GameObject player = Database.singleton.CharacterLoad(character, playerClasses, true);
            characters.Add(player.GetComponent <Player>());
        }
        CharactersAvailableMsg message = new CharactersAvailableMsg();

        message.Load(characters);
        characters.ForEach(player => Destroy(player.gameObject));
        return(message);
    }
    private void OnClientCharactersAvailable_UCE_NetworkZones(CharactersAvailableMsg message)
    {
        int index = message.characters.ToList().FindIndex(c => c.name == UCE_NetworkZone.autoSelectCharacter);

        if (index != -1)
        {
            // send character select message
            print("[Zones]: autoselect " + UCE_NetworkZone.autoSelectCharacter + "(" + index + ")");

            byte[] extra = BitConverter.GetBytes(index);
            ClientScene.AddPlayer(NetworkClient.connection, extra);

            // clear auto select
            UCE_NetworkZone.autoSelectCharacter = "";
        }
    }
Example #9
0
    // helper function to make a CharactersAvailableMsg from all characters in
    // an account
    CharactersAvailableMsg MakeCharactersAvailableMessage(string account)
    {
        // load from database
        List<Player> characters = Database.CharactersForAccount(account)
                                    .Select(character => Database.CharacterLoad(character, GetPlayerClasses()))
                                    .Select(go => go.GetComponent<Player>())
                                    .ToList();

        // construct the message
        CharactersAvailableMsg message = new CharactersAvailableMsg();
        message.Load(characters);

        // destroy the temporary players again and return the result
        characters.ForEach(player => Destroy(player.gameObject));
        return message;
    }
    void OnServerCharacterDelete(NetworkMessage netMsg)
    {
        print("OnServerCharacterDelete " + netMsg.conn);
        var message = netMsg.ReadMessage <CharacterDeleteMsg>();

        // can only delete while in lobby (aka after handshake and not ingame)
        if (lobby.ContainsKey(netMsg.conn))
        {
            string account    = lobby[netMsg.conn];
            var    characters = Database.CharactersForAccount(account);

            // validate index
            if (0 <= message.index && message.index < characters.Count)
            {
                // delete the character
                print("delete character: " + characters.Keys.ElementAt(message.index));
                Database.CharacterDelete(characters.Keys.ElementAt(message.index));

                // addon system hooks
                Utils.InvokeMany(typeof(NetworkManagerMMO), this, "OnServerCharacterDelete_", message);

                // send the new character list to client
                characters = Database.CharactersForAccount(account);
                var msgchars = new CharactersAvailableMsg {
                    characterNames   = characters.Keys.ToArray(),
                    characterClasses = characters.Values.ToArray()
                };
                netMsg.conn.Send(CharactersAvailableMsg.MsgId, msgchars);
            }
            else
            {
                print("invalid character index: " + account + " " + message.index);
                ClientSendPopup(netMsg.conn, "invalid character index", false);
            }
        }
        else
        {
            print("CharacterDelete: not in lobby: " + netMsg.conn);
            ClientSendPopup(netMsg.conn, "CharacterDelete: not in lobby", true);
        }
    }
Example #11
0
    // helper function to make a CharactersAvailableMsg from all characters in
    // an account
    CharactersAvailableMsg MakeCharactersAvailableMessage(string account)
    {
        // load from database
        // (avoid Linq for performance/gc. characters are loaded frequently!)
        List <Player> characters = new List <Player>();

        foreach (string characterName in Database.singleton.CharactersForAccount(account))
        {
            GameObject player = Database.singleton.CharacterLoad(characterName, playerClasses, true);
            characters.Add(player.GetComponent <Player>());
        }

        // construct the message
        CharactersAvailableMsg message = new CharactersAvailableMsg();

        message.Load(characters);

        // destroy the temporary players again and return the result
        characters.ForEach(player => Destroy(player.gameObject));
        return(message);
    }
Example #12
0
    void OnServerCharacterDelete(NetworkMessage netMsg)
    {
        print("OnServerCharacterDelete " + netMsg.conn);
        CharacterDeleteMsg message = netMsg.ReadMessage <CharacterDeleteMsg>();

        // only while in lobby (aka after handshake and not ingame)
        if (lobby.ContainsKey(netMsg.conn))
        {
            string        account    = lobby[netMsg.conn];
            List <string> characters = Database.CharactersForAccount(account);

            // validate index
            if (0 <= message.index && message.index < characters.Count)
            {
                // delete the character
                print("delete character: " + characters[message.index]);
                Database.CharacterDelete(characters[message.index]);

                // addon system hooks
                Utils.InvokeMany(typeof(NetworkManagerMMO), this, "OnServerCharacterDelete_", message);

                // send the new character list to client
                characters = Database.CharactersForAccount(account);
                CharactersAvailableMsg reply = MakeCharactersAvailableMessage(account);
                netMsg.conn.Send(CharactersAvailableMsg.MsgId, reply);
            }
            else
            {
                print("invalid character index: " + account + " " + message.index);
                ClientSendPopup(netMsg.conn, "invalid character index", false);
            }
        }
        else
        {
            print("CharacterDelete: not in lobby: " + netMsg.conn);
            ClientSendPopup(netMsg.conn, "CharacterDelete: not in lobby", true);
        }
    }
Example #13
0
    void OnServerCharacterCreate(NetworkMessage netMsg)
    {
        print("OnServerCharacterCreate " + netMsg.conn);
        CharacterCreateMsg message = netMsg.ReadMessage <CharacterCreateMsg>();

        // only while in lobby (aka after handshake and not ingame)
        if (lobby.ContainsKey(netMsg.conn))
        {
            // allowed character name?
            if (IsAllowedCharacterName(message.name))
            {
                // not existant yet?
                string account = lobby[netMsg.conn];
                if (!Database.CharacterExists(message.name))
                {
                    // not too may characters created yet?
                    if (Database.CharactersForAccount(account).Count < characterLimit)
                    {
                        // valid class index?
                        List <Player> classes = GetPlayerClasses();
                        if (0 <= message.classIndex && message.classIndex < classes.Count)
                        {
                            // create new character based on the prefab.
                            // -> we also assign default items and equipment for new characters
                            // -> skills are handled in Database.CharacterLoad every time. if we
                            //    add new ones to a prefab, all existing players should get them
                            // (instantiate temporary player)
                            print("creating character: " + message.name + " " + message.classIndex);
                            Player prefab = GameObject.Instantiate(classes[message.classIndex]).GetComponent <Player>();
                            prefab.name               = message.name;
                            prefab.account            = account;
                            prefab.className          = classes[message.classIndex].name;
                            prefab.transform.position = GetStartPositionFor(prefab.className).position;
                            for (int i = 0; i < prefab.inventorySize; ++i)
                            {
                                // add empty slot or default item if any
                                prefab.inventory.Add(i < prefab.defaultItems.Length ? new ItemSlot(new Item(prefab.defaultItems[i])) : new ItemSlot());
                            }
                            for (int i = 0; i < prefab.equipmentInfo.Length; ++i)
                            {
                                // add empty slot or default item if any
                                EquipmentInfo info = prefab.equipmentInfo[i];
                                prefab.equipment.Add(info.defaultItem != null ? new ItemSlot(new Item(info.defaultItem)) : new ItemSlot());
                            }
                            prefab.health = prefab.healthMax; // after equipment in case of boni
                            prefab.mana   = prefab.manaMax;   // after equipment in case of boni

                            // addon system hooks
                            Utils.InvokeMany(typeof(NetworkManagerMMO), this, "OnServerCharacterCreate_", message, prefab);

                            // save the player
                            Database.CharacterSave(prefab, false);
                            GameObject.Destroy(prefab.gameObject);

                            // send available characters list again, causing
                            // the client to switch to the character
                            // selection scene again
                            CharactersAvailableMsg reply = MakeCharactersAvailableMessage(account);
                            netMsg.conn.Send(CharactersAvailableMsg.MsgId, reply);
                        }
                        else
                        {
                            print("character invalid class: " + message.classIndex);
                            ClientSendPopup(netMsg.conn, "character invalid class", false);
                        }
                    }
                    else
                    {
                        print("character limit reached: " + message.name);
                        ClientSendPopup(netMsg.conn, "character limit reached", false);
                    }
                }
                else
                {
                    print("character name already exists: " + message.name);
                    ClientSendPopup(netMsg.conn, "name already exists", false);
                }
            }
            else
            {
                print("character name not allowed: " + message.name);
                ClientSendPopup(netMsg.conn, "character name not allowed", false);
            }
        }
        else
        {
            print("CharacterCreate: not in lobby");
            ClientSendPopup(netMsg.conn, "CharacterCreate: not in lobby", true);
        }
    }
    void OnServerCharacterCreate(NetworkMessage netMsg)
    {
        print("OnServerCharacterCreate " + netMsg.conn);
        var msg = netMsg.ReadMessage <CharacterCreateMsg>();

        // can only delete while in lobby (aka after handshake and not ingame)
        if (lobby.ContainsKey(netMsg.conn))
        {
            // not too long?
            if (msg.name.Length <= charNameMaxLength)
            {
                // only contains letters, number and underscore and not empty (+)?
                // (important for database safety etc.)
                if (Regex.IsMatch(msg.name, @"^[a-zA-Z0-9_]+$"))
                {
                    // not existant yet?
                    var account = lobby[netMsg.conn];
                    if (!Database.CharacterExists(msg.name))
                    {
                        // not too may characters created yet?
                        if (Database.CharactersForAccount(account).Count < charLimit)
                        {
                            // valid class index?
                            var classes = GetPlayerClasses();
                            if (0 <= msg.classIndex && msg.classIndex < classes.Count)
                            {
                                // create new character based on the class
                                // prefab's defaults
                                print("creating character: " + msg.name + " " + msg.classIndex);
                                var player = classes[msg.classIndex].GetComponent <Player>();
                                CreateCharacter(account, msg.name, player);

                                // send available characters list again, causing
                                // the client to switch to the character
                                // selection scene again
                                var msgchars = new CharactersAvailableMsg();
                                msgchars.characters = Database.CharactersForAccount(account).ToArray();
                                netMsg.conn.Send(CharactersAvailableMsg.MsgId, msgchars);
                            }
                            else
                            {
                                print("character invalid class: " + msg.classIndex);
                                ClientSendPopup(netMsg.conn, "character invalid class", false);
                            }
                        }
                        else
                        {
                            print("character limit reached: " + msg.name);
                            ClientSendPopup(netMsg.conn, "character limit reached", false);
                        }
                    }
                    else
                    {
                        print("character name already exists: " + msg.name);
                        ClientSendPopup(netMsg.conn, "name already exists", false);
                    }
                }
                else
                {
                    print("character name invalid: " + msg.name);
                    ClientSendPopup(netMsg.conn, "invalid name", false);
                }
            }
            else
            {
                print("character name too long: " + msg.name);
                ClientSendPopup(netMsg.conn, "name too long", false);
            }
        }
        else
        {
            print("CharacterCreate: not in lobby");
            ClientSendPopup(netMsg.conn, "CharacterCreate: not in lobby", true);
        }
    }
    void OnServerLogin(NetworkMessage netMsg)
    {
        print("OnServerLogin " + netMsg.conn);
        var message = netMsg.ReadMessage <LoginMsg>();

        // correct version?
        if (message.version == Application.version)
        {
            // not too long?
            if (message.account.Length <= accountMaxLength)
            {
                // only contains letters, number and underscore and not empty (+)?
                // (important for database safety etc.)
                if (Regex.IsMatch(message.account, @"^[a-zA-Z0-9_]+$"))
                {
                    // validate account info
                    if (Database.IsValidAccount(message.account, message.password))
                    {
                        // not in lobby and not in world yet?
                        if (!AccountLoggedIn(message.account))
                        {
                            print("login successful: " + message.account);

                            // add to logged in accounts
                            lobby[netMsg.conn] = message.account;

                            // send available characters to client
                            var characters = Database.CharactersForAccount(message.account);
                            var reply      = new CharactersAvailableMsg {
                                characterNames   = characters.Keys.ToArray(),
                                characterClasses = characters.Values.ToArray()
                            };
                            netMsg.conn.Send(CharactersAvailableMsg.MsgId, reply);

                            // addon system hooks
                            Utils.InvokeMany(typeof(NetworkManagerMMO), this, "OnServerLogin_", message);
                        }
                        else
                        {
                            print("account already logged in: " + message.account);
                            ClientSendPopup(netMsg.conn, "already logged in", true);

                            // note: we should disconnect the client here, but we can't as
                            // long as unity has no "SendAllAndThenDisconnect" function,
                            // because then the error message would never be sent.
                            //netMsg.conn.Disconnect();
                        }
                    }
                    else
                    {
                        print("invalid account or password for: " + message.account);
                        ClientSendPopup(netMsg.conn, "invalid account", true);
                    }
                }
                else
                {
                    print("account invalid: " + message.account);
                    ClientSendPopup(netMsg.conn, "forbidden account name", true);
                }
            }
            else
            {
                print("account too long: " + message.account);
                ClientSendPopup(netMsg.conn, "account too long", true);
            }
        }
        else
        {
            print("version mismatch: " + message.account + " expected:" + Application.version + " received: " + message.version);
            ClientSendPopup(netMsg.conn, "outdated version", true);
        }
    }
Example #16
0
    void OnServerCharacterCreate(NetworkMessage netMsg)
    {
        print("OnServerCharacterCreate " + netMsg.conn);
        var message = netMsg.ReadMessage <CharacterCreateMsg>();

        // can only delete while in lobby (aka after handshake and not ingame)
        if (lobby.ContainsKey(netMsg.conn))
        {
            // not too long?
            if (message.name.Length <= characterNameMaxLength)
            {
                // only contains letters, number and underscore and not empty (+)?
                // (important for database safety etc.)
                if (Regex.IsMatch(message.name, @"^[a-zA-Z0-9_]+$"))
                {
                    // not existant yet?
                    string account = lobby[netMsg.conn];
                    if (!Database.CharacterExists(message.name))
                    {
                        // not too may characters created yet?
                        if (Database.CharactersForAccount(account).Count < characterLimit)
                        {
                            // valid class index?
                            var classes = GetPlayerClasses();
                            if (0 <= message.classIndex && message.classIndex < classes.Count)
                            {
                                // create new character based on the prefab
                                // (instantiate temporary player)
                                print("creating character: " + message.name + " " + message.classIndex);
                                var prefab = GameObject.Instantiate(classes[message.classIndex]).GetComponent <Player>();
                                prefab.name               = message.name;
                                prefab.account            = account;
                                prefab.className          = classes[message.classIndex].name;
                                prefab.transform.position = GetStartPosition().position;
                                prefab.health             = prefab.healthMax;
                                prefab.mana               = prefab.manaMax;
                                Database.CharacterSave(prefab);
                                GameObject.Destroy(prefab.gameObject);

                                // send available characters list again, causing
                                // the client to switch to the character
                                // selection scene again
                                var chars = Database.CharactersForAccount(account);
                                var reply = new CharactersAvailableMsg {
                                    characterNames   = chars.Keys.ToArray(),
                                    characterClasses = chars.Values.ToArray()
                                };
                                netMsg.conn.Send(CharactersAvailableMsg.MsgId, reply);
                            }
                            else
                            {
                                print("character invalid class: " + message.classIndex);
                                ClientSendPopup(netMsg.conn, "character invalid class", false);
                            }
                        }
                        else
                        {
                            print("character limit reached: " + message.name);
                            ClientSendPopup(netMsg.conn, "character limit reached", false);
                        }
                    }
                    else
                    {
                        print("character name already exists: " + message.name);
                        ClientSendPopup(netMsg.conn, "name already exists", false);
                    }
                }
                else
                {
                    print("character name invalid: " + message.name);
                    ClientSendPopup(netMsg.conn, "invalid name", false);
                }
            }
            else
            {
                print("character name too long: " + message.name);
                ClientSendPopup(netMsg.conn, "name too long", false);
            }
        }
        else
        {
            print("CharacterCreate: not in lobby");
            ClientSendPopup(netMsg.conn, "CharacterCreate: not in lobby", true);
        }
    }
 void OnClientCharactersAvailable(NetworkConnection conn, CharactersAvailableMsg message)
 {
     charactersAvailableMsg = message;
     print("characters available:" + charactersAvailableMsg.characters.Length);
     state = NetworkState.Lobby;
 }
Example #18
0
    void OnServerCharacterCreate(NetworkMessage netMsg)
    {
        print("OnServerCharacterCreate " + netMsg.conn);
        var msg = netMsg.ReadMessage <CharacterCreateMsg>();

        // can only delete while in lobby (aka after handshake and not ingame)
        if (lobby.ContainsKey(netMsg.conn))
        {
            // not too long?
            if (msg.name.Length <= charNameMaxLength)
            {
                // only contains letters, number and underscore and not empty (+)?
                // (important for database safety etc.)
                if (Regex.IsMatch(msg.name, @"^[a-zA-Z0-9_]+$"))
                {
                    // not existant yet?
                    string account = lobby[netMsg.conn];
                    if (!Database.CharacterExists(msg.name))
                    {
                        // not too may characters created yet?
                        if (Database.CharactersForAccount(account).Count < charLimit)
                        {
                            // valid class index?
                            var classes = GetPlayerClasses();
                            if (0 <= msg.classIndex && msg.classIndex < classes.Count)
                            {
                                // create new character based on the prefab
                                print("creating character: " + msg.name + " " + msg.classIndex);
                                var prefab = classes[msg.classIndex].GetComponent <Player>();
                                Database.CharacterSave(
                                    msg.name,
                                    account,
                                    prefab.name,
                                    GetStartPosition().position,
                                    prefab.level,
                                    prefab.hpMax,
                                    prefab.mpMax,
                                    prefab.strength,
                                    prefab.intelligence,
                                    prefab.exp,
                                    prefab.skillExp,
                                    prefab.gold,
                                    prefab.inventory.ToList(),
                                    prefab.equipment.ToList(),
                                    prefab.skills.ToList(),
                                    prefab.quests.ToList()
                                    );

                                // send available characters list again, causing
                                // the client to switch to the character
                                // selection scene again
                                var chars    = Database.CharactersForAccount(account);
                                var msgchars = new CharactersAvailableMsg {
                                    characterNames   = chars.Keys.ToArray(),
                                    characterClasses = chars.Values.ToArray()
                                };
                                netMsg.conn.Send(CharactersAvailableMsg.MsgId, msgchars);
                            }
                            else
                            {
                                print("character invalid class: " + msg.classIndex);
                                ClientSendPopup(netMsg.conn, "character invalid class", false);
                            }
                        }
                        else
                        {
                            print("character limit reached: " + msg.name);
                            ClientSendPopup(netMsg.conn, "character limit reached", false);
                        }
                    }
                    else
                    {
                        print("character name already exists: " + msg.name);
                        ClientSendPopup(netMsg.conn, "name already exists", false);
                    }
                }
                else
                {
                    print("character name invalid: " + msg.name);
                    ClientSendPopup(netMsg.conn, "invalid name", false);
                }
            }
            else
            {
                print("character name too long: " + msg.name);
                ClientSendPopup(netMsg.conn, "name too long", false);
            }
        }
        else
        {
            print("CharacterCreate: not in lobby");
            ClientSendPopup(netMsg.conn, "CharacterCreate: not in lobby", true);
        }
    }
 void OnClientCharactersAvailable_Example(CharactersAvailableMsg message)
 {
 }