bugfix(network): Move transport socket bind into separate method#1478
bugfix(network): Move transport socket bind into separate method#1478slurmlord wants to merge 7 commits intoTheSuperHackers:mainfrom
Conversation
|
I have never encountered this issue and I use multiple instances frequently, usually 2, 3 or 4 instances. |
|
I see - I'm running it via Wine (v10.12) which is probably the source of the difference in behavior. |
|
I don't know if it has any drawbacks when running Windows natively. If this is a common issue for Wine then it seems worth fixing, but maybe behind a macro and with a comment that this is primarily for Wine. |
xezon
left a comment
There was a problem hiding this comment.
ASSERTION FAILURE: Could not bind to 0x00000000:8086
Why is it binding to IP 0? How does this come to be? The clients should use 127.0.0.N, selectable in the Options Menu.
You mean a macro to at run-time detect if running on Wine? I can look into that. |
I can trace some more into this, perhaps there's something missing from the configuration somehow. But at the same time, games can be created, joined and played so it's at least mostly working. |
|
Looking into the ordering of things, it became apparent that the binding to IP 0.0.0.0 happens because LANAPI::init calls I propose a fix in the latest iteration, where the initialization of m_transport is removed from init, and kept in SetLocalIP. The only calls to LANAPI::init I found are from LanLobbyMenuInit and NetworkDirectConnectInit. Both call LANAPI::SetLocalIP directly after LANAPI::init, so the effective behavior should be the same except the bind to 0.0.0.0. There is some strangeness with LANAPI going on in the beginning of NetworkDirectConnectInit where it's created (if not already created) and deleted before being re-created again, but I didn't notice any difference in behavior here with the modified init. Does this seem like a viable approach? If so, I'll update the title and description. |
|
Needs some testing then to see if networking still works :) |
|
So after discussing with @tomsons26, we landed on the underlying issue being that Transport binds its socket in the Transport::init method. Since LANAPI::init calls Transport::init before having an IP assigned yet, it results in binding to 0x0. The solution is to split the socket binding out to a separate method in Transport so that LANAPI can init the Transport instance, but wait with the binding until LANAPI::SetLocalIP is called. Validated to work for network game with both local multi-instance game and with 2 separate hosts over a physical network (all through Wine). |
| } | ||
|
|
||
| m_port = port; | ||
| Bool Transport::bind(void) |
There was a problem hiding this comment.
You can potentially reduce the diff complexity by moving this function above clearBuffers
| DEBUG_LOG(("NAT::attachSlotList - using %d as the starting port number", m_startingPortNumber)); | ||
| generatePortNumbers(slotList, localSlot); | ||
| m_transport->init(m_localIP, getSlotPort(localSlot)); | ||
| m_transport->bind(); |
There was a problem hiding this comment.
In LANAPI::SetLocalIP it calls bind only if init succeeded. Here it does not do that. Why?
There was a problem hiding this comment.
Agreed - that pattern should be followed here, too.
| @@ -1258,6 +1257,10 @@ Bool LANAPI::SetLocalIP( UnsignedInt localIP ) | |||
|
|
|||
| m_transport->reset(); | |||
| retval = m_transport->init(m_localIP, lobbyPort); | |||
| } | ||
|
|
||
| clearBuffers(); | ||
| } |
| @@ -102,7 +102,6 @@ void LANAPI::init( void ) | |||
| m_gameStartSeconds = 0; | |||
| m_transport->reset(); | |||
| m_transport->init(m_localIP, lobbyPort); | |||
There was a problem hiding this comment.
Is it even necessary to call init here then? Perhaps the simplest fix is to just remove
m_transport->init(m_localIP, lobbyPort);
m_transport->allowBroadcasts(true);?
There was a problem hiding this comment.
If removing the above 2 lines is enough to fix the issue, then I would recommend to do that and then do a follow up refactor that splits init function into bind and clearBuffers like you have, but call the new functions in init right away.
There was a problem hiding this comment.
So in practice go with just commit
ee4eb87 for this ? I think that change was the one that raised some concerns that the initialization of the LANAPI instance was expected to initialize all dependencies as well.
From the outside the behavior is the same; no bind is being done to 0x0. If this is acceptable I'll update the PR to just what was in ee4eb87.
There was a problem hiding this comment.
ee4eb87 looks to be correct. Perhaps reinforce it by placing some asserts to verify that the transport is initialized before use.
|
|
||
| Bool LANAPI::SetLocalIP( UnsignedInt localIP ) | ||
| { | ||
| DEBUG_ASSERTCRASH(m_currentGame == NULL && m_gameStartTime == 0, ("LANAPI::SetLocalIP called while in a game, should be called right after LANAPI::init")); |
There was a problem hiding this comment.
This is an odd assert because it will not protect from the error of not calling this function at all.
Better add a Transport::isInitialized function and assert on that in all relevant functions that use LANAPI::m_transport
|
Abandoning this PR as the main goal was to avoid an assert when running under Wine/Linux, but the networking behavior with regards to broadcasts is different enough that LAN play does not work as expected anyway. The issue does not show up under Windows so leaving the code as is won't have any bad/worse consequences. |
This change removes the call to Transport::init from LANAPI::init, which avoids an unnecessary socket bind to IP 0x0 (INADDR_ANY). Correctness is maintained by Transport::init still being called from LANAPI::SetLocalIP.
Background
When launching multiple instances of the game in Wine, an assertion will trigger for all instances except the first one when navigating to the Network menu:
This is caused by LANAPI::init calling Transport::init, which will bind to IP address 0x0, the same as INADDR_ANY; all interfaces. This is due to LANAPI not having set the local IP address (m_localIP) that Transport should use, and instead using the default value of 0.
When LANAPI::SetLocalIP is later called, the local IP is assigned and Transport::init called again, which will bind to the same port as previous but on one specific IP. On Wine/Linux this results in the assertion above, as the port is still considered in use/unavailable for a longer period than on Windows.
By avoiding the init/bind from LANAPI::init, binding will only be done from LANAPI::SetLocalIP, where the proper address is used.
Validated by running a multi instance network game.