Fix mod communication (probably)

This commit is contained in:
Brant Martin
2018-12-24 18:02:43 -05:00
parent 76637b130c
commit 66b7adf485
3 changed files with 95 additions and 94 deletions

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Torch.Mod.Messages
{
/// <summary>
/// shim to store incoming message data
/// </summary>
internal class IncomingMessage : MessageBase
{
public IncomingMessage()
{
}
public override void ProcessClient()
{
throw new Exception();
}
public override void ProcessServer()
{
throw new Exception();
}
}
}

View File

@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Sandbox.ModAPI; using Sandbox.ModAPI;
using Torch.Mod.Messages; using Torch.Mod.Messages;
using VRage; using VRage;
using VRage.Collections;
using VRage.Game.ModAPI; using VRage.Game.ModAPI;
using VRage.Utils; using VRage.Utils;
using Task = ParallelTasks.Task; using Task = ParallelTasks.Task;
@@ -18,24 +19,20 @@ namespace Torch.Mod
{ {
public const ushort NET_ID = 4352; public const ushort NET_ID = 4352;
private static bool _closing; private static bool _closing;
private static ConcurrentQueue<MessageBase> _outgoing; private static BlockingCollection<MessageBase> _processing;
private static ConcurrentQueue<byte[]> _incoming; private static MyConcurrentPool<IncomingMessage> _messagePool;
private static List<IMyPlayer> _playerCache; private static List<IMyPlayer> _playerCache;
private static FastResourceLock _lock;
private static Task _task;
public static void Register() public static void Register()
{ {
MyLog.Default.WriteLineAndConsole("TORCH MOD: Registering mod communication."); MyLog.Default.WriteLineAndConsole("TORCH MOD: Registering mod communication.");
_outgoing = new ConcurrentQueue<MessageBase>(); _processing = new BlockingCollection<MessageBase>(new ConcurrentQueue<MessageBase>());
_incoming = new ConcurrentQueue<byte[]>();
_playerCache = new List<IMyPlayer>(); _playerCache = new List<IMyPlayer>();
_lock = new FastResourceLock(); _messagePool = new MyConcurrentPool<IncomingMessage>(8);
MyAPIGateway.Multiplayer.RegisterMessageHandler(NET_ID, MessageHandler); MyAPIGateway.Multiplayer.RegisterMessageHandler(NET_ID, MessageHandler);
//background thread to handle de/compression and processing //background thread to handle de/compression and processing
_task = MyAPIGateway.Parallel.StartBackground(DoProcessing); MyAPIGateway.Parallel.StartBackground(DoProcessing);
MyLog.Default.WriteLineAndConsole("TORCH MOD: Mod communication registered successfully."); MyLog.Default.WriteLineAndConsole("TORCH MOD: Mod communication registered successfully.");
} }
@@ -43,15 +40,16 @@ namespace Torch.Mod
{ {
MyLog.Default.WriteLineAndConsole("TORCH MOD: Unregistering mod communication."); MyLog.Default.WriteLineAndConsole("TORCH MOD: Unregistering mod communication.");
MyAPIGateway.Multiplayer?.UnregisterMessageHandler(NET_ID, MessageHandler); MyAPIGateway.Multiplayer?.UnregisterMessageHandler(NET_ID, MessageHandler);
ReleaseLock(); _processing.CompleteAdding();
_closing = true; _closing = true;
//_task.Wait(); //_task.Wait();
} }
private static void MessageHandler(byte[] bytes) private static void MessageHandler(byte[] bytes)
{ {
_incoming.Enqueue(bytes); var m = _messagePool.Get();
ReleaseLock(); m.CompressedData = bytes;
_processing.Add(m);
} }
public static void DoProcessing() public static void DoProcessing()
@@ -60,75 +58,68 @@ namespace Torch.Mod
{ {
try try
{ {
byte[] incoming; var m = _processing.Take();
while (_incoming.TryDequeue(out incoming)) if (m is IncomingMessage)
{ {
MessageBase m; MessageBase i;
try try
{ {
var o = MyCompression.Decompress(incoming); var o = MyCompression.Decompress(m.CompressedData);
m = MyAPIGateway.Utilities.SerializeFromBinary<MessageBase>(o); m.CompressedData = null;
_messagePool.Return((IncomingMessage)m);
i = MyAPIGateway.Utilities.SerializeFromBinary<MessageBase>(o);
} }
catch (Exception ex) catch (Exception ex)
{ {
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Failed to deserialize message! {ex}"); MyLog.Default.WriteLineAndConsole($"TORCH MOD: Failed to deserialize message! {ex}");
continue; continue;
} }
if (MyAPIGateway.Multiplayer.IsServer)
m.ProcessServer();
else
m.ProcessClient();
}
if (!_outgoing.IsEmpty) if (MyAPIGateway.Multiplayer.IsServer)
i.ProcessServer();
else
i.ProcessClient();
}
else
{ {
List<MessageBase> tosend = new List<MessageBase>(_outgoing.Count); var b = MyAPIGateway.Utilities.SerializeToBinary(m);
MessageBase outMessage; m.CompressedData = MyCompression.Compress(b);
while (_outgoing.TryDequeue(out outMessage))
{
var b = MyAPIGateway.Utilities.SerializeToBinary(outMessage);
outMessage.CompressedData = MyCompression.Compress(b);
tosend.Add(outMessage);
}
MyAPIGateway.Utilities.InvokeOnGameThread(() => MyAPIGateway.Utilities.InvokeOnGameThread(() =>
{ {
MyAPIGateway.Players.GetPlayers(_playerCache);
foreach (var outgoing in tosend)
{
switch (outgoing.TargetType)
{
case MessageTarget.Single:
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, outgoing.CompressedData, outgoing.Target);
break;
case MessageTarget.Server:
MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, outgoing.CompressedData);
break;
case MessageTarget.AllClients:
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId)
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, outgoing.CompressedData, p.SteamUserId);
}
break;
case MessageTarget.AllExcept:
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId || outgoing.Ignore.Contains(p.SteamUserId))
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, outgoing.CompressedData, p.SteamUserId);
}
break;
default:
throw new Exception();
}
}
_playerCache.Clear();
});
}
AcquireLock(); switch (m.TargetType)
{
case MessageTarget.Single:
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, m.Target);
break;
case MessageTarget.Server:
MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, m.CompressedData);
break;
case MessageTarget.AllClients:
MyAPIGateway.Players.GetPlayers(_playerCache);
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId)
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
}
break;
case MessageTarget.AllExcept:
MyAPIGateway.Players.GetPlayers(_playerCache);
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId || m.Ignore.Contains(p.SteamUserId))
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
}
break;
default:
throw new Exception();
}
_playerCache.Clear();
});
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -136,14 +127,15 @@ namespace Torch.Mod
} }
} }
MyLog.Default.WriteLineAndConsole("TORCH MOD: COMMUNICATION THREAD: EXIT SIGNAL RECIEVED!"); MyLog.Default.WriteLineAndConsole("TORCH MOD: COMMUNICATION THREAD: EXIT SIGNAL RECEIVED!");
//exit signal received. Clean everything and GTFO //exit signal received. Clean everything and GTFO
_outgoing = null; _processing.Dispose();
_incoming = null; _processing = null;
_messagePool.Clean();
_messagePool = null;
_playerCache = null; _playerCache = null;
_lock = null;
} }
public static void SendMessageTo(MessageBase message, ulong target) public static void SendMessageTo(MessageBase message, ulong target)
{ {
if (!MyAPIGateway.Multiplayer.IsServer) if (!MyAPIGateway.Multiplayer.IsServer)
@@ -155,8 +147,7 @@ namespace Torch.Mod
message.Target = target; message.Target = target;
message.TargetType = MessageTarget.Single; message.TargetType = MessageTarget.Single;
MyLog.Default.WriteLineAndConsole($"Sending message of type {message.GetType().FullName}"); MyLog.Default.WriteLineAndConsole($"Sending message of type {message.GetType().FullName}");
_outgoing.Enqueue(message); _processing.Add(message);
ReleaseLock();
} }
public static void SendMessageToClients(MessageBase message) public static void SendMessageToClients(MessageBase message)
@@ -168,8 +159,7 @@ namespace Torch.Mod
return; return;
message.TargetType = MessageTarget.AllClients; message.TargetType = MessageTarget.AllClients;
_outgoing.Enqueue(message); _processing.Add(message);
ReleaseLock();
} }
public static void SendMessageExcept(MessageBase message, params ulong[] ignoredUsers) public static void SendMessageExcept(MessageBase message, params ulong[] ignoredUsers)
@@ -182,8 +172,7 @@ namespace Torch.Mod
message.TargetType = MessageTarget.AllExcept; message.TargetType = MessageTarget.AllExcept;
message.Ignore = ignoredUsers; message.Ignore = ignoredUsers;
_outgoing.Enqueue(message); _processing.Add(message);
ReleaseLock();
} }
public static void SendMessageToServer(MessageBase message) public static void SendMessageToServer(MessageBase message)
@@ -192,22 +181,7 @@ namespace Torch.Mod
return; return;
message.TargetType = MessageTarget.Server; message.TargetType = MessageTarget.Server;
_outgoing.Enqueue(message); _processing.Add(message);
ReleaseLock();
}
private static void ReleaseLock()
{
while(_lock?.TryAcquireExclusive() == false)
_lock?.ReleaseExclusive();
_lock?.ReleaseExclusive();
}
private static void AcquireLock()
{
ReleaseLock();
_lock?.AcquireExclusive();
_lock?.AcquireExclusive();
} }
} }
} }

View File

@@ -9,6 +9,7 @@
<Import_RootNamespace>Torch.Mod</Import_RootNamespace> <Import_RootNamespace>Torch.Mod</Import_RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Messages\IncomingMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\NotificationMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\NotificationMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\DialogMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\DialogMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\MessageBase.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\MessageBase.cs" />