Examples (Unity)
Examples (iOS)
Examples (Android)

 

» Buddy Messenger

» Overview

The Buddy Messenger example gives a demonstration of the client-side capabilities of the Buddy List API using Unity and SmartFoxServer 2X. It is fully compatible with the Buddy Messenger clients from the other APIs, which means users can communicate with one another even if they are on different clients and devices.

Using the Buddy List API, developers can add an instant messenger-like interface to any application, allowing users to see the status of friends (the so-called buddies) in their contact list and communicate with them regardless of the SmartFoxServer Room they are connected to (although they will need to know the user name to do so). In fact, in order to optimize the bandwidth usage in SmartFoxServer, messaging and user presence notifications are mostly limited to the users in the same Room. Using the buddy list system, this limit can be overridden in order to offer an improved user experience.

In this example, the screen-space overlay canvas of the Unity scene contains a sliding panel allowing the user to add new buddies by entering their name, remove buddies, block a buddy (to stop receiving status notifications and instant messages) and send him/her a message (a separate draggable panel is displayed whenever a new conversation is started by the user himself or by one of his buddies). The panel also shows the buddies state and their details.

In fact, using a separate sliding panel, each user can change his own details and state as buddy to other users, thanks to the very flexible Buddy Variable objects featured by SmartFoxServer. Similarly to User Variables and Room Variables, Buddy Variables are stored on the server and broadcasted to the other clients. In particular, when set or updated, a Buddy Variable is broadcasted to all the users referenced in the buddy list of its owner.
Some Buddy Variables are predefined and reserved to store specific informations:

Buddy Variables can be online or offline. Offline variables can be accessed even if the user is not connected to SmartFoxServer (for example to store buddy details which are independent from the current session), while the online variables require the user presence. In our example the user's age is saved as an offline Buddy Variable, while his current mood as an online one.

>> DOWNLOAD the source files <<

» Installation

» Running the example

In order to run the application follow these steps:

  1. make sure your SmartFoxServer 2X installation contains the BasicExamples Zone definition;
  2. start SmartFoxServer 2X (v2.10 or later is highly recommended);
  3. make sure the client runs on the same machine as the server (the IP address can otherwise be changed in the source code);
  4. open the /deploy/BuddyMessenger.html file in a browser.

» Source code setup

The complete project is contained in a zipped folder. To access and build it please follow these steps:

  1. unzip the file contained in the /source folder;
  2. start Unity;
  3. in the Projects panel click on the Open other button and browse to the top folder of the unzipped package, then click Open;
  4. wait for the project setup completion (Unity needs to regenerate some libraries);
  5. go to the Project panel, click on the Assets folder and double click on the MainScene scene to open it.

All relevant assets are contained in the Assets/BuddyMessengerAssets folder; in particular the C# code is in the Scripts subfolder and the SmartFoxServer 2X client API DLLs are contained in the Plugins subfolder. Read the introduction to understand why multiple DLLs are used.

» Code highlights

Code for this example is contained in the script file called BuddyMessenger in the Scripts subfolder, attached to the Controller game object in the scene.
The structure of the file is a basic Unity C# script with a Start() and Update() methods. Additionally there are a number of listeners for the event generated by the UI components (mostly buttons), some helper methods and the listeners for the SmartFoxServer client API events.

» Connection and login

The connection and login approach followed in this example is very similar to the one from the Lobby example, which we recommend to review. The only difference is that we have to addsome additional listeners for the BuddyList-related events.

sfs.AddEventListener(SFSBuddyEvent.BUDDY_LIST_INIT, OnBuddyListInit);
sfs.AddEventListener(SFSBuddyEvent.BUDDY_ERROR, OnBuddyError);
sfs.AddEventListener(SFSBuddyEvent.BUDDY_ONLINE_STATE_UPDATE, OnBuddyListUpdate);
sfs.AddEventListener(SFSBuddyEvent.BUDDY_VARIABLES_UPDATE, OnBuddyListUpdate);
sfs.AddEventListener(SFSBuddyEvent.BUDDY_ADD, OnBuddyListUpdate);
sfs.AddEventListener(SFSBuddyEvent.BUDDY_REMOVE, OnBuddyListUpdate);
sfs.AddEventListener(SFSBuddyEvent.BUDDY_BLOCK, OnBuddyListUpdate);
sfs.AddEventListener(SFSBuddyEvent.BUDDY_MESSAGE, OnBuddyMessage);

