This document is about: SERVER 4
SWITCH TO

An App From Scratch

This tutorial will try to help you understand how to build an application from scratch.

This tutorial has nothing to do with Photon Chat product. It is just a very basic and simple demo sample meant as an example of how to start a Photon Server application.

Build a Simple Chat Server In 10 Minutes.

Note: We assume you have followed the steps in "Photon in 5 Minutes" and have a locally running Photon Server.

This tutorial is thought as a first step in understanding the basics of the main concepts in Photon Application and Peer.

  • Download the Photon Server SDK and unzip it.
  • Using Visual Studio, create a new class library project ChatServer.
  • Add references to ExitGamesLibs.dll, Photon.SocketServer.dll and PhotonHostRuntimeInterfaces.dll

Now, create a new class ChatServer that inherits from Photon.SocketServer.ApplicationBase:

C#

    using Photon.SocketServer;

    public class ChatServer : ApplicationBase
    {
        protected override PeerBase CreatePeer(InitRequest initRequest)
        {
        }

        protected override void Setup()
        {
        }

        protected override void TearDown()
        {
        }
    }

Create a new class ChatPeer that inherits from Photon.SocketServer.ClientPeer:

C#

    using Photon.SocketServer;
    using PhotonHostRuntimeInterfaces;

    public class ChatPeer : ClientPeer
    {
        public ChatPeer(InitRequest initRequest)
            : base(initRequest)
        {
        }

        protected override void OnDisconnect(DisconnectReason disconnectCode, string reasonDetail)
        {
        }

        protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
        {
        }
    }

Return a new instance of ChatPeer at ChatServer.CreatePeer:

C#

        protected override PeerBase CreatePeer(InitRequest initRequest)
        {
            return new ChatPeer(initRequest);
        }

The ChatServer assembly will be loaded by Photon native core on the Default Photon instance. Thus, the server configuration file, PhotonServer.config, should contain the proper definition of that application. The following snippet illustrates an easy way to do this:

XML

<?xml version="1.0"?>
<Configuration>
    <ChatServer DisplayName="Chat Server">
        <TCPListeners>
            <TCPListener
                IPAddress="0.0.0.0"
                Port="4530"
                OverrideApplication="ChatServer"
                >
            </TCPListener>
        </TCPListeners>
        <!-- Defines the Photon Runtime Assembly to use. -->
        <Runtime
            Assembly="PhotonHostRuntime, Culture=neutral"
            Type="PhotonHostRuntime.PhotonDomainManager"
            UnhandledExceptionPolicy="Ignore">
        </Runtime>
        <!-- other elements -->
        <Applications Default="ChatServer">
            <Application
                Name="ChatServer"
                BaseDirectory="ChatServer"
                Assembly="ChatServer"
                Type="ChatServer">
            </Application>
            <!-- any other applications -->
        </Applications>
        <!-- other elements -->
    </ChatServer>
</Configuration>

This configuration requires that the server binaries are located under deploy/ChatServer/bin and that class ChatServer does not belong to a namespace. For more information about server configuration please refer to the respective documentation page.

Create a new console project for a chat client. Add reference to Photon3DotNet.dll to the new project. Client code should read as follows.

C#

using System;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using System.Threading;

public class ChatClient : IPhotonPeerListener
{
    private bool connected;
    PhotonPeer peer;

    public static void Main()
    {
        var client = new ChatClient();
        client.peer = new PhotonPeer(client, ConnectionProtocol.Tcp);
        // connect
        client.DebugReturn(DebugLevel.INFO, "Connecting to server at 127.0.0.1:4530 using TCP");
        client.peer.Connect("127.0.0.1:4530", "ChatServer");
        // client needs a background thread to dispatch incoming messages and send outgoing messages
        client.Run();
        while (true)
        {
            if (!client.connected) { continue; }
            // read input
            string buffer = Console.ReadLine();
            // send to server
            var parameters = new Dictionary<byte, object> { { 1, buffer } };
            client.peer.OpCustom(1, parameters, true);
        }
    }

    private void UpdateLoop()
    {
        while (true)
        {
            peer.Service();
        }
    }

    public void Run()
    {
        Thread thread = new Thread(UpdateLoop); 
        thread.IsBackground = true;
        thread.Start();
    }

    #region IPhotonPeerListener

    public void DebugReturn(DebugLevel level, string message)
    {
        Console.WriteLine(string.Format("{0}: {1}", level, message));
    }

    public void OnEvent(EventData eventData)
    {
        DebugReturn(DebugLevel.INFO, eventData.ToStringFull());
        if (eventData.Code == 1)
        {
            DebugReturn(DebugLevel.INFO, string.Format("Chat Message: {0}", eventData.Parameters[1]));
        }
    }

    public void OnMessage(object messages)
    {
        throw new NotImplementedException();
    }

    public void OnOperationResponse(OperationResponse operationResponse)
    {
        DebugReturn(DebugLevel.INFO, operationResponse.ToStringFull());
    }

    public void OnStatusChanged(StatusCode statusCode)
    {
        if (statusCode == StatusCode.Connect)
        {
            connected = true;
        }
        switch (statusCode)
        {
            case StatusCode.Connect:
                DebugReturn(DebugLevel.INFO, "Connected");
                connected = true;
                break;
            default:
                DebugReturn(DebugLevel.ERROR, statusCode.ToString());
                break;
        }
    }

    #endregion
}

If we now start the server the client will be able to connect and to send text messages. The server logic to process these text messages is still missing. To verify that the message was received we answer with an OperationResponse at ChatPeer.OnOperationRequest.

C#

    protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
    {
        // send operation response (~ACK) back to peer
        var response = new OperationResponse(operationRequest.OperationCode);
        SendOperationResponse(response, sendParameters);
    }

The chat client should now be able to print the event code and the chat message.

Next thing we want to do is to receive the chat messages on other clients. We implement the receiver using a publish/subscribe pattern.

C#

using Photon.SocketServer;
using PhotonHostRuntimeInterfaces;
using System;

public class ChatPeer : ClientPeer
{
    public ChatPeer(InitRequest request)
        : base(request)
    {
        BroadcastMessage += OnBroadcastMessage;
    }

    private static event Action<ChatPeer, EventData, SendParameters> BroadcastMessage;

    protected override void OnDisconnect(DisconnectReason disconnectCode, string reasonDetail)
    {
        BroadcastMessage -= OnBroadcastMessage;
    }

    protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
    {
        if (operationRequest.OperationCode == 1) // Chat Custom Operation Code = 1
        {
            // broadcast chat custom event to other peers
            var eventData = new EventData(1) { Parameters = operationRequest.Parameters }; // Chat Custom Event Code = 1
            BroadcastMessage(this, eventData, sendParameters);
            // send operation response (~ACK) back to peer
            var response = new OperationResponse(operationRequest.OperationCode);
            SendOperationResponse(response, sendParameters);
        }
    }

    private void OnBroadcastMessage(ChatPeer peer, EventData eventData, SendParameters sendParameters)
    {
        if (peer != this) // do not send chat custom event to peer who called the chat custom operation 
        {
            SendEvent(eventData, sendParameters);
        }
    }
}

If you now start two clients both will be able to exchange messages. Do not forget to start Photon server or to restart it with the new configuration. Go to the documentation page if you have troubles with it.

Back to top