Skip to content

Custom Networking, Server has a server and client class #318

@ZaCkOX

Description

@ZaCkOX

My custom networking (Server is a player) has a connection array holding data about clients. If someone is hosting as a server, both the Dissonance server and client classes will run. How would I move the bytes then if my network doesn't work like this. I suppose I keep a reference to the custom comms network and to the custom server, then send the bytes directly without any networking but I have to pass a CustomPeer and that peer includes an IPEndPoint and or SteamConnection (NetworkReceivedPacket needs CustomPeer for Custom Server). I have spent a lot of time reading through the custom networking tutorial, and preparing everything to make this work. However, I understand my setup and not this very well. I have been in the source code many times over. I wouldn't be surprised if I have made more than a handful of mistakes, any help would be appreciated.

#define USING_DISSONANCE

using Dissonance;
using Dissonance.Networking;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Net;
using UnityEngine;
using static NetworkManager;
using static PublicStatics;
using static SteamworksManager;

public class CustomCommsNetwork: BaseCommsNetwork<CustomServer, CustomClient, CustomPeer, Unit, Unit> {

    #region Declares

    //Declare privates
    private CustomServer customServer = null;
    private CustomClient customClient = null;

    //Declare public properties
    public CustomServer GetCustomServer { get => customServer; }
    public CustomClient GetCustomClient { get => customClient; }

    #endregion

    #region Constructor

    public CustomCommsNetwork() { } //Do not add parameters

    #endregion

    #region Public functions

    protected override CustomServer CreateServer(Unit details) {
        customServer = new CustomServer(this, details);
        return customServer;
    }

    protected override CustomClient CreateClient(Unit details) {
        customClient = new CustomClient(this, details);
        return customClient;
    }

    #if USING_DISSONANCE

    protected override void Initialize() {

        Debug.Log("Dissonance is about to initialize.");

        base.Initialize();
    }

    protected override void Update() {
        Debug.Log("Dissonance update is running!");

        if (IsInitialized) {
            bool networkActive = NetworkIsActive();
            if (networkActive) {
                bool server = networkManager.SubSystem.IsHost;
                bool client = networkManager.SubSystem.IsHost;

                // Check what mode Dissonance is in and if
                // they're different then call the correct method
                if (Mode.IsServerEnabled() != server || Mode.IsClientEnabled() != client)
                {
                    // HLAPI is server and client, so run as a non-dedicated
                    // host (passing in the correct parameters)
                    if (server && client)
                        RunAsHost(Unit.None, Unit.None);

                    // HLAPI is just a server, so run as a dedicated host
                    else if (server)
                        RunAsDedicatedServer(Unit.None);

                    // HLAPI is just a client, so run as a client
                    else if (client)
                        RunAsClient(Unit.None);
                }
            }
            else if (Mode != NetworkMode.None)
            {
                //Network is not active, make sure Dissonance is not active
                Stop();
            }
        }
        base.Update();
        #region Functions

        bool NetworkIsActive() {
            if (networkManager.Network != null) {
                if (gameCanvas.ScreenViewState == GameCanvas.ScreenViewStates.Lobby || 
                    gameCanvas.ScreenViewState == GameCanvas.ScreenViewStates.LoadingSceneForGame ||
                    gameCanvas.ScreenViewState == GameCanvas.ScreenViewStates.UnloadingSceneForLobby ||
                    gameCanvas.ScreenViewState == GameCanvas.ScreenViewStates.GameScene ||
                    gameCanvas.ScreenViewState == GameCanvas.ScreenViewStates.UnloadingSceneForGame) {

                    //For now return true, until sure if more code is necessary here
                    return true;

                }
            }
            return false;
        }

        #endregion
    }

    #endif

    #endregion

}

public class CustomServer: BaseServer<CustomServer, CustomClient, CustomPeer> {

    #region Declares

    //Declare
    private ConcurrentQueue<Packet> packets = new ConcurrentQueue<Packet>(); //Convert to non allocating later

    #endregion

    #region Classes

    private class Packet {
        public CustomPeer customPeer;
        public ArraySegment<byte> packet;
        public Packet(CustomPeer customPeer, ArraySegment<byte> packet) {
            this.customPeer = customPeer;
            this.packet = packet;
        }
    }

    #endregion

