Home

Awesome

Ginet

<img src="https://github.com/gmich/Ginet/blob/master/Resources/Icons/network.png?raw=true" alt="Cedrus logo" title="Cedrus" hspace="10" align="left" width=96 height=96 >

Build status Nuget downloads

A fluent networking library build on top of lidgren network. Ginet extends lidgren network with a fluent and functional API.


Quick start

Ginet is available as a NuGet package. You can install it using the NuGet Package Console window:

PM> Install-Package Ginet

Ginet favors message exchange. All classes that are handled via the INetworkManager must be marked with the GinetPackageAttribute. Ginet inferres messages by adding a unique byte id.

The classes must be marked as public with public getters / setters and contain only primitive types. The client and server registered packages should be the same. For complex serialization you can provide your custom serializer via attribute.

          [GinetPackage]
          public class MyPackage
          {
             public string Message { get; set; }
          }
          
          [GinetPackage]
          public class ConnectionApprovalMessage
          {
             public string Sender { get; set; }
             public string Password { get; set; }
          }

A simple client-server chat example here.


Server

Create, configure and register the classes that are marked with the GinetPackage attribute

          var server = new NetworkServer("MyServer",
                    builder =>
                    {
                        //via reflection
                        builder.RegisterPackages(Assembly.Load("Packages.Assembly.Name"));
                        //or manually
                        builder.Register<MyPackage>();
                    },
                    cfg =>
                    {
                       cfg.Port = 1234;
                       cfg.ConnectionTimeout = 5.0f;
                       //Additional configuration
                    });
          server.IncomingMessageHandler.LogTraffic();

Start the server

          server.Start(NetDeliveryMethod.ReliableOrdered, channel: 0);

Process Incoming Messages in a separate thread

          server.ProcessMessagesInBackground();           

If you have an update loop you can await the message processing in an async method

        await server.ProcessMessages()

Configure how to respond to incoming packages

          server.Broadcast<ChatMessage>((msg, im, om) =>
          {
              server.Out.Info($"Received {msg.Message}");
              server.SendToAllExcept(om, im.SenderConnection, NetDeliveryMethod.ReliableOrdered, channel: 0);
          }, 
          packageTransformer: msg => 
              msg.Message += "this is broadcasted");
          
          server.BroadcastExceptSender<ChatMessage>((sender, msg) =>
          {
              server.Out.Info($"Broadcasting {msg.Message}. Received from: {sender}");
          });

Configure context specific behavior with the NetIncomingMessageType and NetConnectionStatus enums

        server.IncomingMessageHandler.OnMessage(NetIncomingMessageType.ConnectionApproval, incomingMsg =>
        {
           //Configure connection approval
           var parsedMsg = server.ReadAs<ConnectionApprovalMessage>(incomingMsg);
           if(parsedMsg.Password == "my secret and encrypted password")
           {
                incomingMsg.SenderConnection.Approve();
                incomingMsg.SenderConnection.Tag = parsedMsg.Sender;
           }
           else
           {
                incomingMsg.SenderConnection.Deny();
           }
        });
         server.IncomingMessageHandler.OnConnectionChange(NetConnectionStatus.Disconnected, incomingMsg =>
         {
                 server.SendToAllExcept(
                     server.ConvertToOutgoingMessage(new MyPackage
                     { Message = $"Disconnected {incomingMsg.SenderConnection}" }),
                     im.SenderConnection,
                     NetDeliveryMethod.ReliableOrdered,
                     channel: 0);
          });
         server.IncomingMessageHandler.OnConnection(server.Host.Connections.First(), (msgType, incomingMsg) =>
         {
                 //...
         });

Upon adding a handler, an IDisposable object is returned. Disposing it causes the handler deregistration.

        var handlerDisposable = server.IncomingMessageHandler.OnMessage(NetIncomingMessageType.Data, im => { });
        
        handlerDisposable.Dispose();

Client

Create, configure and register the classes that are marked with the GinetPackage attribute

         var client = new NetworkClient("Chat", 
                   builder=>
                        builder.RegisterPackages(Assembly.Load("Packages.Assembly.Name")),
                   cfg =>
                   {  
                      //client configuration
                   });

Start / Connect

         client.Start(NetDeliveryMethod.ReliableOrdered, channel: 0);
         client.Connect("localhost", 1234, new ConnectionApprovalMessage
         {
             Sender = "Me",
             Password = "1234"
         });

Process Incoming messages

         client.ProcessMessagesInBackground();           

Handle incoming messages

        client.IncomingMessageHandler
              .OnPackage<MyPackage>((msg, sender) => 
                  Console.WriteLine($"Received {msg.Message} from {sender.SenderConnection}"));
                  

Send a message

       client.Send(new MyPackage { Message = "Hello" },
                  (om,peer) => peer.SendMessage(om,NetDeliveryMethod.ReliableOrdered));

Create a message sender lifter

       IPackageSender packageSender = client.LiftSender((msg, peer) =>
              peer.SendMessage(msg, NetDeliveryMethod.ReliableOrdered));
              
       packageSender.Send(new MyPackage { Message = "Hello" });

Logging

For custom logging targets implement the IAppender interface and pass it in the client , server creation

          var server = new NetworkServer(
          "MyServer",
          cfg => {},
          output: new MyAppenderImplementation());

The default IAppender uses the Console.WriteLine in the ActionAppender constructor.

          var server = new NetworkServer(
          "MyServer",
          cfg => {},
          output: new ActionAppender(Console.WriteLine));

Custom serializers

For custom encoding / decoding for a type, simply implement the IPackageSerializer interface add the PackageSerializerAttribute to the class

       [GinetPackage]
       [PackageSerializer(typeof(MyCustomSerializer))]
       public class MyPackage
       {
          public string Message { get; set; }
       }

<div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>