com.unity.netcode.gameobjects icon indicating copy to clipboard operation
com.unity.netcode.gameobjects copied to clipboard

Cannot build MLAPI WebGL project

Open yeOldeNoob opened this issue 4 years ago • 11 comments

Describe the bug After adding MLAPI to my project, WebGL builds fail.

To Reproduce Steps to reproduce the behavior:

  1. Create a new Unity project (3D).
  2. Add the MLAPI project via the package manager (git).
  3. File->Build Settings->WebGL->Switch Platform->Build

Expected behavior Project builds successfully.

Screenshots See below.

Environment (please complete the following information):

  • OS: Windows 10
  • Unity Version: 2019.4.21f1 Personal
  • MLAPI Version: 0.10.0
  • MLAPI Commit:https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/commit/3e3aef6aa02c2a25359898319e5bd49d3518b57b

Additional context Errors (for searchability):

Library\PackageCache\com.unity.multiplayer.mlapi@3e3aef6aa0\Runtime\Transports\UNET\RelayTransport.cs(251,74): error CS0117: 'NetworkTransport' does not contain a definition for 'QueueMessageForSending'
Library\PackageCache\com.unity.multiplayer.mlapi@3e3aef6aa0\Runtime\Transports\UNET\RelayTransport.cs(266,60): error CS0117: 'NetworkTransport' does not contain a definition for 'QueueMessageForSending'
Library\PackageCache\com.unity.multiplayer.mlapi@3e3aef6aa0\Runtime\Transports\UNET\RelayTransport.cs(271,74): error CS0117: 'NetworkTransport' does not contain a definition for 'SendQueuedMessages'
Library\PackageCache\com.unity.multiplayer.mlapi@3e3aef6aa0\Runtime\Transports\UNET\RelayTransport.cs(273,60): error CS0117: 'NetworkTransport' does not contain a definition for 'SendQueuedMessages'
Error building Player because scripts had compiler errors
Build completed with a result of 'Failed' in 1 seconds (1391 ms)
UnityEngine.GUIUtility:ProcessEvent (int,intptr)
UnityEditor.BuildPlayerWindow+BuildMethodException: 5 errors
  at UnityEditor.BuildPlayerWindow+DefaultBuildMethods.BuildPlayer (UnityEditor.BuildPlayerOptions options) [0x002bf] in <208995a09fe148f5a6f6c571838c154f>:0 
  at UnityEditor.BuildPlayerWindow.CallBuildMethods (System.Boolean askForBuildLocation, UnityEditor.BuildOptions defaultBuildOptions) [0x00080] in <208995a09fe148f5a6f6c571838c154f>:0 
UnityEngine.GUIUtility:ProcessEvent (int,intptr)

yeOldeNoob avatar Apr 09 '21 18:04 yeOldeNoob

mlapi_webgl_error

yeOldeNoob avatar Apr 09 '21 18:04 yeOldeNoob

https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/blob/3e3aef6aa02c2a25359898319e5bd49d3518b57b/com.unity.multiplayer.mlapi/Runtime/Transports/UNET/RelayTransport.cs#L251 https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/blob/3e3aef6aa02c2a25359898319e5bd49d3518b57b/com.unity.multiplayer.mlapi/Runtime/Transports/UNET/RelayTransport.cs#L266 https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/blob/3e3aef6aa02c2a25359898319e5bd49d3518b57b/com.unity.multiplayer.mlapi/Runtime/Transports/UNET/RelayTransport.cs#L271 https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/blob/3e3aef6aa02c2a25359898319e5bd49d3518b57b/com.unity.multiplayer.mlapi/Runtime/Transports/UNET/RelayTransport.cs#L273

These function calls are obsolete: https://docs.unity3d.com/2019.4/Documentation/ScriptReference/Networking.NetworkTransport.QueueMessageForSending.html https://docs.unity3d.com/2019.4/Documentation/ScriptReference/Networking.NetworkTransport.SendQueuedMessages.html

yeOldeNoob avatar Apr 09 '21 18:04 yeOldeNoob

I'm still having this issue, any updates on this?

meruiden avatar Jul 13 '21 12:07 meruiden

