Search Unity

[Tutorial] Take full control of Server application

Discussion in 'Multiplayer' started by limjq44, Jul 20, 2017.

  1. limjq44

    limjq44

    Joined:
    Sep 16, 2016
    Posts:
    10
    Hi guys, this tutorial is for those developers who needs FULL control of their Server application and able to write their own algorithms.

    Things you'll need :
    - Java IDE (I'll recommend you go with Eclipse)
    - netty.io 4.1.11 (You can download the latest one here https://netty.io/downloads.html)
    - WAMP/LAMP (Link : http://www.wampserver.com/en/)
    - Amazon account - Requires you to sign up using credit/debit card, if you don't have 1, you can host the server on your own PC too. (if you are hosting on your own PC, make sure you can do port forwarding!!)


    Configurations:

    1 - Install Eclipse and WAMP/LAMP first, you do not have to worry about the amazon account yet!

    2 - Open up Eclipse and create a new Java project ( File > New > Java Project )

    3 - Type in UnityServer as the project name and click Ok

    4 - Now to add in netty.io dependencies (In Package Explorer, right click on your project UnityServer > Build Path > Configure Build Path, make sure you are in the Libraries tab, click on Add External JARS, browse to the netty.io folder jar>all-in-one>netty-all-4.1.11.Final.jar and jar>all-in-one>netty-all-4.1.11.Final-sources.jar .After you've added the two jar files, click on Ok

    5 - Now we will create 4 class files. Expand your project UnityServer project folder and you should see a folder called src, right click on src and go to New>Class, in the Name, type in MyHandler and click Finish.
    Repeat the steps above and create 3 more classes with the following names :
    Server
    ServerHandler
    ServerInitializer

    6 - Open up MyHandler.java and paste in the following code :
    Code (Java):
    1.  
    2. import io.netty.channel.ChannelDuplexHandler;
    3. import io.netty.channel.ChannelHandlerContext;
    4. import io.netty.handler.timeout.ReadTimeoutException;
    5.  
    6. // Handler should handle the ReadTimeoutException.
    7. public class MyHandler extends ChannelDuplexHandler {
    8.       @Override
    9.      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    10.              throws Exception {
    11.          if (cause instanceof ReadTimeoutException) {
    12.             // System.out.println("Read time out1!!");
    13.              // do something
    14.          } else {
    15.            //  super.exceptionCaught(ctx, cause);
    16.             // System.out.println("Read time out2!!");
    17.          }
    18.      }
    19. }
    7 - Open up Server.java and paste the following code :
    Code (Java):
    1.  
    2. import io.netty.bootstrap.ServerBootstrap;
    3. import io.netty.channel.EventLoopGroup;
    4. import io.netty.channel.nio.NioEventLoopGroup;
    5. import io.netty.channel.socket.nio.NioServerSocketChannel;
    6.  
    7. public class Server {
    8.  
    9.     public static void main(String[] args) throws InterruptedException {
    10.         new Server(8081).run();
    11.     }
    12.  
    13.     private final int port;
    14.     public Server(int port){
    15.         this.port = port;
    16.     }
    17.  
    18.     public void run() throws InterruptedException{
    19.         EventLoopGroup bossGroup = new NioEventLoopGroup();
    20.         EventLoopGroup workerGroup = new NioEventLoopGroup();
    21.  
    22.         try{
    23.             ServerBootstrap bootstrap = new ServerBootstrap()
    24.             .group(bossGroup , workerGroup)
    25.             .channel(NioServerSocketChannel.class)
    26.             .childHandler(new ServerInitializer());
    27.    
    28.             bootstrap.bind(port).sync().channel().closeFuture().sync();
    29.    
    30.         }
    31.         finally{
    32.             bossGroup.shutdownGracefully();
    33.             workerGroup.shutdownGracefully();
    34.         }
    35.    
    36.     }
    37. }
    38.  
    8 - Open up ServerHandler.java and paste the following code :
    Code (Java):
    1.  
    2. import io.netty.channel.Channel;
    3. import io.netty.channel.ChannelHandlerContext;
    4. import io.netty.channel.SimpleChannelInboundHandler;
    5. import io.netty.channel.group.ChannelGroup;
    6. import io.netty.channel.group.DefaultChannelGroup;
    7. import io.netty.util.concurrent.GlobalEventExecutor;
    8.  
    9.  
    10. public class ServerHandler extends SimpleChannelInboundHandler<String> {
    11.  
    12.     private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    13.          
    14.     @Override
    15.     public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
    16.  
    17.         Channel incoming = ctx.channel();
    18.    
    19.         System.out.println("New connection["+incoming.remoteAddress()+"]");
    20.         channels.add(ctx.channel());    
    21.     }
    22.  
    23.     @Override
    24.     public void handlerRemoved(ChannelHandlerContext ctx) throws Exception{
    25.  
    26.         Channel incoming = ctx.channel();
    27.         System.out.println("Connection closed! ["+incoming.remoteAddress()+"]");
    28.         channels.remove(ctx.channel());
    29.     }
    30.  
    31.     @Override
    32.     protected void channelRead0(ChannelHandlerContext arg0, String arg1)
    33.             throws Exception {
    34.          // TODO Auto-generated method stub
    35.         Channel incoming = arg0.channel();
    36.     }
    37. }
    38.  
    39.  
    9 - Open up ServerInitializer.java and paste the following code :
    Code (Java):
    1.  
    2. import io.netty.channel.ChannelInitializer;
    3. import io.netty.channel.ChannelPipeline;
    4. import io.netty.channel.socket.SocketChannel;
    5. import io.netty.handler.codec.DelimiterBasedFrameDecoder;
    6. import io.netty.handler.codec.Delimiters;
    7. import io.netty.handler.codec.bytes.ByteArrayEncoder;
    8. import io.netty.handler.codec.string.StringDecoder;
    9. import io.netty.handler.timeout.ReadTimeoutHandler;
    10.  
    11. public class ServerInitializer extends ChannelInitializer<SocketChannel> {
    12.  
    13.     @Override
    14.     protected void initChannel(SocketChannel arg0) throws Exception{
    15.         ChannelPipeline pipeline = arg0.pipeline();
    16.         // Decoders
    17.  
    18.         pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
    19.         pipeline.addLast("decoder", new StringDecoder());
    20.         pipeline.addLast("encoder", new ByteArrayEncoder());  
    21.         pipeline.addLast("readTimeoutHandler", new ReadTimeoutHandler(120));
    22.         pipeline.addLast("handler", new ServerHandler());
    23.         pipeline.addLast("myHandler", new MyHandler());
    24.  
    25.     }
    26. }
    27.  


    Now choose a port number you want then go to Server.java and find this line
    Change 8081 to the port number you want e.g 9543, you new code should look like this
    Now open up Unity C#, create a new C# script , name it NetworkHandler , attach the script to a GameObject and paste in the following codes

    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using System.Net.Sockets;
    4. using System.Text;
    5. using System.Threading;
    6. using UnityEngine;
    7.  
    8. public class NetworkHandler : MonoBehaviour {
    9.  
    10.     //For Socket Send
    11.     Socket sender;
    12.     string ip_addy = "127.0.0.1";
    13.     int port = 9543;
    14.     ThreadStart ts;
    15.     Thread socketThread;
    16.     bool socketThreadStarted = false;
    17.     bool socketThreadFinishLoading = false;
    18.     bool socketThreadError = false;
    19.     int connectionTimeOut = 5000;
    20.  
    21.  
    22.  
    23.     //For Socket Receive connection
    24.     ThreadStart ts2;
    25.     Thread receiveThread;
    26.     bool receiveThreadStarted = false;
    27.     bool receiveThreadFinishLoading = false;
    28.     string messageReceived = "";
    29.     string messageReceived2 = "";
    30.     int zeroByteCount = 0;
    31.     List<string> receiveMsgList = new List<string>();
    32.     byte[] bytes = new byte[1024];
    33.  
    34.  
    35.  
    36.     void OnApplicationQuit()
    37.     {
    38.         if (sender != null)
    39.         {
    40.             if (sender.Connected)
    41.             {
    42.                 sender.Close();
    43.                 receiveThreadStarted = false;
    44.  
    45.             }
    46.             Debug.Log("Application ending after " + Time.time + " seconds");
    47.         }
    48.     }
    49.  
    50.  void Awake() {
    51.        DontDestroyOnLoad(transform.gameObject);
    52.    }
    53.  
    54.     // Use this for initialization
    55.     void Start () {
    56.         if (!socketThreadStarted)
    57.         {
    58.             Debug.Log("Going to connect to Server IP : "+ip_addy+" on Port : "+port);
    59.             socketThreadStarted = true;
    60.             ts = new ThreadStart(openSocket);
    61.             socketThread = new Thread(ts);
    62.             socketThread.Start();
    63.         }
    64.  
    65.     }
    66.  
    67.     // Update is called once per frame
    68.     void Update () {
    69.    
    70.     }
    71.  
    72.  
    73.  
    74.     void openSocket()
    75.     {
    76.         try
    77.         {
    78.             socketThreadError = false;
    79.             sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    80.             Debug.Log("Trying to connect");
    81.             System.IAsyncResult result = sender.BeginConnect(ip_addy, port, null, null);
    82.             bool success = result.AsyncWaitHandle.WaitOne(connectionTimeOut, true);
    83.  
    84.             if (!success)
    85.             {
    86.                 socketThreadError = true;
    87.                 // NOTE, MUST CLOSE THE SOCKET
    88.                 Debug.Log("FAILED TO CONNECT");
    89.                 sender.Close();
    90.                 // throw new ApplicationException("Failed to connect server.");
    91.             }
    92.             else
    93.             {
    94.                 if (sender.Connected)
    95.                 {
    96.                
    97.                     Debug.Log("Connected");
    98.  
    99.                     //Start Receive thread
    100.                     receiveThreadStarted = true;
    101.                     ts2 = new ThreadStart(receiveHandler);
    102.                     receiveThread = new Thread(ts2);
    103.                     receiveThread.Start();
    104.                 }
    105.                 else
    106.                 {
    107.                     Debug.Log("Failed to connect");
    108.                     sender.Close();
    109.                 }
    110.             }
    111.  
    112.         }
    113.         catch (SocketException se)
    114.         {
    115.             NetworkMessageHandler.error3 = true;
    116.             Debug.Log("SocketException :" + se.SocketErrorCode);
    117.         }
    118.         socketThreadFinishLoading = true;
    119.     }
    120.  
    121.  
    122.     void receiveHandler()
    123.     {
    124.         int bytesRec = 0;
    125.         while (receiveThreadStarted)
    126.         {
    127.             if (bytesRec == 0)
    128.             {
    129.                 zeroByteCount++;
    130.                 if (zeroByteCount > 20)
    131.                 {
    132.                     //Connection closed
    133.                     receiveThreadStarted = false;
    134.                 }
    135.             }
    136.             bytesRec = sender.Receive(bytes);
    137.  
    138.             messageReceived = Encoding.ASCII.GetString(bytes, 0, bytesRec);
    139.  
    140.             if (messageReceived.Contains("\n"))
    141.             {
    142.                 string[] split = messageReceived.Split(new string[] { "\n" }, System.StringSplitOptions.None);
    143.                 for (int i = 0; i < split.Length - 1; i++)
    144.                 {
    145.                     receiveMsgList.Add(split[i]);
    146.                     Debug.Log("Added " + split[i] + " into List");
    147.            
    148.                 }
    149.             }
    150.      
    151.         }
    152.     }
    153.  
    154. }
    155.  
    156.  
    Now go to Eclipse and open up Server.java, on the top you should able to see a Play/Run button, click on that button or press F11. Select Java Application and run it.
    After that go to Unity and Click Play (make sure the script is attached to a GameObject).
    You should be able to see in the Unity Console which says Connected and Eclipse's console which says New Connection[127.0.0.1...

    Now you have successfully established a socket connection between Unity and your Java Server


    Let me know what I should do next and I'll update the codes and instructions

    E.g
    -How to perform encryption between Server/Client
    -Login
    -Create rooms
    -Join Rooms
    etc,etc
     
  2. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Seems promising, but in real life not really usefull. Still, better alternative than nothing.
    I would rather go with C# and LLAPI if I would've writing server/client once again with Unity.

    Offtopic:
    As a former Java developer I must say... Don't use Eclipse it sucks!
    Use JetBrains IDEA. There's a community edition.
     
    fvde likes this.
  3. fvde

    fvde

    Joined:
    May 15, 2014
    Posts:
    19
    It could be more helpful to elaborate on design ideas
    • Web sockets vs. RESTful
    • Database management
    • Authorization and Authentication
    • Error Handling and Feedback
    instead of posting code that may work, but solves one thing and one thing only.

    Also: Yes: IntelliJ (from JetBrains) all the way!
     
    xVergilx and TwoTen like this.
  4. MrVixio

    MrVixio

    Joined:
    Aug 17, 2018
    Posts:
    1
    Hey, how can I make a command sender? So that I can send a broadcast command.