I'm working on my multiplayer game (with FacepunchStudio implementation) on Unity, and I have a weird behaviour with my unmarshalling.
For send data between players, I use the 'SteamSocketManager' and I need to use Marshal. So, I have created 2 struct:
public struct Data
{
public TypeInfo typeData;
public IntPtr data;
}
public struct PlayerInfo
{
public uint idNetwork;
public CommandType typeCommand;
public Vector3 position, rotation;
public float xAnimator, yAnimator;
}
PlayerInfo: it's for all player information.
Data: it's to make a generic structure for all data between the server and clients.
When a player is connected to the socket, the server sends a request for creating the player locally and create a 'remote' of the player to the other players.
public override void OnConnected(Connection connection, ConnectionInfo data)
{
base.OnConnected(connection, data);
Debug.Log($"{data.Identity} has joined the game");
// Prepare the player experience
uint idUser = connection.Id;
Vector3 playerPosition = new Vector3(UnityEngine.Random.Range(-area, area), 1, UnityEngine.Random.Range(area, area));
Debug.Log($"new pos: {playerPosition}");
// @ 1 > Create a new PlayerInfo for the server
PlayerInfo infoPlayer = new PlayerInfo()
{
idNetwork = idUser,
typeCommand = CommandType.Remote,
position = playerPosition,
rotation = new Vector3(0,0,0),
xAnimator = 0,
yAnimator = 0
};
players.Add(idUser, infoPlayer);
int sizePlayerInformation = Marshal.SizeOf(infoPlayer);
// @ 2 > Send the 'Remote' PlayerInfo to the other players
int sizeMarcel = Marshal.SizeOf(infoPlayer);
IntPtr infoRemotePtr = Marshal.AllocHGlobal(sizeMarcel);
Marshal.StructureToPtr(infoPlayer, infoRemotePtr, false);
Data remoteD = new Data
{
typeData = TypeInfo.PlayerInfo,
data = infoRemotePtr
};
IntPtr dataRemote = Marshal.AllocHGlobal(Marshal.SizeOf(remoteD));
Marshal.StructureToPtr(remoteD, dataRemote, false);
SteamManager.Instance.RelaySocketMessageReceived(dataRemote, Marshal.SizeOf(remoteD), idUser);
// @ 3 > Send the 'Create' Player to the new player
infoPlayer.typeCommand = CommandType.Create;
int sizeCreatePlayerData = Marshal.SizeOf(infoPlayer);
IntPtr infoCompressed = Marshal.AllocHGlobal(sizeCreatePlayerData);
Marshal.StructureToPtr(infoPlayer, infoCompressed, false);
Data dataInfoPlayer = new Data
{
typeData = TypeInfo.PlayerInfo,
data = infoCompressed
};
IntPtr dataCreate = Marshal.AllocHGlobal(Marshal.SizeOf(dataInfoPlayer));
Marshal.StructureToPtr(dataInfoPlayer, dataCreate, false);
Result sc = connection.SendMessage(dataCreate, Marshal.SizeOf(dataInfoPlayer), SendType.Reliable);
if (sc != Result.OK)
{
Result retry = connection.SendMessage(dataCreate, Marshal.SizeOf(dataInfoPlayer), SendType.Reliable);
if (retry != Result.OK) Debug.LogError("EET BRO, CA MARCHE PAS KENNY");
}
Debug.Log(dataCreate);
}
And when the player receives a new message from the server, this message is unmarshall by this
Method: Data data = MultiPlayerSocket.UncompressData(messageIntPtr);
After, I check if the type of the data is a 'PlayerInfo'.
If is true: a unmarshall this data into a PlayerInfo struct, and a execute all actions wanted.
public void ProcessMessageFromSocketServer(IntPtr messageIntPtr, int dataBlockSize)
{
try
{
// Data decompressing
print($"<< DECOMPRESS +{messageIntPtr}");
Data data = MultiPlayerSocket.UncompressData(messageIntPtr);
print("data.data = " + data.data);
// Do something with received message
if (data.typeData == TypeInfo.PlayerInfo)
{
print("Execute Player Info");
print(data.data + "|| " + data.typeData);
PlayerInfo player = (PlayerInfo)Marshal.PtrToStructure(data.data, typeof(PlayerInfo));
print($"DATA DECOMPRESSED (Player == )");
print("Player: "+ player+ "|| idNet: "+player.idNetwork);
ExecutePlayerDataCommand(player);
}
}
catch (Exception e)
{
Debug.LogError($"Unable to process message from socket server \n\n --[ERROR({e.Source})]-- \n\n "+e.Message);
}
}
All this system works if you are connected in 'Owner' of the Lobby. But if you are connected just in 'Client', a have this error:
Litelo invited you to his lobby.
Client joined the lobby
SocketIP_Local: 3938562153
wouhu, connection right here '$-$
<< DECOMPRESS +2249505835568
data.data = 2387748430064
Execute Player Info
2387748430064|| PlayerInfo
Unable to process message from socket server
--[ERROR()]--
Object reference not set to an instance of an object
Connection Got A Message 2249505835568
the problem seems to from
PlayerInfo player = (PlayerInfo)Marshal.PtrToStructure(data.data, typeof(PlayerInfo));
Anybody can help me to solve this problem ?
This won't work:
IntPtr infoCompressed = Marshal.AllocHGlobal(sizeCreatePlayerData);
Marshal.StructureToPtr(infoPlayer, infoCompressed, false);
Data dataInfoPlayer = new Data
{
typeData = TypeInfo.PlayerInfo,
data = infoCompressed
};
This creates an unmanaged pointer to a data structure and then adds that pointer(!) to the transmission object. That pointer has no meaning on the other side. It's interesting you "only" get a NullReferenceException, as this could give you errors that are much worse. Side note: You're leaking the unmanaged memory.
Why don't you just use a standard serialization, using Json or XML? Your data structures aren't that complicated.