Most events are handled by the same method, OnBuddyListUpdate(), which rebuilds the buddy list in the UI from scratch. This has been done on purpose, to simplify the code. A more refined approach would update the specific list item to which each event refers, also discarding those events referring to the current user.

» Buddy list initialization and UI update

In order to make the buddy list system available in our game, the InitBuddyList request must be sent to the server after a successful login is performed (see the OnLogin() handler).

sfs.Send(new Sfs2X.Requests.Buddylist.InitBuddyListRequest());

This causes the OnBuddyListInit event handler to be called, which takes care of populating the buddy list (see the OnBuddyListUpdate() handler below) and set in the UI the current user details (like nickname, state, and age) saved as offline Buddy Variables.

private void OnBuddyListInit(BaseEvent evt) {
    // Populate list of buddies
    OnBuddyListUpdate(evt);
    
    // Set current user details as buddy

    // Nick
    nickInput.text = (sfs.BuddyManager.MyNickName != null ? sfs.BuddyManager.MyNickName : "");
    
    // States
    foreach (string state in sfs.BuddyManager.BuddyStates) {
        string stateValue = state;
        GameObject newDropDownItem = Instantiate(stateItemPrefab) as GameObject;
        BuddyStateItemButton stateItem = newDropDownItem.GetComponent<BuddyStateItemButton>();
        stateItem.stateValue = stateValue;
        stateItem.label.text = stateValue;
        
        stateItem.button.onClick.AddListener(() => OnStateItemClick(stateValue));
        
        newDropDownItem.transform.SetParent(stateDropDown, false);

        // Set current state
        if (sfs.BuddyManager.MyState == state) {
            OnStateItemClick(state);
        }
    }

    // Online
    onlineToggle.isOn = sfs.BuddyManager.MyOnlineState;
    
    // Buddy variables
    BuddyVariable age = sfs.BuddyManager.GetMyVariable(BUDDYVAR_AGE);
    ageInput.text = ((age != null && !age.IsNull()) ? Convert.ToString(age.GetIntValue()) : "");
    
    BuddyVariable mood = sfs.BuddyManager.GetMyVariable(BUDDYVAR_MOOD);
    moodInput.text = ((mood != null && !mood.IsNull()) ? mood.GetStringValue() : "");
}

The OnBuddyListUpdate() event handler is responsible of building (or re-building) the list of buddies displayed in the example's UI. This includes an image showing the state of the buddy, along with his name or nickname, age and mood (if set).

private void OnBuddyListUpdate(BaseEvent evt) {
    // Remove current list content
    for (int i = buddyListContent.childCount - 1; i >= 0; --i) {
        GameObject.Destroy(buddyListContent.GetChild(i).gameObject);
    }
    buddyListContent.DetachChildren();

    // Recreate list content
    foreach (Buddy buddy in sfs.BuddyManager.BuddyList) {
        GameObject newListItem = Instantiate(buddyListItemPrefab) as GameObject;

        BuddyListItem buddylistItem = newListItem.GetComponent();

        // Nickname
        buddylistItem.mainLabel.text = (buddy.NickName != null && buddy.NickName != "") ? buddy.NickName : buddy.Name;

        // Age
        BuddyVariable age = buddy.GetVariable(BuddyMessenger.BUDDYVAR_AGE);
        buddylistItem.mainLabel.text += (age != null && !age.IsNull()) ? " (" + age.GetIntValue() + " yo)" : "";

        // Mood
        BuddyVariable mood = buddy.GetVariable(BuddyMessenger.BUDDYVAR_MOOD);
        buddylistItem.moodLabel.text = (mood != null && !mood.IsNull()) ? mood.GetStringValue() : "";

        // Icon
        if (buddy.IsBlocked) {
            buddylistItem.stateIcon.sprite = IconBlocked;
            buddylistItem.chatButton.interactable = false;
            buddylistItem.blockButton.transform.GetChild(0).GetComponentInChildren().sprite = IconUnblock;
        } else {
            buddylistItem.blockButton.transform.GetChild(0).GetComponentInChildren().sprite = IconBlock;

            if (!buddy.IsOnline) {
                buddylistItem.stateIcon.sprite = IconOffline;
                buddylistItem.chatButton.interactable = false;
            } else {
                string state = buddy.State;
                
                if (state == "Available")
                    buddylistItem.stateIcon.sprite = IconAvailable;
                else if (state == "Away")
                    buddylistItem.stateIcon.sprite = IconAway;
                else if (state == "Occupied")
                    buddylistItem.stateIcon.sprite = IconOccupied;
            }
        }

        // Buttons
        string buddyName = buddy.Name; // Required or the listeners will always receive the last buddy name
        buddylistItem.removeButton.onClick.AddListener(() => OnRemoveBuddyButtonClick(buddyName));
        buddylistItem.blockButton.onClick.AddListener(() => OnBlockBuddyButtonClick(buddyName));
        buddylistItem.chatButton.onClick.AddListener(() => OnChatBuddyButtonClick(buddyName));

        buddylistItem.buddyName = buddyName;

        // Add item to list
        newListItem.transform.SetParent(buddyListContent, false);

        // Also update chat panel if open
        Transform panel = chatPanelsContainer.Find(buddyName);
        
        if (panel != null) {
            ChatPanel chatPanel = panel.GetComponent();
            chatPanel.buddy = buddy;
        }
    }
}

