Cannot build MLAPI WebGL project
Describe the bug After adding MLAPI to my project, WebGL builds fail.
To Reproduce Steps to reproduce the behavior:
- Create a new Unity project (3D).
- Add the MLAPI project via the package manager (git).
- 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)

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
I'm still having this issue, any updates on this?
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.
Same problem, with websocket transport (https://github.com/Unity-Technologies/mlapi-community-contributions/tree/main/Transports/com.mlapi.contrib.transport.websocket) dont work.
Same error here, any suggestions or workarounds?
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.
//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
Unity-Technologies/multiplayer-community-contributions#136 Since the last version, this is unusable on WebGL
Tracked in our backlog MTT-3061
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
Netcode for GameObjects building on WebGL is now possible, after https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/pull/2199