diff --git a/EssentialsPlugin/EntityManagers/VoxelManagement.cs b/EssentialsPlugin/EntityManagers/VoxelManagement.cs new file mode 100644 index 0000000..701d848 --- /dev/null +++ b/EssentialsPlugin/EntityManagers/VoxelManagement.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Xml.Serialization; + +using EssentialsPlugin.Utility; + +using Sandbox.ModAPI; +using Sandbox.Common.ObjectBuilders; +using Sandbox.Common.ObjectBuilders.VRageData; + +using SEModAPIInternal.API.Entity.Sector.SectorObject.CubeGrid.CubeBlock; +using SEModAPIInternal.API.Server; + +using EssentialsPlugin.UtilityClasses; + +namespace EssentialsPlugin.EntityManagers +{ + public class VoxelManagement + { + private static Dictionary> m_userVoxels = new Dictionary>(); + private static bool m_voxelCheck = false; + + public static void CheckAndSendVoxels() + { + if (m_voxelCheck) + return; + + m_voxelCheck = true; + + try + { + DateTime start = DateTime.Now; + HashSet entities = new HashSet(); + List players = new List(); + try + { + MyAPIGateway.Entities.GetEntities(entities); + MyAPIGateway.Players.GetPlayers(players); + } + catch + { + Logging.WriteLineAndConsole(string.Format("CheckAndSendVoxels(): Entity list busy, skipping check")); + return; + } + + HashSet> voxelsToSend = new HashSet>(); + foreach (IMyEntity entity in entities) + { + if (!(entity is IMyVoxelMap)) + continue; + + IMyVoxelMap voxel = (IMyVoxelMap)entity; + + foreach (IMyPlayer player in players) + { + double distance = 0d; + if (Entity.GetDistanceBetweenPointAndPlayer(entity.GetPosition(), player, out distance)) + { + if (!m_userVoxels.ContainsKey(player.SteamUserId)) + m_userVoxels.Add(player.SteamUserId, new HashSet()); + + HashSet voxels = m_userVoxels[player.SteamUserId]; + + if (distance < PluginSettings.Instance.DynamicVoxelDistance && !voxels.Contains(entity.EntityId)) + { + voxelsToSend.Add(new Tuple(player.SteamUserId, entity)); + } + } + } + } + + if (voxelsToSend.Count > 0) + { + Wrapper.GameAction(() => + { + foreach (Tuple p in voxelsToSend) + { + SendVoxel(p.Item1, p.Item2); + } + }); + } + + if ((DateTime.Now - start).TotalSeconds > 1) + Logging.WriteLineAndConsole(string.Format("CheckAndSendVoxels(): {0}ms", (DateTime.Now - start).TotalMilliseconds)); + } + catch (Exception ex) + { + Logging.WriteLineAndConsole(string.Format("CheckAndSendVoxels(): {0}", ex.ToString())); + } + finally + { + m_voxelCheck = false; + } + } + + public static void ClearCache(ulong steamId) + { + if (!m_userVoxels.ContainsKey(steamId)) + return; + + HashSet voxels = m_userVoxels[steamId]; + voxels.Clear(); + } + + private static void SendVoxel(ulong steamId, IMyEntity voxel) + { + if (!m_userVoxels.ContainsKey(steamId)) + m_userVoxels.Add(steamId, new HashSet()); + + HashSet voxels = m_userVoxels[steamId]; + voxels.Add(voxel.EntityId); + + SendVoxelData(steamId, voxel); + } + + private static void SendVoxelData(ulong steamId, IMyEntity voxel) + { + try + { + IMyVoxelMap voxelMap = (IMyVoxelMap)voxel; + + byte[] voxelData; + voxelMap.Storage.Save(out voxelData); + + VoxelHeaderData header = new VoxelHeaderData(); + header.EntityId = voxel.EntityId; + header.HalfExtent = voxelMap.Storage.Size / 2; + header.Position = voxel.GetPosition(); + header.Name = voxelMap.StorageName; + header.DataLength = voxelData.Length; + + string headerString = MyAPIGateway.Utilities.SerializeToXML(header); + ushort length = (ushort)headerString.Length; + + byte[] headerData = new byte[2 + headerString.Length]; + + headerData[0] = (byte)length; + headerData[1] = (byte)(length >> 8); + + for (int r = 0; r < headerString.Length; r++) + { + headerData[r + 2] = (byte)headerString[r]; + } + Logging.WriteLineAndConsole(string.Format("Sending Voxel Header Data: {0} / {1} - {2} ({3})", voxelData.Length, headerData.Length, steamId, voxel.GetPosition())); + Communication.SendDataMessage(steamId, 5001, headerData); + + int blockSize = 4096; + for (ushort r = 0; r < (voxelData.Length / blockSize) + 1; r++) + { + int partLength = voxelData.Length - (r * blockSize); + + if (partLength > blockSize) + partLength = blockSize; + + byte[] outData = new byte[partLength + 12]; + + for (int s = 0; s < 8; s++) + outData[s] = (byte)(header.EntityId >> (s * 8)); + + for (int s = 0; s < 2; s++) + outData[s + 8] = (byte)(partLength >> (s * 8)); + + for (int s = 0; s < 2; s++) + outData[s + 10] = (byte)(r >> (s * 8)); + + Buffer.BlockCopy(voxelData, r * blockSize, outData, 12, partLength); + Communication.SendDataMessage(steamId, 5002, outData); + } + } + catch (Exception ex) + { + Logging.WriteLineAndConsole(string.Format("SendVoxelData(): {0}", ex.ToString())); + } + } + + public class VoxelHeaderData + { + public long EntityId { get; set; } + public string Name { get; set; } + public SerializableVector3I HalfExtent { get; set; } + public SerializableVector3D Position { get; set; } + public int DataLength { get; set; } + } + } +} diff --git a/EssentialsPlugin/Essentials.cs b/EssentialsPlugin/Essentials.cs index 8824a07..6f2a708 100644 --- a/EssentialsPlugin/Essentials.cs +++ b/EssentialsPlugin/Essentials.cs @@ -683,6 +683,32 @@ public DynamicTurretManagementMode DynamicTurretManagementMode } } + [Category("Dynamic Entity Management")] + [Description("Enable / Disable dynamic voxel management.")] + [Browsable(true)] + [ReadOnly(false)] + public bool DynamicVoxelManagementEnabled + { + get { return PluginSettings.Instance.DynamicVoxelManagementEnabled; } + set + { + PluginSettings.Instance.DynamicVoxelManagementEnabled = value; + } + } + + [Category("Dynamic Entity Management")] + [Description("Sets the distance in which voxels are sent to the client")] + [Browsable(true)] + [ReadOnly(false)] + public int DynamicVoxelDistance + { + get { return PluginSettings.Instance.DynamicVoxelDistance; } + set + { + PluginSettings.Instance.DynamicVoxelDistance = value; + } + } + /* [Category("Dynamic Entity Management")] [Description("Enable / Disable dynamic block management. This manager disables blocks of ships that can't be concealed to further increase gamelogic savings.")] diff --git a/EssentialsPlugin/EssentialsPlugin.csproj b/EssentialsPlugin/EssentialsPlugin.csproj index 52dcd66..50bbc5d 100644 --- a/EssentialsPlugin/EssentialsPlugin.csproj +++ b/EssentialsPlugin/EssentialsPlugin.csproj @@ -129,6 +129,7 @@ ControlForm.cs + @@ -142,6 +143,7 @@ + diff --git a/EssentialsPlugin/ProcessHandlers/ProcessVoxels.cs b/EssentialsPlugin/ProcessHandlers/ProcessVoxels.cs new file mode 100644 index 0000000..ad842f6 --- /dev/null +++ b/EssentialsPlugin/ProcessHandlers/ProcessVoxels.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using EssentialsPlugin.Utility; +using Sandbox.ModAPI; +using SEModAPIInternal.API.Common; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using VRage.Common.Utils; +using System.Text.RegularExpressions; +using System.Threading; + +using SEModAPIInternal.API.Entity.Sector.SectorObject; +using SEModAPIInternal.API.Entity.Sector.SectorObject.CubeGrid; +using SEModAPIInternal.API.Entity.Sector.SectorObject.CubeGrid.CubeBlock; +using SEModAPIInternal.API.Entity; + +using Sandbox.Common.ObjectBuilders; + +using EssentialsPlugin.Settings; +using EssentialsPlugin.EntityManagers; + +using Sandbox.Definitions; + +namespace EssentialsPlugin.ProcessHandler +{ + public class ProcessVoxels : ProcessHandlerBase + { + private static DateTime m_lastEnableCheck; + + + public override int GetUpdateResolution() + { + return 1000; + } + + public override void Handle() + { + if (PluginSettings.Instance.DynamicVoxelManagementEnabled) + { + if (DateTime.Now - m_lastEnableCheck > TimeSpan.FromSeconds(5)) + { + VoxelManagement.CheckAndSendVoxels(); + m_lastEnableCheck = DateTime.Now; + } + } + + base.Handle(); + } + + public override void OnPlayerJoined(ulong remoteUserId) + { + if (!PluginSettings.Instance.DynamicVoxelManagementEnabled) + return; + + VoxelManagement.ClearCache(remoteUserId); + } + } +} + diff --git a/EssentialsPlugin/Settings/PluginSettings.cs b/EssentialsPlugin/Settings/PluginSettings.cs index a567f57..8e64efe 100644 --- a/EssentialsPlugin/Settings/PluginSettings.cs +++ b/EssentialsPlugin/Settings/PluginSettings.cs @@ -83,6 +83,8 @@ public class PluginSettings private bool _dynamicTurretAllowExemption; private bool _dynamicBlockManagementEnabled; private DynamicTurretManagementMode _mDynamicTurretManagementMode; + private bool _dynamicVoxelManagementEnabled; + private int _dynamicVoxelDistance; private bool _dynamicConcealServerOnly; private bool _dynamicClientConcealEnabled; @@ -636,6 +638,26 @@ public DynamicTurretManagementMode DynamicTurretManagementMode } } + public bool DynamicVoxelManagementEnabled + { + get { return _dynamicVoxelManagementEnabled; } + set + { + _dynamicVoxelManagementEnabled = value; + Save(); + } + } + + public int DynamicVoxelDistance + { + get { return _dynamicVoxelDistance; } + set + { + _dynamicVoxelDistance = value; + Save(); + } + } + public bool DynamicBlockManagementEnabled { get { return _dynamicBlockManagementEnabled; } @@ -792,6 +814,8 @@ public PluginSettings() _dynamicShowMessages = false; _dynamicTurretTargetDistance = 2000; _dynamicTurretManagementEnabled = false; + _dynamicVoxelManagementEnabled = false; + _dynamicVoxelDistance = 20000; _dockingShipsPerZone = 1; diff --git a/EssentialsPlugin/Utility/Communication.cs b/EssentialsPlugin/Utility/Communication.cs index eae049e..b2a63fb 100644 --- a/EssentialsPlugin/Utility/Communication.cs +++ b/EssentialsPlugin/Utility/Communication.cs @@ -105,5 +105,37 @@ public static void DisplayDialog(ulong steamId, string header, string subheader, { SendClientMessage(steamId, string.Format("/dialog \"{0}\" \"{1}\" \"{2}\" \"{3}\" \"{4}\"", header, subheader, " ", content.Replace("\r\n", "|"), buttonText)); } + + public static void SendDataMessage(ulong steamId, long msgId, byte[] data) + { + string msgIdString = msgId.ToString(); + byte[] newData = new byte[data.Length + msgIdString.Length + 1]; + newData[0] = (byte)msgIdString.Length; + for (int r = 0; r < msgIdString.Length; r++) + newData[r + 1] = (byte)msgIdString[r]; + + Buffer.BlockCopy(data, 0, newData, msgIdString.Length + 1, data.Length); + + ServerNetworkManager.SendDataMessage(9000, newData, steamId); + } + + public static void BroadcastDataMessage(long msgId, byte[] data) + { + string msgIdString = msgId.ToString(); + byte[] newData = new byte[data.Length + msgIdString.Length + 1]; + newData[0] = (byte)msgIdString.Length; + for (int r = 0; r < msgIdString.Length; r++) + newData[r + 1] = (byte)msgIdString[r]; + + Buffer.BlockCopy(data, 0, newData, msgIdString.Length + 1, data.Length); + + MyAPIGateway.Multiplayer.SendMessageToOthers(9000, newData); + } + + public class ServerMessageItem + { + public string From { get; set; } + public string Message { get; set; } + } } }