» Add, remove, update and block buddies

The buddy list is recreated each time a buddy is added, removed, blocked, unblocked, his online/offline status changes or one of its Buddy Variables is updated. All these events are triggered by the user interaction, through the respective requests sent to SmartFoxServer like the following examples show.

Add buddy (see OnAddBuddyButtonClick() method):

if (buddyInput.text != "") {
    sfs.Send(new Sfs2X.Requests.Buddylist.AddBuddyRequest(buddyInput.text));
    buddyInput.text = "";
}

Block/unblock buddy (see OnBlockBuddyButtonClick() method):

bool isBlocked = sfs.BuddyManager.GetBuddyByName(buddyName).IsBlocked;
sfs.Send(new Sfs2X.Requests.Buddylist.BlockBuddyRequest(buddyName, !isBlocked));

Set user nickname, age, mood or state (see OnSetDetailsButtonClick() method):

List buddyVars = new List();
buddyVars.Add(new SFSBuddyVariable(ReservedBuddyVariables.BV_NICKNAME, nickInput.text));
buddyVars.Add(new SFSBuddyVariable(BUDDYVAR_AGE, Convert.ToInt32(ageInput.text)));
buddyVars.Add(new SFSBuddyVariable(BUDDYVAR_MOOD, moodInput.text));
buddyVars.Add(new SFSBuddyVariable(ReservedBuddyVariables.BV_STATE, currentState));

sfs.Send(new Sfs2X.Requests.Buddylist.SetBuddyVariablesRequest(buddyVars));

» Exchanging messages with buddies

When clicking on the chat icon in the buddy list, a draggable panel is displayed in the screen-space overlay, to start a conversation with the related buddy (if he/she is online in the buddy list system). By means of the input field and Send button available on the panel, a message can be sent with a BuddyMessage request.

ChatPanel chatPanel = panel.GetComponent();

string message = chatPanel.messageInput.text;

// Add a custom parameter containing the recipient name,
// so that we are able to write messages in the proper chat tab
ISFSObject _params = new SFSObject();
_params.PutUtfString("recipient", buddyName);

Buddy buddy = sfs.BuddyManager.GetBuddyByName(buddyName);

sfs.Send(new Sfs2X.Requests.Buddylist.BuddyMessageRequest(message, buddy, _params));

chatPanel.messageInput.text = "";

Note that the custom parameter "recipient" is added to the request, set to the name of the message recipient. This is useful in the OnBuddyMessage() handler to determine in which chat panel (if more than one is open) the message must be displayed when the sender is the current user.

private void OnBuddyMessage(BaseEvent evt) {
    bool isItMe = (bool)evt.Params["isItMe"];
    Buddy sender = (Buddy)evt.Params["buddy"];
    string message = (string)evt.Params["message"];

    Buddy buddy;
    if (isItMe)
    {
        string buddyName = (evt.Params["data"] as ISFSObject).GetUtfString("recipient");
        buddy = sfs.BuddyManager.GetBuddyByName(buddyName);
    }
    else
        buddy = sender;

    if (buddy != null) {
        // Open panel if needed
        OnChatBuddyButtonClick(buddy.Name);

        // Print message
        Transform panel = chatPanelsContainer.Find(buddy.Name);
        ChatPanel chatPanel = panel.GetComponent();
        chatPanel.addMessage("" + (isItMe ? "You" : buddy.Name) + ": " + message);
    }
}

To see other advanced uses of SmartFoxServer you can now move onwards to the next examples.

NOTE
You should also read the comments to methods and properties in the example source code for additional informations and possible code optimizations.

» More resources

You can learn more about the SmartFoxServer basics by consulting the following resources: