Skip to content

Conversation

@otavepto
Copy link
Contributor

@otavepto otavepto commented Nov 22, 2025

Fixes and closes #380

  • Add a missing mutex lock in P2p_Manager::send_packet() when sending a network message, this is why the game was crashing constantly at Steam_Networking::SendP2PPacket()

  • Return true in Steam_UGC::BInitWorkshopForGameServer() since the emu supports workshop/mods via the UGC interface.
    This also allows the dedicated server (appid 298740) to work out of the box without the need to patch/modify its files.

@otavepto otavepto marked this pull request as draft November 22, 2025 14:05
@otavepto
Copy link
Contributor Author

otavepto commented Nov 22, 2025

I faced this same problem with the overlay before, the core issue is that the periodic and networking runnables always lock the global_mutex (callers lock it).
So inside the callback, you are already locking a mutex which you don't need necessarily.

And here's how it can easily deadlock:
1- A game calls for example SendP2PPacket
---> p2p_mutex is locked by main thread
2- In the meantime, the background thread wakes up and triggers the networking runnable
---> global_mutex is locked by background thread


3- Back in the main thread where SendP2PPacket is running, and after preparing the object to send, the function attempts to lock global_mutex before calling network->sendTo(...) but now that won't be possible
---> Main thread is halted for now
4- Back in our background thread, the networking callback needs to store the packet, so it attempts to lock the p2p_mutexbut now that won't be possible
---> Background thread is halted for now

Both threads are now deadlocked.
To solve this issues:
1- We have to ensure that mutex locking always happens in the same order
2- Or, avoid the global mutex lock everywhere, only when needed

The first solution is really ugly, because you have to manually trace the call order of your functions and future edits become a huge burden.

The second solution is just not feasible nor practical, because a lot of the functionality will break, and also requires a ton of edits everywhere.

The way I solved hacked it in the overlay, is by doing a "best possible effort", in other words, if we can safely lock both mutexes then proceed as normal, otherwise, ignore this callback!

// don't wait to lock the overlay mutex
// * the overlay proc might be active and holding the overlay mutex
// * this steam callback will be blocked, but it has the global mutex locked
// * the overlay proc tries to lock the global mutex, but since we have it, it will be blocked forever
if (overlay_mutex.try_lock()) {
if (Ready()) {
// ==============================================================
// call steam callbacks that has to change the overlay state here
// ==============================================================
steam_run_callback_friends_actions();
}
overlay_mutex.unlock();
}
}

A third solution, which I don't know how to implement at the moment, is to keep everything steam-related in a separate collection than the networking ones, so for example a separate std::vector or std::map for connections, but that's seems not possible to me because at some point we have to interact with these collections within the context of networking and steam at the same time.

@otavepto otavepto marked this pull request as ready for review November 22, 2025 17:51
@Detanup01
Copy link
Owner

Looks good to me but just bc more eye can see more

@Detanup01 Detanup01 merged commit e88797f into Detanup01:dev Nov 23, 2025
64 checks passed
@otavepto otavepto deleted the patch/p2pt branch November 23, 2025 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Regression] Space Engineers stuck loading server after 2025_09_13

3 participants