#SendQueue in Server INetworkInterface always empty (Custom INetworkStreamDriverConstructor)

1 messages · Page 1 of 1 (latest)

gray escarp
#

Hey there,

I'm trying to create my own INetworkInterfaces for Netcode for Entities. To get Netcode to use them, I do

NetworkStreamReceiveSystem.DriverConstructor = new NetworkStreamDriverConstructor();

before the usual stuff:

var server = ClientServerBootstrap.CreateServerWorld("ServerWorld");
var client = ClientServerBootstrap.CreateClientWorld("ClientWorld");

DestroyLocalSimulationWorld();
World.DefaultGameObjectInjectionWorld ??= server;
SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);

var serverQuery = server.EntityManager.CreateEntityQuery(ComponentType.ReadWrite<NetworkStreamDriver>());
serverQuery.GetSingletonRW<NetworkStreamDriver>().ValueRW.Listen(serverEP);

var clientQuery = client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite<NetworkStreamDriver>());
clientQuery.GetSingletonRW<NetworkStreamDriver>().ValueRW.Connect(client.EntityManager, clientEP);

Behind the scenes, I'm already connected to one local client and one remote client by that point, and my custom INetworkInterfaces are going to use those connections.

I can see that for server and client the respective INetworkInterfaces are having their Bind() called and shortly after their ScheduleSend() and ScheduleReceive().

Both the local client as well as the remote client send 13 byte messages ending in UTP (or in byte values [85][84][80][1]) to the server. That happens in the clients' ScheduleSend() and gets all the way propagated to the server's ScheduleReceive() where I handle the messages like this:

if (!receiveQueue.EnqueuePacket(out var packetProcessor))
   break;
packetProcessor.EndpointRef = IndexToEndpoint[msg.SenderIndex];
unsafe
{
    fixed (byte* ptr = msg.PacketData)
    {
        packetProcessor.AppendToPayload(ptr, msg.PacketData.Length);
    }
}

But then nothing happens. The server's SendQueue in ScheduleSend() is always empty. Is there something else I need to do to kickstart the process? I've been trying for days now and I'm all out of ideas. 😣

gray escarp
#

I suspect it has something to do with the NetworkStreamReceiveSystem. For some reason the Server's Driver doesn't get its Accept() called. But I don't know why frustration

wise epoch
#

In the client schedule send, what does your packet send system look like. Are you using an external network stack (Epic, Steamworks, etc) or your own socket implementation?

gray escarp
# wise epoch In the client schedule send, what does your packet send system look like. Are yo...

I'm sending it via a WebRTC connection. I have a reference to that in the NetworkInterface that I pass the packet payload and length to. Also, I can see it arrive in the Server's ScheduleReceive(). So at least in that direction it seems to work. The issue is that the server doesn't schedule anything to send.
When I look at my ServerWorld, I can see the NetworkStreamDriver entity, but I don't have a NetworkStreamConnection nor a NetworkId. Those should get added in the NetworkStreamReceiveSystem via the ConnectionAcceptJob. But for some reason that isn't happening. The docs say "Connections must be accepted before data can be sent on them.". So that is most likely the reason why the server interface doesn't schedule anything to send.

wise epoch
#

Server does not send anything until network handshake completes and JoinInGame (or whatever that component is called) has been added to the connection entity

#

Client will use SimpleConnectionLayer to repeatedly send open connection requests on it's send queue and the server will receive and process them using its own simple connection layer. From there, a corresponding connection entity will be created (client creates their connection entity on .Connect() which is why that call needs entity manager piped in).

gray escarp
#

But the connection entity isn't being created...

wise epoch
#

On the client or server?

gray escarp
#

On the server (but probably also not on the client).

wise epoch
#

The client should always have a connection entity, that's created manually on the Connect() call.

#

The server will create one when the connection is accepted.

#

Server will not send any data until connection is established

gray escarp
wise epoch
#

You say that something is received by the server. Clients will repeatedly send a handshake request to their designated endpoint (whatever clientEP is) and the server, once it receives one, will process a new connection automatically

#

Does your server receive queue have data in it?

gray escarp
#

Yes.

#

It receives 13 byte long packages ending in UTP (or in byte values [85][84][80][1]). Those are probably the requests you mentioned.

wise epoch
#

Hrm, give me a sec. I never actually looked at what the packet for connection contains.

#

Have you stuck a debug breakpoint into the simple connection layer file and see what it is reading?

#

Actually give me longer than a sec, my netcode is in a million pieces as I'm reconfiguring some logic. I don't think I can put this together again just to see what connection is reading from. It works automatically if you're properly sending packets

gray escarp
wise epoch
#

When I was making my own custom network interface to work with Epic Online Services and Steamworks, I spent hours going line by line through SimpleConnectionLayer to figure out what is going on and what I needed to do.

It's very self contained, I didn't need to touch it at all. So long as my interface can send packets, unity handles the rest. It's a beauty of isolated modules and minimal spaghetti code.

gray escarp