    public CustomServer(CustomCommsNetwork network, Unit details) { }

    public void EnqueuePacket(CustomPeer customPeer, ArraySegment<byte> packet) {
        packets.Enqueue(new Packet(customPeer, packet));
    }

    public override void Connect() {
        
    }

    public override void Disconnect() {
        
    }
    
    protected override void ReadMessages() {
        int count = packets.Count;
        for (int i = 0; i < count; i++) {
            packets.LineBlockingDequeue(out Packet packet);
            NetworkReceivedPacket(packet.customPeer, packet.packet);
        }
    }

    protected override void SendReliable(CustomPeer destination, ArraySegment<byte> packet) {
        //Declare
        UDPSubSystem.Host host = networkManager.Host;
        UDPSubSystem.Connection connection = GetConnection(host, destination, out ushort slotIndex);
        byte localHeaderSize = UDPSubSystem.PUBLIC_CONSTANTS.PACKET.SIZE.OPCODE + UDPSubSystem.PUBLIC_CONSTANTS.DATA_TYPE.SIZE.USHORT + 
                               UDPSubSystem.PUBLIC_CONSTANTS.DATA_TYPE.SIZE.USHORT;
        byte[] data = new byte[localHeaderSize + packet.Count]; //Remove GC later
        byte[] opcode = OpcodesClient.GetOpcode(OpcodesClient.Opcodes.R_DissonanceMicrophone);
        byte[] slotIndexBytes = BitConverter.GetBytes(slotIndex); //Remove bitconverter later
        byte[] length = BitConverter.GetBytes((ushort)packet.Count); //Remove bitconverter later

        data[0] = opcode[0];
        data[1] = opcode[1];
        data[2] = slotIndexBytes[0];
        data[3] = slotIndexBytes[1];
        data[4] = length[0];
        data[5] = length[1];

        int count = packet.Count;
        for (int i = 0; i < count; i++) {
            data[i + localHeaderSize] = packet[i];
        }

        host.WriteData(connection, data);
    }

    //Write better later
    private UDPSubSystem.Connection GetConnection(UDPSubSystem.Host host, CustomPeer destination, out ushort slotIndex) {
        switch (networkManager.NetworkingSocket) {
            case NetworkingSockets.SteamSocket:

                for (int i = 0; i < host.connections.Length; i++) {

                    if (host.connections[i].steamConnection.Equals(destination.steamConnection)) {
                        slotIndex = (ushort)(i + 1);
                        return host.connections[i];
                    }
                }
                break;
            default: //UDPSocket

                for (int i = 0; i < host.connections.Length; i++) {
                    if (host.connections[i].ipEndPoint.Equals(destination.ipEndPoint)) {
                        slotIndex = (ushort)(i + 1);
                        return host.connections[i];
                    }
                }
                break;
        }
        //Set, should never get here
        slotIndex = 1;
        return host.connections[0];
    }

    protected override void SendUnreliable(CustomPeer destination, ArraySegment<byte> packet) {
        UDPSubSystem.Host host = networkManager.Host;
        UDPSubSystem.Connection connection = GetConnection(host, destination, out ushort slotIndex);
        byte localHeaderSize = UDPSubSystem.PUBLIC_CONSTANTS.PACKET.SIZE.OPCODE + UDPSubSystem.PUBLIC_CONSTANTS.DATA_TYPE.SIZE.USHORT + 
                               UDPSubSystem.PUBLIC_CONSTANTS.DATA_TYPE.SIZE.USHORT;
        byte[] data = new byte[localHeaderSize + packet.Count]; //Remove GC later
        byte[] opcode = OpcodesClient.GetOpcode(OpcodesClient.Opcodes.U_DissonanceMicrophone);
        byte[] slotIndexBytes = BitConverter.GetBytes(slotIndex); //Remove bitconverter later
        byte[] length = BitConverter.GetBytes((ushort)packet.Count); //Remove bitconverter later

        data[0] = opcode[0];
        data[1] = opcode[1];
        data[2] = slotIndexBytes[0];
        data[3] = slotIndexBytes[1];
        data[4] = length[0];
        data[5] = length[1];

        int count = packet.Count;
        for (int i = 0; i < count; i++) {
            data[i + localHeaderSize] = packet[i];
        }

        host.WriteData(connection, data);
    }

}