Hi, is there a possibility you could create a release version with this commit added only? This is a pretty important bug and I should not have to use the development branch to compile for WebGL.

notcat avatar Aug 06 '21 20:08 notcat

Same problem, with websocket transport (https://github.com/Unity-Technologies/mlapi-community-contributions/tree/main/Transports/com.mlapi.contrib.transport.websocket) dont work.

Saltant avatar Aug 13 '21 21:08 Saltant

Same error here, any suggestions or workarounds?

crioseth avatar Aug 18 '21 13:08 crioseth

Same error here, any suggestions or workarounds?

Just remove broken code from source (SendQueuedMessages, QueueMessageForSending) i dont know why mlapi devs is sleep, but remove code and problem goned.

Saltant avatar Aug 18 '21 14:08 Saltant

//Ctrl+A the entire of UNetTransport and paste in code below

#pragma warning disable 618 #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Collections.Generic; using MLAPI.Exceptions; using MLAPI.Logging; using MLAPI.Profiling; using MLAPI.Transports.Tasks; using UnityEngine.Networking;

namespace MLAPI.Transports.UNET { public class UNetTransport : NetworkTransport, ITransportProfilerData { public enum SendMode { Immediately, Queued }

    private static ProfilingDataStore s_TransportProfilerData = new ProfilingDataStore();
    public static bool ProfilerEnabled;

    // Inspector / settings
    public int MessageBufferSize = 1024 * 5;
    public int MaxConnections = 100;
    public int MaxSentMessageQueueSize = 128;

    public string ConnectAddress = "127.0.0.1";
    public int ConnectPort = 7777;
    public int ServerListenPort = 7777;
    public int ServerWebsocketListenPort = 8887;
    public bool SupportWebsocket = false;

    // user-definable channels.  To add your own channel, do something of the form:
    //  #define MY_CHANNEL 0
    //  ...
    //  transport.Channels.Add(
    //     new UNetChannel()
    //       {
    //         Id = Channel.ChannelUnused + MY_CHANNEL,  <<-- must offset from reserved channel offset in MLAPI SDK
    //         Type = QosType.Unreliable
    //       }
    //  );
    public List<UNetChannel> Channels = new List<UNetChannel>();

    // Relay
    public bool UseMLAPIRelay = false;
    public string MLAPIRelayAddress = "184.72.104.138";
    public int MLAPIRelayPort = 8888;

    public SendMode MessageSendMode = SendMode.Immediately;

    // Runtime / state
    private byte[] m_MessageBuffer;
    private WeakReference m_TemporaryBufferReference;

    // Lookup / translation
    private readonly Dictionary<NetworkChannel, int> m_ChannelNameToId = new Dictionary<NetworkChannel, int>();
    private readonly Dictionary<int, NetworkChannel> m_ChannelIdToName = new Dictionary<int, NetworkChannel>();
    private int m_ServerConnectionId;
    private int m_ServerHostId;

    private SocketTask m_ConnectTask;
    public override ulong ServerClientId => GetMLAPIClientId(0, 0, true);

    protected void LateUpdate()
    {
        if (UnityEngine.Networking.NetworkTransport.IsStarted && MessageSendMode == SendMode.Queued)
        {
            if (NetworkManager.Singleton.IsServer)
            {
                for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++)
                {
                    SendQueued(NetworkManager.Singleton.ConnectedClientsList[i].ClientId);
                }
            }
            else
            {
                SendQueued(NetworkManager.Singleton.LocalClientId);
            }
        }
    }

    public override void Send(ulong clientId, ArraySegment<byte> data, NetworkChannel networkChannel)
    {
        if (ProfilerEnabled)
        {
            s_TransportProfilerData.Increment(ProfilerConstants.NumberOfTransportSends);
        }

        GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);

        int channelId = 0;

        if (m_ChannelNameToId.ContainsKey(networkChannel))
        {
            channelId = m_ChannelNameToId[networkChannel];
        }
        else
        {
            channelId = m_ChannelNameToId[NetworkChannel.Internal];
        }

        byte[] buffer;

        if (data.Offset > 0)
        {
            // UNET cant handle this, do a copy

            if (m_MessageBuffer.Length >= data.Count)
            {
                buffer = m_MessageBuffer;
            }
            else
            {
                object bufferRef = null;
                if (m_TemporaryBufferReference != null && ((bufferRef = m_TemporaryBufferReference.Target) != null) && ((byte[])bufferRef).Length >= data.Count)
                {
                    buffer = (byte[])bufferRef;
                }
                else
                {
                    buffer = new byte[data.Count];
                    m_TemporaryBufferReference = new WeakReference(buffer);
                }
            }

            Buffer.BlockCopy(data.Array, data.Offset, buffer, 0, data.Count);
        }
        else
        {
            buffer = data.Array;
        }

        if (MessageSendMode == SendMode.Queued)
        {

#if !UNITY_WEBGL RelayTransport.QueueMessageForSending(hostId, connectionId, channelId, buffer, data.Count, out byte error); #endif } else { RelayTransport.Send(hostId, connectionId, channelId, buffer, data.Count, out byte error); } }

    public void SendQueued(ulong clientId)
    {
        if (ProfilerEnabled)
        {
            s_TransportProfilerData.Increment(ProfilerConstants.NumberOfTransportSendQueues);
        }

        GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);

#if !UNITY_WEBGL RelayTransport.SendQueuedMessages(hostId, connectionId, out byte error); #endif }

    public override NetworkEvent PollEvent(out ulong clientId, out NetworkChannel networkChannel, out ArraySegment<byte> payload, out float receiveTime)
    {
        var eventType = RelayTransport.Receive(out int hostId, out int connectionId, out int channelId, m_MessageBuffer, m_MessageBuffer.Length, out int receivedSize, out byte error);

        clientId = GetMLAPIClientId((byte)hostId, (ushort)connectionId, false);
        receiveTime = UnityEngine.Time.realtimeSinceStartup;

        var networkError = (NetworkError)error;
        if (networkError == NetworkError.MessageToLong)
        {
            byte[] tempBuffer;

            if (m_TemporaryBufferReference != null && m_TemporaryBufferReference.IsAlive && ((byte[])m_TemporaryBufferReference.Target).Length >= receivedSize)
            {
                tempBuffer = (byte[])m_TemporaryBufferReference.Target;
            }
            else
            {
                tempBuffer = new byte[receivedSize];
                m_TemporaryBufferReference = new WeakReference(tempBuffer);
            }

            eventType = RelayTransport.Receive(out hostId, out connectionId, out channelId, tempBuffer, tempBuffer.Length, out receivedSize, out error);
            payload = new ArraySegment<byte>(tempBuffer, 0, receivedSize);
        }
        else
        {
            payload = new ArraySegment<byte>(m_MessageBuffer, 0, receivedSize);
        }

        if (m_ChannelIdToName.ContainsKey(channelId))
        {
            networkChannel = m_ChannelIdToName[channelId];
        }
        else
        {
            networkChannel = NetworkChannel.Internal;
        }

        if (m_ConnectTask != null && hostId == m_ServerHostId && connectionId == m_ServerConnectionId)
        {
            if (eventType == NetworkEventType.ConnectEvent)
            {
                // We just got a response to our connect request.
                m_ConnectTask.Message = null;
                m_ConnectTask.SocketError = networkError == NetworkError.Ok ? System.Net.Sockets.SocketError.Success : System.Net.Sockets.SocketError.SocketError;
                m_ConnectTask.State = null;
                m_ConnectTask.Success = networkError == NetworkError.Ok;
                m_ConnectTask.TransportCode = (byte)networkError;
                m_ConnectTask.TransportException = null;
                m_ConnectTask.IsDone = true;

                m_ConnectTask = null;
            }
            else if (eventType == NetworkEventType.DisconnectEvent)
            {
                // We just got a response to our connect request.
                m_ConnectTask.Message = null;
                m_ConnectTask.SocketError = System.Net.Sockets.SocketError.SocketError;
                m_ConnectTask.State = null;
                m_ConnectTask.Success = false;
                m_ConnectTask.TransportCode = (byte)networkError;
                m_ConnectTask.TransportException = null;
                m_ConnectTask.IsDone = true;

                m_ConnectTask = null;
            }
        }

        if (networkError == NetworkError.Timeout)
        {
            // In UNET. Timeouts are not disconnects. We have to translate that here.
            eventType = NetworkEventType.DisconnectEvent;
        }

        // Translate NetworkEventType to NetEventType
        switch (eventType)
        {
            case NetworkEventType.DataEvent:
                return NetworkEvent.Data;
            case NetworkEventType.ConnectEvent:
                return NetworkEvent.Connect;
            case NetworkEventType.DisconnectEvent:
                return NetworkEvent.Disconnect;
            case NetworkEventType.Nothing:
                return NetworkEvent.Nothing;
            case NetworkEventType.BroadcastEvent:
                return NetworkEvent.Nothing;
        }

        return NetworkEvent.Nothing;
    }

    public override SocketTasks StartClient()
    {
        var socketTask = SocketTask.Working;

        m_ServerHostId = RelayTransport.AddHost(new HostTopology(GetConfig(), 1), false);
        m_ServerConnectionId = RelayTransport.Connect(m_ServerHostId, ConnectAddress, ConnectPort, 0, out byte error);

        var connectError = (NetworkError)error;

        switch (connectError)
        {
            case NetworkError.Ok:
                socketTask.Success = true;
                socketTask.TransportCode = error;
                socketTask.SocketError = System.Net.Sockets.SocketError.Success;
                socketTask.IsDone = false;

                // We want to continue to wait for the successful connect
                m_ConnectTask = socketTask;
                break;
            default:
                socketTask.Success = false;
                socketTask.TransportCode = error;
                socketTask.SocketError = System.Net.Sockets.SocketError.SocketError;
                socketTask.IsDone = true;
                break;
        }

        return socketTask.AsTasks();
    }

    public override SocketTasks StartServer()
    {
        var topology = new HostTopology(GetConfig(), MaxConnections);

        if (SupportWebsocket)
        {
            if (!UseMLAPIRelay)
            {
                int websocketHostId = UnityEngine.Networking.NetworkTransport.AddWebsocketHost(topology, ServerWebsocketListenPort);
            }
            else
            {
                if (NetworkLog.CurrentLogLevel <= LogLevel.Error) NetworkLog.LogError("Cannot create websocket host when using MLAPI relay");
            }
        }

        int normalHostId = RelayTransport.AddHost(topology, ServerListenPort, true);

        return SocketTask.Done.AsTasks();
    }

    public override void DisconnectRemoteClient(ulong clientId)
    {
        GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);

        RelayTransport.Disconnect((int)hostId, (int)connectionId, out byte error);
    }

    public override void DisconnectLocalClient()
    {
        RelayTransport.Disconnect(m_ServerHostId, m_ServerConnectionId, out byte error);
    }

    public override ulong GetCurrentRtt(ulong clientId)
    {
        GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);

        if (UseMLAPIRelay)
        {
            return 0;
        }
        else
        {
            return (ulong)UnityEngine.Networking.NetworkTransport.GetCurrentRTT((int)hostId, (int)connectionId, out byte error);
        }
    }

    public override void Shutdown()
    {
        m_ChannelIdToName.Clear();
        m_ChannelNameToId.Clear();
        UnityEngine.Networking.NetworkTransport.Shutdown();
    }

    public override void Init()
    {
        UpdateRelay();

        m_MessageBuffer = new byte[MessageBufferSize];

        s_TransportProfilerData.Clear();

        UnityEngine.Networking.NetworkTransport.Init();
    }

    public ulong GetMLAPIClientId(byte hostId, ushort connectionId, bool isServer)
    {
        if (isServer)
        {
            return 0;
        }
        else
        {
            return ((ulong)connectionId | (ulong)hostId << 16) + 1;
        }
    }

    public void GetUNetConnectionDetails(ulong clientId, out byte hostId, out ushort connectionId)
    {
        if (clientId == 0)
        {
            hostId = (byte)m_ServerHostId;
            connectionId = (ushort)m_ServerConnectionId;
        }
        else
        {
            hostId = (byte)((clientId - 1) >> 16);
            connectionId = (ushort)((clientId - 1));
        }
    }

    public ConnectionConfig GetConfig()
    {
        var connectionConfig = new ConnectionConfig();

        // MLAPI built-in channels
        for (int i = 0; i < MLAPI_CHANNELS.Length; i++)
        {
            int channelId = AddMLAPIChannel(MLAPI_CHANNELS[i].Delivery, connectionConfig);

            m_ChannelIdToName.Add(channelId, MLAPI_CHANNELS[i].Channel);
            m_ChannelNameToId.Add(MLAPI_CHANNELS[i].Channel, channelId);
        }

        // Custom user-added channels
        for (int i = 0; i < Channels.Count; i++)
        {
            int channelId = AddUNETChannel(Channels[i].Type, connectionConfig);

            if (m_ChannelNameToId.ContainsKey(Channels[i].Id))
            {
                throw new InvalidChannelException($"Channel {channelId} already exists");
            }

            m_ChannelIdToName.Add(channelId, Channels[i].Id);
            m_ChannelNameToId.Add(Channels[i].Id, channelId);
        }

        connectionConfig.MaxSentMessageQueueSize = (ushort)MaxSentMessageQueueSize;

        return connectionConfig;
    }

    public int AddMLAPIChannel(NetworkDelivery type, ConnectionConfig config)
    {
        switch (type)
        {
            case NetworkDelivery.Unreliable:
                return config.AddChannel(QosType.Unreliable);
            case NetworkDelivery.Reliable:
                return config.AddChannel(QosType.Reliable);
            case NetworkDelivery.ReliableSequenced:
                return config.AddChannel(QosType.ReliableSequenced);
            case NetworkDelivery.ReliableFragmentedSequenced:
                return config.AddChannel(QosType.ReliableFragmentedSequenced);
            case NetworkDelivery.UnreliableSequenced:
                return config.AddChannel(QosType.UnreliableSequenced);
        }

        return 0;
    }

    public int AddUNETChannel(QosType type, ConnectionConfig config)
    {
        switch (type)
        {
            case QosType.Unreliable:
                return config.AddChannel(QosType.Unreliable);
            case QosType.UnreliableFragmented:
                return config.AddChannel(QosType.UnreliableFragmented);
            case QosType.UnreliableSequenced:
                return config.AddChannel(QosType.UnreliableSequenced);
            case QosType.Reliable:
                return config.AddChannel(QosType.Reliable);
            case QosType.ReliableFragmented:
                return config.AddChannel(QosType.ReliableFragmented);
            case QosType.ReliableSequenced:
                return config.AddChannel(QosType.ReliableSequenced);
            case QosType.StateUpdate:
                return config.AddChannel(QosType.StateUpdate);
            case QosType.ReliableStateUpdate:
                return config.AddChannel(QosType.ReliableStateUpdate);
            case QosType.AllCostDelivery:
                return config.AddChannel(QosType.AllCostDelivery);
            case QosType.UnreliableFragmentedSequenced:
                return config.AddChannel(QosType.UnreliableFragmentedSequenced);
            case QosType.ReliableFragmentedSequenced:
                return config.AddChannel(QosType.ReliableFragmentedSequenced);
        }

        return 0;
    }

    private void UpdateRelay()
    {
        RelayTransport.Enabled = UseMLAPIRelay;
        RelayTransport.RelayAddress = MLAPIRelayAddress;
        RelayTransport.RelayPort = (ushort)MLAPIRelayPort;
    }

    public void BeginNewTick()
    {
        s_TransportProfilerData.Clear();
    }

    public IReadOnlyDictionary<string, int> GetTransportProfilerData()
    {
        return s_TransportProfilerData.GetReadonly();
    }
}

} #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member #pragma warning restore 618

Awhesian avatar Nov 23 '21 00:11 Awhesian

Unity-Technologies/multiplayer-community-contributions#136 Since the last version, this is unusable on WebGL

theomonnom avatar Mar 10 '22 00:03 theomonnom

Tracked in our backlog MTT-3061

ashwinimurt avatar Mar 30 '22 14:03 ashwinimurt

Does not work with recent NGO. Linking duplicate issue: https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues/1962 and Tracked in our backlog MTT-4088

ashwinimurt avatar Aug 01 '22 23:08 ashwinimurt

Netcode for GameObjects building on WebGL is now possible, after https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/pull/2199

jeffreyrainy avatar Sep 19 '22 17:09 jeffreyrainy