Monday, November 17, 2014

Steam C# Wrapper Steamworks.NET Unity

I got Steamworks.NET and C# wrapping the Steamworks C++ library to work alot.

The code is in a standalone dedicated server, but als should work in Unity3D with Unity networking disabled.  Our goal is to be on Steam networking instead of the Unity native RakNet based networking.

This lets us have a dedicated (no GUI, no Untiy) server on Linux, Win, Mac.

So here is the key that was not immediately obvious about the Steam libraries.

There is teh 'normal' SteamUser, SteamNetworking set of calls and callbacks.  They let you do things like SendP2PPacket, and IsP2PPacketAvailable calls to send packets.  I also make a lobby with SteamLobby.Create.

Gotcha #1
Callbacks MUST keep a reference when made or they get deleted. 
So the code...

Callback< SteamServersConnected_t >.CreateGameServer(OnSteamServersConnected);

is deadly.  The callback gets made and then soon gets deleted as the garbage collector in C# sees it has no reference.  Instead make a reference in your class and then the code reads...

SteamServersConnectedCB = Callback< SteamServersConnected_t >.CreateGameServer(OnSteamServersConnected);

Gotcha #2
Steam has two sets of callback and message and event queues.  On for the client use and one for a server if there is one.  I can't stress this enough.  The code...

SteamGameServerNetworking.IsP2PPacketAvailable(out sz, 0)

is very different from 

SteamNetworking.IsP2PPacketAvailable(out sz, 0)

The first one is asking if the GameServer you made has incomming packets, and the second one asks if the local SteamUser.GetSteamID() user has incomming packets.  The second one is the local player.

Same for all the callbacks.

 P2PSessionRequestCB = Callback< P2PSessionRequest_t >.Create(OnP2PSessionRequest);

 P2PSessionRequestServerCB = Callback< P2PSessionRequest_t >.CreateGameServer(OnP2PSessionRequestServer);

Are completely dioffernet events.  The first is when the CLIENT receives incomming connections, and the second is when the SERVER gets data.

And the implementations are different....

    void OnP2PSessionRequest(P2PSessionRequest_t s)
    {
        Debug.Log("OnP2PSessionRequest: " + s.m_steamIDRemote);
        SteamNetworking.AcceptP2PSessionWithUser(s.m_steamIDRemote);
    }
       
    void OnP2PSessionRequestServer(P2PSessionRequest_t s)
    {
        Debug.Log("OnP2PSessionRequestServer: " + s.m_steamIDRemote);
        SteamGameServerNetworking.AcceptP2PSessionWithUser(s.m_steamIDRemote);
    }