public class CustomClient : BaseClient<CustomServer, CustomClient, CustomPeer> {

    #region Declares

    private ConcurrentQueue<ArraySegment<byte>> packets = new ConcurrentQueue<ArraySegment<byte>>(); //Convert to non allocating later
    private CustomCommsNetwork customCommsNetwork = null;

    #endregion

    public CustomClient(CustomCommsNetwork network, Unit details) : base(network) { 
        customCommsNetwork = network;
    }

    public void EnqueuePacket(ArraySegment<byte> packet) {
        packets.Enqueue(packet);
    }

    public override void Connect() {
        base.Connected();
    }

    protected override void ReadMessages() {
        int count = packets.Count;
        for (int i = 0; i < count; i++) {
            packets.LineBlockingDequeue(out ArraySegment<byte> packet);
            NetworkReceivedPacket(packet);
        }
    }

    protected override void SendReliable(ArraySegment<byte> packet) {
        if (networkManager.SubSystem.IsHost) {
            customCommsNetwork.GetCustomServer.EnqueuePacket(new CustomPeer(), packet);
        } else {
            UDPSubSystem.Client client = networkManager.Client;
            byte localHeaderSize = UDPSubSystem.PUBLIC_CONSTANTS.PACKET.SIZE.OPCODE + UDPSubSystem.PUBLIC_CONSTANTS.DATA_TYPE.SIZE.USHORT;
            byte[] data = new byte[localHeaderSize + packet.Count]; //Remove GC later
            byte[] opcode = OpcodesHost.GetOpcode(OpcodesHost.Opcodes.R_DissonanceMicrophone);
            byte[] length = BitConverter.GetBytes((ushort)packet.Count); //Remove bitconverter later

            data[0] = opcode[0];
            data[1] = opcode[1];
            data[2] = length[0];
            data[3] = length[1];

            int count = packet.Count;
            for (int i = 0; i < count; i++) {
                data[i + localHeaderSize] = packet[i];
            }

            client.WriteData(client.connections[0], data);
        }
    }

    protected override void SendUnreliable(ArraySegment<byte> packet) {
        if (networkManager.SubSystem.IsHost) {
            customCommsNetwork.GetCustomServer.EnqueuePacket(new CustomPeer(), packet);
        } else {
            UDPSubSystem.Client client = networkManager.Client;
            byte localHeaderSize = UDPSubSystem.PUBLIC_CONSTANTS.PACKET.SIZE.OPCODE + UDPSubSystem.PUBLIC_CONSTANTS.DATA_TYPE.SIZE.USHORT;
            byte[] data = new byte[localHeaderSize + packet.Count]; //Remove GC later
            byte[] opcode = OpcodesHost.GetOpcode(OpcodesHost.Opcodes.U_DissonanceMicrophone);
            byte[] length = BitConverter.GetBytes((ushort)packet.Count); //Remove bitconverter later

            data[0] = opcode[0];
            data[1] = opcode[1];
            data[2] = length[0];
            data[3] = length[1];

            int count = packet.Count;
            for (int i = 0; i < count; i++) {
                data[i + localHeaderSize] = packet[i];
            }

            client.WriteData(client.connections[0], data);        
        }
    }

}

public struct CustomPeer : IEquatable<CustomPeer> {
    public IPEndPoint ipEndPoint;
    public SteamworksNetworking.Connection steamConnection;
    public CustomPeer(IPEndPoint ipEndPoint, SteamworksNetworking.Connection steamConnection) {
        this.ipEndPoint = ipEndPoint;
        this.steamConnection = steamConnection;
    }
    public bool Equals(CustomPeer other) {
        return
            Equals(ipEndPoint, other.ipEndPoint) &&
            steamConnection.connection == other.steamConnection.connection &&
            EqualityComparer<User>.Default.Equals(steamConnection.user, other.steamConnection.user);
    }

    public override bool Equals(object obj) {
        return obj is CustomPeer other && Equals(other);
    }

    public override int GetHashCode() {
        return HashCode.Combine(ipEndPoint, steamConnection.connection, steamConnection.user);
    }

    public override string ToString() {
        return $"IPEndpoint[{ipEndPoint}, Steam Connection={steamConnection.connection}, Steam User={steamConnection.user}]";
    }
}

public struct CustomServerParam {}
public struct CustomClientParam {}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions