Merge branch 'experiment' of https://github.com/TorchAPI/Torch into experiment
This commit is contained in:
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@@ -50,7 +50,7 @@ node {
|
|||||||
|
|
||||||
packageAndArchive(buildMode, "torch-server", "Torch.Client*")
|
packageAndArchive(buildMode, "torch-server", "Torch.Client*")
|
||||||
|
|
||||||
packageAndArchive(buildMode, "torch-client", "Torch.Server*")
|
/*packageAndArchive(buildMode, "torch-client", "Torch.Server*")*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disabled because they fail builds more often than they detect actual problems
|
/* Disabled because they fail builds more often than they detect actual problems
|
||||||
@@ -73,4 +73,4 @@ node {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
72
Torch.Mod/Messages/DialogMessage.cs
Normal file
72
Torch.Mod/Messages/DialogMessage.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ProtoBuf;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
|
||||||
|
namespace Torch.Mod.Messages
|
||||||
|
{
|
||||||
|
/// Dialogs are structured as follows
|
||||||
|
///
|
||||||
|
/// _____________________________________
|
||||||
|
/// | Title |
|
||||||
|
/// --------------------------------------
|
||||||
|
/// | Prefix Subtitle |
|
||||||
|
/// --------------------------------------
|
||||||
|
/// | ________________________________ |
|
||||||
|
/// | | Content | |
|
||||||
|
/// | --------------------------------- |
|
||||||
|
/// | ____________ |
|
||||||
|
/// | | ButtonText | |
|
||||||
|
/// | -------------- |
|
||||||
|
/// --------------------------------------
|
||||||
|
///
|
||||||
|
/// Button has a callback on click option,
|
||||||
|
/// but can't serialize that, so ¯\_(ツ)_/¯
|
||||||
|
[ProtoContract]
|
||||||
|
public class DialogMessage : MessageBase
|
||||||
|
{
|
||||||
|
[ProtoMember(201)]
|
||||||
|
public string Title;
|
||||||
|
[ProtoMember(202)]
|
||||||
|
public string Subtitle;
|
||||||
|
[ProtoMember(203)]
|
||||||
|
public string Prefix;
|
||||||
|
[ProtoMember(204)]
|
||||||
|
public string Content;
|
||||||
|
[ProtoMember(205)]
|
||||||
|
public string ButtonText;
|
||||||
|
|
||||||
|
public DialogMessage()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public DialogMessage(string title, string subtitle, string content)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
Subtitle = subtitle;
|
||||||
|
Content = content;
|
||||||
|
Prefix = String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DialogMessage(string title = null, string prefix = null, string subtitle = null, string content = null, string buttonText = null)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
Subtitle = subtitle;
|
||||||
|
Prefix = prefix ?? String.Empty;
|
||||||
|
Content = content;
|
||||||
|
ButtonText = buttonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ProcessClient()
|
||||||
|
{
|
||||||
|
MyAPIGateway.Utilities.ShowMissionScreen(Title, Prefix, Subtitle, Content, null, ButtonText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ProcessServer()
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
Torch.Mod/Messages/MessageBase.cs
Normal file
51
Torch.Mod/Messages/MessageBase.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace Torch.Mod.Messages
|
||||||
|
{
|
||||||
|
#region Includes
|
||||||
|
[ProtoInclude(1, typeof(DialogMessage))]
|
||||||
|
[ProtoInclude(2, typeof(NotificationMessage))]
|
||||||
|
[ProtoInclude(3, typeof(VoxelResetMessage))]
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public abstract class MessageBase
|
||||||
|
{
|
||||||
|
[ProtoMember(101)]
|
||||||
|
public ulong SenderId;
|
||||||
|
|
||||||
|
public abstract void ProcessClient();
|
||||||
|
public abstract void ProcessServer();
|
||||||
|
|
||||||
|
//members below not serialized, they're just metadata about the intended target(s) of this message
|
||||||
|
internal MessageTarget TargetType;
|
||||||
|
internal ulong Target;
|
||||||
|
internal ulong[] Ignore;
|
||||||
|
internal byte[] CompressedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MessageTarget
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Send to Target
|
||||||
|
/// </summary>
|
||||||
|
Single,
|
||||||
|
/// <summary>
|
||||||
|
/// Send to Server
|
||||||
|
/// </summary>
|
||||||
|
Server,
|
||||||
|
/// <summary>
|
||||||
|
/// Send to all Clients (only valid from server)
|
||||||
|
/// </summary>
|
||||||
|
AllClients,
|
||||||
|
/// <summary>
|
||||||
|
/// Send to all except those steam ID listed in Ignore
|
||||||
|
/// </summary>
|
||||||
|
AllExcept,
|
||||||
|
}
|
||||||
|
}
|
39
Torch.Mod/Messages/NotificationMessage.cs
Normal file
39
Torch.Mod/Messages/NotificationMessage.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using ProtoBuf;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
|
||||||
|
namespace Torch.Mod.Messages
|
||||||
|
{
|
||||||
|
[ProtoContract]
|
||||||
|
public class NotificationMessage : MessageBase
|
||||||
|
{
|
||||||
|
[ProtoMember(201)]
|
||||||
|
public string Message;
|
||||||
|
[ProtoMember(202)]
|
||||||
|
public string Font;
|
||||||
|
[ProtoMember(203)]
|
||||||
|
public int DisappearTimeMs;
|
||||||
|
|
||||||
|
public NotificationMessage()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public NotificationMessage(string message, int disappearTimeMs, string font)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
DisappearTimeMs = disappearTimeMs;
|
||||||
|
Font = font;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ProcessClient()
|
||||||
|
{
|
||||||
|
MyAPIGateway.Utilities.ShowNotification(Message, DisappearTimeMs, Font);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ProcessServer()
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
Torch.Mod/Messages/VoxelResetMessage.cs
Normal file
46
Torch.Mod/Messages/VoxelResetMessage.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using ProtoBuf;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
using VRage.ModAPI;
|
||||||
|
using VRage.Voxels;
|
||||||
|
|
||||||
|
namespace Torch.Mod.Messages
|
||||||
|
{
|
||||||
|
[ProtoContract]
|
||||||
|
public class VoxelResetMessage : MessageBase
|
||||||
|
{
|
||||||
|
[ProtoMember(201)]
|
||||||
|
public long[] EntityId;
|
||||||
|
|
||||||
|
public VoxelResetMessage()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public VoxelResetMessage(long[] entityId)
|
||||||
|
{
|
||||||
|
EntityId = entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ProcessClient()
|
||||||
|
{
|
||||||
|
MyAPIGateway.Parallel.ForEach(EntityId, id =>
|
||||||
|
{
|
||||||
|
IMyEntity e;
|
||||||
|
if (!MyAPIGateway.Entities.TryGetEntityById(id, out e))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var v = e as IMyVoxelBase;
|
||||||
|
if (v == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
v.Storage.Reset(MyStorageDataTypeFlags.All);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ProcessServer()
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
213
Torch.Mod/ModCommunication.cs
Normal file
213
Torch.Mod/ModCommunication.cs
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
using Torch.Mod.Messages;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Game.ModAPI;
|
||||||
|
using VRage.Utils;
|
||||||
|
using Task = ParallelTasks.Task;
|
||||||
|
|
||||||
|
namespace Torch.Mod
|
||||||
|
{
|
||||||
|
public static class ModCommunication
|
||||||
|
{
|
||||||
|
public const ushort NET_ID = 4352;
|
||||||
|
private static bool _closing;
|
||||||
|
private static ConcurrentQueue<MessageBase> _outgoing;
|
||||||
|
private static ConcurrentQueue<byte[]> _incoming;
|
||||||
|
private static List<IMyPlayer> _playerCache;
|
||||||
|
private static FastResourceLock _lock;
|
||||||
|
private static Task _task;
|
||||||
|
|
||||||
|
public static void Register()
|
||||||
|
{
|
||||||
|
MyLog.Default.WriteLineAndConsole("TORCH MOD: Registering mod communication.");
|
||||||
|
_outgoing = new ConcurrentQueue<MessageBase>();
|
||||||
|
_incoming = new ConcurrentQueue<byte[]>();
|
||||||
|
_playerCache = new List<IMyPlayer>();
|
||||||
|
_lock = new FastResourceLock();
|
||||||
|
|
||||||
|
|
||||||
|
MyAPIGateway.Multiplayer.RegisterMessageHandler(NET_ID, MessageHandler);
|
||||||
|
//background thread to handle de/compression and processing
|
||||||
|
_task = MyAPIGateway.Parallel.StartBackground(DoProcessing);
|
||||||
|
MyLog.Default.WriteLineAndConsole("TORCH MOD: Mod communication registered successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Unregister()
|
||||||
|
{
|
||||||
|
MyLog.Default.WriteLineAndConsole("TORCH MOD: Unregistering mod communication.");
|
||||||
|
MyAPIGateway.Multiplayer?.UnregisterMessageHandler(NET_ID, MessageHandler);
|
||||||
|
ReleaseLock();
|
||||||
|
_closing = true;
|
||||||
|
//_task.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MessageHandler(byte[] bytes)
|
||||||
|
{
|
||||||
|
_incoming.Enqueue(bytes);
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DoProcessing()
|
||||||
|
{
|
||||||
|
while (!_closing)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] incoming;
|
||||||
|
while (_incoming.TryDequeue(out incoming))
|
||||||
|
{
|
||||||
|
MessageBase m;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var o = MyCompression.Decompress(incoming);
|
||||||
|
m = MyAPIGateway.Utilities.SerializeFromBinary<MessageBase>(o);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Failed to deserialize message! {ex}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (MyAPIGateway.Multiplayer.IsServer)
|
||||||
|
m.ProcessServer();
|
||||||
|
else
|
||||||
|
m.ProcessClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_outgoing.IsEmpty)
|
||||||
|
{
|
||||||
|
List<MessageBase> tosend = new List<MessageBase>(_outgoing.Count);
|
||||||
|
MessageBase outMessage;
|
||||||
|
while (_outgoing.TryDequeue(out outMessage))
|
||||||
|
{
|
||||||
|
var b = MyAPIGateway.Utilities.SerializeToBinary(outMessage);
|
||||||
|
outMessage.CompressedData = MyCompression.Compress(b);
|
||||||
|
tosend.Add(outMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Exception occurred in communication thread! {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MyLog.Default.WriteLineAndConsole("TORCH MOD: COMMUNICATION THREAD: EXIT SIGNAL RECIEVED!");
|
||||||
|
//exit signal received. Clean everything and GTFO
|
||||||
|
_outgoing = null;
|
||||||
|
_incoming = null;
|
||||||
|
_playerCache = null;
|
||||||
|
_lock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendMessageTo(MessageBase message, ulong target)
|
||||||
|
{
|
||||||
|
if (!MyAPIGateway.Multiplayer.IsServer)
|
||||||
|
throw new Exception("Only server can send targeted messages");
|
||||||
|
|
||||||
|
if (_closing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
message.Target = target;
|
||||||
|
message.TargetType = MessageTarget.Single;
|
||||||
|
MyLog.Default.WriteLineAndConsole($"Sending message of type {message.GetType().FullName}");
|
||||||
|
_outgoing.Enqueue(message);
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendMessageToClients(MessageBase message)
|
||||||
|
{
|
||||||
|
if (!MyAPIGateway.Multiplayer.IsServer)
|
||||||
|
throw new Exception("Only server can send targeted messages");
|
||||||
|
|
||||||
|
if (_closing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
message.TargetType = MessageTarget.AllClients;
|
||||||
|
_outgoing.Enqueue(message);
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendMessageExcept(MessageBase message, params ulong[] ignoredUsers)
|
||||||
|
{
|
||||||
|
if (!MyAPIGateway.Multiplayer.IsServer)
|
||||||
|
throw new Exception("Only server can send targeted messages");
|
||||||
|
|
||||||
|
if (_closing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
message.TargetType = MessageTarget.AllExcept;
|
||||||
|
message.Ignore = ignoredUsers;
|
||||||
|
_outgoing.Enqueue(message);
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendMessageToServer(MessageBase message)
|
||||||
|
{
|
||||||
|
if (_closing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
message.TargetType = MessageTarget.Server;
|
||||||
|
_outgoing.Enqueue(message);
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReleaseLock()
|
||||||
|
{
|
||||||
|
while(_lock?.TryAcquireExclusive() == false)
|
||||||
|
_lock?.ReleaseExclusive();
|
||||||
|
_lock?.ReleaseExclusive();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AcquireLock()
|
||||||
|
{
|
||||||
|
ReleaseLock();
|
||||||
|
_lock?.AcquireExclusive();
|
||||||
|
_lock?.AcquireExclusive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Torch.Mod/Torch.Mod.projitems
Normal file
19
Torch.Mod/Torch.Mod.projitems
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||||
|
<HasSharedItems>true</HasSharedItems>
|
||||||
|
<SharedGUID>3ce4d2e9-b461-4f19-8233-f87e0dfddd74</SharedGUID>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<Import_RootNamespace>Torch.Mod</Import_RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Messages\NotificationMessage.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Messages\DialogMessage.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Messages\MessageBase.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Messages\VoxelResetMessage.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)ModCommunication.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)TorchModCore.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
13
Torch.Mod/Torch.Mod.shproj
Normal file
13
Torch.Mod/Torch.Mod.shproj
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>3ce4d2e9-b461-4f19-8233-f87e0dfddd74</ProjectGuid>
|
||||||
|
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
|
||||||
|
<PropertyGroup />
|
||||||
|
<Import Project="Torch.Mod.projitems" Label="Shared" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
|
||||||
|
</Project>
|
37
Torch.Mod/TorchModCore.cs
Normal file
37
Torch.Mod/TorchModCore.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Game.Components;
|
||||||
|
|
||||||
|
namespace Torch.Mod
|
||||||
|
{
|
||||||
|
[MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation)]
|
||||||
|
public class TorchModCore : MySessionComponentBase
|
||||||
|
{
|
||||||
|
public const long MOD_ID = 1406994352;
|
||||||
|
private static bool _init;
|
||||||
|
|
||||||
|
public override void UpdateAfterSimulation()
|
||||||
|
{
|
||||||
|
if (_init)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_init = true;
|
||||||
|
ModCommunication.Register();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UnloadData()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ModCommunication.Unregister();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
//session unloading, don't care
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -18,6 +18,7 @@ using Torch.API;
|
|||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.API.Session;
|
using Torch.API.Session;
|
||||||
using Torch.Commands;
|
using Torch.Commands;
|
||||||
|
using Torch.Mod;
|
||||||
using Torch.Server.Commands;
|
using Torch.Server.Commands;
|
||||||
using Torch.Server.Managers;
|
using Torch.Server.Managers;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
@@ -45,7 +46,7 @@ namespace Torch.Server
|
|||||||
private Timer _watchdog;
|
private Timer _watchdog;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TorchServer(TorchConfig config = null)
|
public TorchServer(TorchConfig config = null)
|
||||||
{
|
{
|
||||||
DedicatedInstance = new InstanceManager(this);
|
DedicatedInstance = new InstanceManager(this);
|
||||||
AddManager(DedicatedInstance);
|
AddManager(DedicatedInstance);
|
||||||
@@ -174,10 +175,14 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
_watchdog?.Dispose();
|
_watchdog?.Dispose();
|
||||||
_watchdog = null;
|
_watchdog = null;
|
||||||
|
ModCommunication.Unregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newState == TorchSessionState.Loaded)
|
if (newState == TorchSessionState.Loaded)
|
||||||
|
{
|
||||||
CurrentSession.Managers.GetManager<CommandManager>().RegisterCommandModule(typeof(WhitelistCommands));
|
CurrentSession.Managers.GetManager<CommandManager>().RegisterCommandModule(typeof(WhitelistCommands));
|
||||||
|
ModCommunication.Register();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@@ -26,8 +26,8 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<DockPanel Grid.Row="0">
|
<DockPanel Grid.Row="0">
|
||||||
<Label Content="World:" DockPanel.Dock="Left" />
|
<Label Content="World:" DockPanel.Dock="Left" />
|
||||||
<Button Content="Import World Config" Margin="3" DockPanel.Dock="Right" Click="ImportConfig_OnClick" ToolTip="Override the DS config with the one from the selected world."/>
|
<Button Content="Import World Config" Margin="3" DockPanel.Dock="Right" Click="ImportConfig_OnClick" ToolTip="Override the DS config with the one from the selected world." IsEnabled="{Binding ElementName=WorldList, Path=Items.Count, Mode=OneWay}"/>
|
||||||
<ComboBox ItemsSource="{Binding Worlds}" SelectedItem="{Binding SelectedWorld}" Margin="3"
|
<ComboBox x:Name="WorldList" ItemsSource="{Binding Worlds}" SelectedItem="{Binding SelectedWorld}" Margin="3"
|
||||||
SelectionChanged="Selector_OnSelectionChanged">
|
SelectionChanged="Selector_OnSelectionChanged">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
<DataTemplate DataType="managers:WorldViewModel">
|
<DataTemplate DataType="managers:WorldViewModel">
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
</ListView>
|
</ListView>
|
||||||
<Button Grid.Row="1" Content="Open Folder" Margin="3" DockPanel.Dock="Bottom" Click="OpenFolder_OnClick"/>
|
<Button Grid.Row="1" Content="Open Folder" Margin="3" DockPanel.Dock="Bottom" Click="OpenFolder_OnClick"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto" Margin="3">
|
<ScrollViewer Name="PScroll" Grid.Column="1" Margin="3">
|
||||||
<Frame NavigationUIVisibility="Hidden" Content="{Binding SelectedPlugin.Control}"/>
|
<Frame NavigationUIVisibility="Hidden" Content="{Binding SelectedPlugin.Control}"/>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -19,6 +20,7 @@ using Torch.API;
|
|||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
using Torch.Server.ViewModels;
|
using Torch.Server.ViewModels;
|
||||||
|
using Torch.Views;
|
||||||
|
|
||||||
namespace Torch.Server.Views
|
namespace Torch.Server.Views
|
||||||
{
|
{
|
||||||
@@ -35,6 +37,17 @@ namespace Torch.Server.Views
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PluginManagerOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
|
||||||
|
{
|
||||||
|
if (propertyChangedEventArgs.PropertyName == nameof(PluginManagerViewModel.SelectedPlugin))
|
||||||
|
{
|
||||||
|
if (((PluginManagerViewModel)DataContext).SelectedPlugin.Control is PropertyGrid)
|
||||||
|
PScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
|
||||||
|
else
|
||||||
|
PScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void BindServer(ITorchServer server)
|
public void BindServer(ITorchServer server)
|
||||||
{
|
{
|
||||||
_server = server;
|
_server = server;
|
||||||
@@ -48,6 +61,7 @@ namespace Torch.Server.Views
|
|||||||
_plugins = _server.Managers.GetManager<PluginManager>();
|
_plugins = _server.Managers.GetManager<PluginManager>();
|
||||||
var pluginManager = new PluginManagerViewModel(_plugins);
|
var pluginManager = new PluginManagerViewModel(_plugins);
|
||||||
DataContext = pluginManager;
|
DataContext = pluginManager;
|
||||||
|
pluginManager.PropertyChanged += PluginManagerOnPropertyChanged;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
28
Torch.sln
28
Torch.sln
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.27004.2010
|
VisualStudioVersion = 15.0.26430.14
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch", "Torch\Torch.csproj", "{7E01635C-3B67-472E-BCD6-C5539564F214}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch", "Torch\Torch.csproj", "{7E01635C-3B67-472E-BCD6-C5539564F214}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -27,41 +27,60 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Versioning", "Versioning",
|
|||||||
Versioning\AssemblyVersion.cs = Versioning\AssemblyVersion.cs
|
Versioning\AssemblyVersion.cs = Versioning\AssemblyVersion.cs
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Torch.Mod", "Torch.Mod\Torch.Mod.shproj", "{3CE4D2E9-B461-4F19-8233-F87E0DFDDD74}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(Performance) = preSolution
|
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||||
HasPerformanceSessions = true
|
Torch.Mod\Torch.Mod.projitems*{3ce4d2e9-b461-4f19-8233-f87e0dfddd74}*SharedItemsImports = 13
|
||||||
|
Torch.Mod\Torch.Mod.projitems*{7e01635c-3b67-472e-bcd6-c5539564f214}*SharedItemsImports = 4
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
Release|x64 = Release|x64
|
Release|x64 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.ActiveCfg = Debug|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.Build.0 = Debug|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.ActiveCfg = Release|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.ActiveCfg = Release|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.Build.0 = Release|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.Build.0 = Release|x64
|
||||||
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.ActiveCfg = Debug|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.Build.0 = Debug|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.ActiveCfg = Release|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.ActiveCfg = Release|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.Build.0 = Release|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.Build.0 = Release|x64
|
||||||
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.ActiveCfg = Debug|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.Build.0 = Debug|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.ActiveCfg = Release|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.ActiveCfg = Release|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.Build.0 = Release|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.Build.0 = Release|x64
|
||||||
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.ActiveCfg = Debug|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.Build.0 = Debug|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.ActiveCfg = Release|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.ActiveCfg = Release|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.Build.0 = Release|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Debug|x64.ActiveCfg = Debug|x64
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Debug|x64.Build.0 = Debug|x64
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Release|x64.ActiveCfg = Release|x64
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Release|x64.ActiveCfg = Release|x64
|
||||||
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Release|x64.Build.0 = Release|x64
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Release|x64.Build.0 = Release|x64
|
||||||
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Debug|x64.ActiveCfg = Debug|x64
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Debug|x64.Build.0 = Debug|x64
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Release|x64.ActiveCfg = Release|x64
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Release|x64.ActiveCfg = Release|x64
|
||||||
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Release|x64.Build.0 = Release|x64
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Release|x64.Build.0 = Release|x64
|
||||||
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Debug|x64.ActiveCfg = Debug|x64
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Debug|x64.Build.0 = Debug|x64
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Release|x64.ActiveCfg = Release|x64
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Release|x64.ActiveCfg = Release|x64
|
||||||
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Release|x64.Build.0 = Release|x64
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
@@ -74,4 +93,7 @@ Global
|
|||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {BB51D91F-958D-4B63-A897-3C40642ACD3E}
|
SolutionGuid = {BB51D91F-958D-4B63-A897-3C40642ACD3E}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(Performance) = preSolution
|
||||||
|
HasPerformanceSessions = true
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@@ -17,6 +17,9 @@ using Torch.API.Managers;
|
|||||||
using Torch.API.Session;
|
using Torch.API.Session;
|
||||||
using Torch.Commands.Permissions;
|
using Torch.Commands.Permissions;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
|
using Torch.Mod;
|
||||||
|
using Torch.Mod.Messages;
|
||||||
|
using VRage.Game;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
|
|
||||||
namespace Torch.Commands
|
namespace Torch.Commands
|
||||||
@@ -78,7 +81,7 @@ namespace Torch.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command("longhelp", "Get verbose help. Will send a long message, check the Comms tab.")]
|
[Command("longhelp", "Get verbose help. Will send a long message in a dialog window.")]
|
||||||
[Permission(MyPromoteLevel.None)]
|
[Permission(MyPromoteLevel.None)]
|
||||||
public void LongHelp()
|
public void LongHelp()
|
||||||
{
|
{
|
||||||
@@ -110,13 +113,20 @@ namespace Torch.Commands
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder("Available commands:\n");
|
var sb = new StringBuilder();
|
||||||
foreach (var command in commandManager.Commands.WalkTree())
|
foreach (var command in commandManager.Commands.WalkTree())
|
||||||
{
|
{
|
||||||
if (command.IsCommand)
|
if (command.IsCommand)
|
||||||
sb.AppendLine($"{command.Command.SyntaxHelp}\n {command.Command.HelpText}");
|
sb.AppendLine($"{command.Command.SyntaxHelp}\n {command.Command.HelpText}");
|
||||||
}
|
}
|
||||||
Context.Respond(sb.ToString());
|
|
||||||
|
if (!Context.SentBySelf)
|
||||||
|
{
|
||||||
|
var m = new DialogMessage("Torch Help", subtitle: "Available commands:", content: sb.ToString());
|
||||||
|
ModCommunication.SendMessageTo(m, Context.Player.SteamUserId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Context.Respond($"Available commands: {sb}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +182,13 @@ namespace Torch.Commands
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Command("notify", "Shows a message as a notification in the middle of all players' screens.")]
|
||||||
|
[Permission(MyPromoteLevel.Admin)]
|
||||||
|
public void Notify(string message, int disappearTimeMs = 2000, string font = "White")
|
||||||
|
{
|
||||||
|
ModCommunication.SendMessageToClients(new NotificationMessage(message, disappearTimeMs, font));
|
||||||
|
}
|
||||||
|
|
||||||
[Command("restart cancel", "Cancel a pending restart.")]
|
[Command("restart cancel", "Cancel a pending restart.")]
|
||||||
public void CancelRestart()
|
public void CancelRestart()
|
||||||
{
|
{
|
||||||
|
29
Torch/Patches/SessionDownloadPatch.cs
Normal file
29
Torch/Patches/SessionDownloadPatch.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Torch.Managers.PatchManager;
|
||||||
|
using Torch.Mod;
|
||||||
|
using VRage.Game;
|
||||||
|
|
||||||
|
namespace Torch.Patches
|
||||||
|
{
|
||||||
|
[PatchShim]
|
||||||
|
internal static class SessionDownloadPatch
|
||||||
|
{
|
||||||
|
internal static void Patch(PatchContext context)
|
||||||
|
{
|
||||||
|
context.GetPattern(typeof(MySession).GetMethod(nameof(MySession.GetWorld))).Suffixes.Add(typeof(SessionDownloadPatch).GetMethod(nameof(SuffixGetWorld), BindingFlags.Static | BindingFlags.NonPublic));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private static void SuffixGetWorld(ref MyObjectBuilder_World __result)
|
||||||
|
{
|
||||||
|
if (!__result.Checkpoint.Mods.Any(m => m.PublishedFileId == TorchModCore.MOD_ID))
|
||||||
|
__result.Checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(TorchModCore.MOD_ID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -18,7 +18,19 @@ namespace Torch
|
|||||||
public sealed class Persistent<T> : IDisposable where T : new()
|
public sealed class Persistent<T> : IDisposable where T : new()
|
||||||
{
|
{
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public T Data { get; private set; }
|
private T _data;
|
||||||
|
public T Data
|
||||||
|
{
|
||||||
|
get => _data;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (_data is INotifyPropertyChanged npc1)
|
||||||
|
npc1.PropertyChanged -= OnPropertyChanged;
|
||||||
|
_data = value;
|
||||||
|
if (_data is INotifyPropertyChanged npc2)
|
||||||
|
npc2.PropertyChanged += OnPropertyChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
~Persistent()
|
~Persistent()
|
||||||
{
|
{
|
||||||
@@ -29,13 +41,23 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
Data = data;
|
Data = data;
|
||||||
if (Data is INotifyPropertyChanged npc)
|
}
|
||||||
npc.PropertyChanged += OnPropertyChanged;
|
|
||||||
|
private Timer _saveConfigTimer;
|
||||||
|
|
||||||
|
private void SaveAsync()
|
||||||
|
{
|
||||||
|
if (_saveConfigTimer == null)
|
||||||
|
{
|
||||||
|
_saveConfigTimer = new Timer((x) => Save());
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveConfigTimer.Change(1000, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
Save();
|
SaveAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save(string path = null)
|
public void Save(string path = null)
|
||||||
@@ -52,20 +74,20 @@ namespace Torch
|
|||||||
|
|
||||||
public static Persistent<T> Load(string path, bool saveIfNew = true)
|
public static Persistent<T> Load(string path, bool saveIfNew = true)
|
||||||
{
|
{
|
||||||
var config = new Persistent<T>(path, new T());
|
Persistent<T> config = null;
|
||||||
|
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
var ser = new XmlSerializer(typeof(T));
|
var ser = new XmlSerializer(typeof(T));
|
||||||
using (var f = File.OpenText(path))
|
using (var f = File.OpenText(path))
|
||||||
{
|
{
|
||||||
config.Data = (T)ser.Deserialize(f);
|
config = new Persistent<T>(path, (T)ser.Deserialize(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (saveIfNew)
|
if (config == null)
|
||||||
{
|
config = new Persistent<T>(path, new T());
|
||||||
config.Save(path);
|
if (!File.Exists(path) && saveIfNew)
|
||||||
}
|
config.Save();
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -76,6 +98,7 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
if (Data is INotifyPropertyChanged npc)
|
if (Data is INotifyPropertyChanged npc)
|
||||||
npc.PropertyChanged -= OnPropertyChanged;
|
npc.PropertyChanged -= OnPropertyChanged;
|
||||||
|
_saveConfigTimer?.Dispose();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@@ -377,6 +377,7 @@ namespace Torch.Managers
|
|||||||
private void InstantiatePlugin(PluginManifest manifest, IEnumerable<Assembly> assemblies)
|
private void InstantiatePlugin(PluginManifest manifest, IEnumerable<Assembly> assemblies)
|
||||||
{
|
{
|
||||||
Type pluginType = null;
|
Type pluginType = null;
|
||||||
|
bool mult = false;
|
||||||
foreach (var asm in assemblies)
|
foreach (var asm in assemblies)
|
||||||
{
|
{
|
||||||
foreach (var type in asm.GetExportedTypes())
|
foreach (var type in asm.GetExportedTypes())
|
||||||
@@ -384,16 +385,26 @@ namespace Torch.Managers
|
|||||||
if (!type.GetInterfaces().Contains(typeof(ITorchPlugin)))
|
if (!type.GetInterfaces().Contains(typeof(ITorchPlugin)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
_log.Info($"Loading plugin at {type.FullName}");
|
||||||
|
|
||||||
if (pluginType != null)
|
if (pluginType != null)
|
||||||
{
|
{
|
||||||
_log.Error($"The plugin '{manifest.Name}' has multiple implementations of {nameof(ITorchPlugin)}, not loading.");
|
//_log.Error($"The plugin '{manifest.Name}' has multiple implementations of {nameof(ITorchPlugin)}, not loading.");
|
||||||
return;
|
//return;
|
||||||
|
mult = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginType = type;
|
pluginType = type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mult)
|
||||||
|
{
|
||||||
|
_log.Error($"The plugin '{manifest.Name}' has multiple implementations of {nameof(ITorchPlugin)}, not loading.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pluginType == null)
|
if (pluginType == null)
|
||||||
{
|
{
|
||||||
_log.Error($"The plugin '{manifest.Name}' does not have an implementation of {nameof(ITorchPlugin)}, not loading.");
|
_log.Error($"The plugin '{manifest.Name}' does not have an implementation of {nameof(ITorchPlugin)}, not loading.");
|
||||||
|
@@ -208,6 +208,7 @@
|
|||||||
<Compile Include="Patches\GameAnalyticsPatch.cs" />
|
<Compile Include="Patches\GameAnalyticsPatch.cs" />
|
||||||
<Compile Include="Patches\GameStatePatchShim.cs" />
|
<Compile Include="Patches\GameStatePatchShim.cs" />
|
||||||
<Compile Include="Patches\ObjectFactoryInitPatch.cs" />
|
<Compile Include="Patches\ObjectFactoryInitPatch.cs" />
|
||||||
|
<Compile Include="Patches\SessionDownloadPatch.cs" />
|
||||||
<Compile Include="Patches\TorchAsyncSaving.cs" />
|
<Compile Include="Patches\TorchAsyncSaving.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Collections\KeyTree.cs" />
|
<Compile Include="Collections\KeyTree.cs" />
|
||||||
@@ -269,6 +270,9 @@
|
|||||||
<DependentUpon>DictionaryEditor.xaml</DependentUpon>
|
<DependentUpon>DictionaryEditor.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Views\DisplayAttribute.cs" />
|
<Compile Include="Views\DisplayAttribute.cs" />
|
||||||
|
<Compile Include="Views\EmbeddedCollectionEditor.xaml.cs">
|
||||||
|
<DependentUpon>EmbeddedCollectionEditor.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Views\ObjectCollectionEditor.xaml.cs">
|
<Compile Include="Views\ObjectCollectionEditor.xaml.cs">
|
||||||
<DependentUpon>ObjectCollectionEditor.xaml</DependentUpon>
|
<DependentUpon>ObjectCollectionEditor.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -299,6 +303,10 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\EmbeddedCollectionEditor.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\ObjectCollectionEditor.xaml">
|
<Page Include="Views\ObjectCollectionEditor.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@@ -315,6 +323,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<Import Project="..\Torch.Mod\Torch.Mod.projitems" Label="Shared" />
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
</Project>
|
</Project>
|
@@ -16,6 +16,7 @@ namespace Torch.Views
|
|||||||
public bool Enabled = true;
|
public bool Enabled = true;
|
||||||
public bool Visible = true;
|
public bool Visible = true;
|
||||||
public bool ReadOnly = false;
|
public bool ReadOnly = false;
|
||||||
|
public Type EditorType = null;
|
||||||
|
|
||||||
public DisplayAttribute()
|
public DisplayAttribute()
|
||||||
{ }
|
{ }
|
||||||
|
32
Torch/Views/EmbeddedCollectionEditor.xaml
Normal file
32
Torch/Views/EmbeddedCollectionEditor.xaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<UserControl x:Class="Torch.Views.EmbeddedCollectionEditor"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Torch.Views"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
|
<Grid Width="Auto" Height="Auto">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition Height="25" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ListBox Grid.Row="0" Grid.Column="0" x:Name="ElementList"
|
||||||
|
HorizontalContentAlignment="Stretch" Margin="0" VerticalContentAlignment="Stretch" />
|
||||||
|
<GridSplitter Grid.Column="1" Grid.Row="0" Width="2" HorizontalAlignment="Left" VerticalAlignment="Stretch"
|
||||||
|
Background="Gray" ShowsPreview="True" VerticalContentAlignment="Stretch" />
|
||||||
|
|
||||||
|
<local:PropertyGrid Grid.Row="0" Grid.Column="1" x:Name="PGrid" Margin="4,0,0,0" />
|
||||||
|
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="AddButton" Content="Add"
|
||||||
|
HorizontalAlignment="Left" Margin="0" VerticalAlignment="Top"
|
||||||
|
Width="90" />
|
||||||
|
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="RemoveButton" Content="Remove"
|
||||||
|
HorizontalAlignment="Left" Margin="100,0,0,0"
|
||||||
|
VerticalAlignment="Top" Width="90" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
126
Torch/Views/EmbeddedCollectionEditor.xaml.cs
Normal file
126
Torch/Views/EmbeddedCollectionEditor.xaml.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
|
|
||||||
|
namespace Torch.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for EmbeddedCollectionEditor.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class EmbeddedCollectionEditor : UserControl
|
||||||
|
{
|
||||||
|
public EmbeddedCollectionEditor()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContextChanged += OnDataContextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
|
||||||
|
{
|
||||||
|
var c = dependencyPropertyChangedEventArgs.NewValue as ICollection;
|
||||||
|
//var c = DataContext as ICollection;
|
||||||
|
if (c != null)
|
||||||
|
Edit(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, MethodInfo> MethodCache = new Dictionary<Type, MethodInfo>();
|
||||||
|
private static readonly MethodInfo EditMethod;
|
||||||
|
|
||||||
|
|
||||||
|
static EmbeddedCollectionEditor()
|
||||||
|
{
|
||||||
|
var m = typeof(EmbeddedCollectionEditor).GetMethods();
|
||||||
|
EditMethod = m.First(mt => mt.Name == "Edit" && mt.GetGenericArguments().Length == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Edit(ICollection collection)
|
||||||
|
{
|
||||||
|
if (collection == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Cannot load null collection.", "Edit Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gt = collection.GetType().GenericTypeArguments[0];
|
||||||
|
|
||||||
|
//substitute for 'where T : new()'
|
||||||
|
if (gt.GetConstructor(Type.EmptyTypes) == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Unsupported collection type. Type must have paramaterless ctor.", "Edit Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MethodCache.TryGetValue(gt, out MethodInfo gm))
|
||||||
|
{
|
||||||
|
gm = EditMethod.MakeGenericMethod(gt);
|
||||||
|
MethodCache.Add(gt, gm);
|
||||||
|
}
|
||||||
|
|
||||||
|
gm.Invoke(this, new object[] {collection});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Edit<T>(ICollection<T> collection) where T : new()
|
||||||
|
{
|
||||||
|
var oc = collection as ObservableCollection<T> ?? new ObservableCollection<T>(collection);
|
||||||
|
|
||||||
|
AddButton.Click += (sender, args) =>
|
||||||
|
{
|
||||||
|
var t = new T();
|
||||||
|
oc.Add(t);
|
||||||
|
ElementList.SelectedItem = t;
|
||||||
|
};
|
||||||
|
|
||||||
|
RemoveButton.Click += RemoveButton_OnClick<T>;
|
||||||
|
ElementList.SelectionChanged += ElementsList_OnSelected;
|
||||||
|
|
||||||
|
ElementList.ItemsSource = oc;
|
||||||
|
oc.CollectionChanged += (sender, args) => RefreshList();
|
||||||
|
|
||||||
|
if (!(collection is ObservableCollection<T>))
|
||||||
|
{
|
||||||
|
collection.Clear();
|
||||||
|
foreach (var o in oc)
|
||||||
|
collection.Add(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveButton_OnClick<T>(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
//this is kinda shitty, but item count is normally small, and it prevents CollectionModifiedExceptions
|
||||||
|
var l = (ObservableCollection<T>)ElementList.ItemsSource;
|
||||||
|
var r = new List<T>(ElementList.SelectedItems.Cast<T>());
|
||||||
|
foreach (var item in r)
|
||||||
|
l.Remove(item);
|
||||||
|
if (l.Any())
|
||||||
|
ElementList.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ElementsList_OnSelected(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var item = (sender as ListBox)?.SelectedItem;
|
||||||
|
PGrid.DataContext = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshList()
|
||||||
|
{
|
||||||
|
ElementList.Items.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -6,24 +6,5 @@
|
|||||||
xmlns:local="clr-namespace:Torch.Views"
|
xmlns:local="clr-namespace:Torch.Views"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Height="370" Width="400" Title="Edit Collection">
|
Height="370" Width="400" Title="Edit Collection">
|
||||||
<Grid Width="Auto" Height="Auto">
|
<local:EmbeddedCollectionEditor x:Name="Editor"/>
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition />
|
|
||||||
<ColumnDefinition />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition Height="25"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<ListBox Grid.Row="0" Grid.Column="0" x:Name="ElementList"
|
|
||||||
HorizontalContentAlignment="Stretch" Margin="0" VerticalContentAlignment="Stretch" />
|
|
||||||
<GridSplitter Grid.Column="1" Grid.Row="0" Width="2" HorizontalAlignment="Left" VerticalAlignment="Stretch" Background="Gray" ShowsPreview="True" VerticalContentAlignment="Stretch"/>
|
|
||||||
|
|
||||||
<local:PropertyGrid Grid.Row="0" Grid.Column="1" x:Name="PGrid" Margin="4,0,0,0" />
|
|
||||||
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="AddButton" Content="Add" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Top"
|
|
||||||
Width="90" />
|
|
||||||
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="RemoveButton" Content="Remove" HorizontalAlignment="Left" Margin="100,0,0,0"
|
|
||||||
VerticalAlignment="Top" Width="90" />
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
</Window>
|
||||||
|
@@ -25,90 +25,18 @@ namespace Torch.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ObjectCollectionEditor : Window
|
public partial class ObjectCollectionEditor : Window
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<Type, MethodInfo> MethodCache = new Dictionary<Type, MethodInfo>();
|
|
||||||
private static readonly MethodInfo EditMethod;
|
|
||||||
|
|
||||||
public ObjectCollectionEditor()
|
public ObjectCollectionEditor()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ObjectCollectionEditor()
|
|
||||||
{
|
|
||||||
var m = typeof(ObjectCollectionEditor).GetMethods();
|
|
||||||
EditMethod = m.First(mt => mt.Name == "Edit" && mt.GetGenericArguments().Length == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Edit(ICollection collection, string title)
|
public void Edit(ICollection collection, string title)
|
||||||
{
|
{
|
||||||
if (collection == null)
|
Editor.Edit(collection);
|
||||||
{
|
|
||||||
MessageBox.Show("Cannot load null collection.", "Edit Error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var gt = collection.GetType().GenericTypeArguments[0];
|
|
||||||
|
|
||||||
//substitute for 'where T : new()'
|
|
||||||
if (gt.GetConstructor(Type.EmptyTypes) == null)
|
|
||||||
{
|
|
||||||
MessageBox.Show("Unsupported collection type. Type must have paramaterless ctor.", "Edit Error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MethodCache.TryGetValue(gt, out MethodInfo gm))
|
|
||||||
{
|
|
||||||
gm = EditMethod.MakeGenericMethod(gt);
|
|
||||||
MethodCache.Add(gt, gm);
|
|
||||||
}
|
|
||||||
|
|
||||||
gm.Invoke(this, new object[] {collection, title});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Edit<T>(ICollection<T> collection, string title) where T : new()
|
|
||||||
{
|
|
||||||
var oc = collection as ObservableCollection<T> ?? new ObservableCollection<T>(collection);
|
|
||||||
|
|
||||||
AddButton.Click += (sender, args) =>
|
|
||||||
{
|
|
||||||
var t = new T();
|
|
||||||
oc.Add(t);
|
|
||||||
ElementList.SelectedItem = t;
|
|
||||||
};
|
|
||||||
|
|
||||||
RemoveButton.Click += RemoveButton_OnClick<T>;
|
|
||||||
ElementList.SelectionChanged += ElementsList_OnSelected;
|
|
||||||
|
|
||||||
ElementList.ItemsSource = oc;
|
|
||||||
|
|
||||||
Title = title;
|
Title = title;
|
||||||
|
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
ShowDialog();
|
ShowDialog();
|
||||||
|
|
||||||
if (!(collection is ObservableCollection<T>))
|
|
||||||
{
|
|
||||||
collection.Clear();
|
|
||||||
foreach (var o in oc)
|
|
||||||
collection.Add(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveButton_OnClick<T>(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
//this is kinda shitty, but item count is normally small, and it prevents CollectionModifiedExceptions
|
|
||||||
var l = (ObservableCollection<T>)ElementList.ItemsSource;
|
|
||||||
var r = new List<T>(ElementList.SelectedItems.Cast<T>());
|
|
||||||
foreach (var item in r)
|
|
||||||
l.Remove(item);
|
|
||||||
if (l.Any())
|
|
||||||
ElementList.SelectedIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ElementsList_OnSelected(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var item = (sender as ListBox)?.SelectedItem;
|
|
||||||
PGrid.DataContext = item;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,6 @@
|
|||||||
<TextBox Name="TbFilter" Grid.Column="1" Margin="3" TextChanged="UpdateFilter" IsEnabled="False"/>
|
<TextBox Name="TbFilter" Grid.Column="1" Margin="3" TextChanged="UpdateFilter" IsEnabled="False"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ScrollViewer Grid.Row="1" x:Name="ScrollViewer"/>
|
<ScrollViewer Grid.Row="1" x:Name="ScrollViewer"/>
|
||||||
<TextBlock x:Name="TbDescription" Grid.Row="2" MinHeight="18"/>
|
<TextBlock x:Name="TbDescription" Grid.Row="2" MinHeight="18" TextWrapping="Wrap"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@@ -67,8 +67,8 @@ namespace Torch.Views
|
|||||||
var properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
|
var properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
|
||||||
var grid = new Grid();
|
var grid = new Grid();
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) });
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) });
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
|
||||||
|
|
||||||
var categories = new Dictionary<string, List<PropertyInfo>>();
|
var categories = new Dictionary<string, List<PropertyInfo>>();
|
||||||
var descriptors = new Dictionary<PropertyInfo, DisplayAttribute>(properties.Length);
|
var descriptors = new Dictionary<PropertyInfo, DisplayAttribute>(properties.Length);
|
||||||
@@ -145,7 +145,12 @@ namespace Torch.Views
|
|||||||
grid.Children.Add(text);
|
grid.Children.Add(text);
|
||||||
|
|
||||||
FrameworkElement valueControl;
|
FrameworkElement valueControl;
|
||||||
if (property.GetSetMethod() == null || descriptor?.ReadOnly == true)
|
if (descriptor?.EditorType != null)
|
||||||
|
{
|
||||||
|
valueControl = (FrameworkElement)Activator.CreateInstance(descriptor.EditorType);
|
||||||
|
valueControl.SetBinding(FrameworkElement.DataContextProperty, property.Name);
|
||||||
|
}
|
||||||
|
else if (property.GetSetMethod() == null && !(propertyType.IsGenericType && typeof(ICollection).IsAssignableFrom(propertyType.GetGenericTypeDefinition()))|| descriptor?.ReadOnly == true)
|
||||||
{
|
{
|
||||||
valueControl = new TextBlock();
|
valueControl = new TextBlock();
|
||||||
var binding = new Binding(property.Name)
|
var binding = new Binding(property.Name)
|
||||||
@@ -211,11 +216,21 @@ namespace Torch.Views
|
|||||||
|
|
||||||
valueControl = button;
|
valueControl = button;
|
||||||
}
|
}
|
||||||
else if (propertyType.IsPrimitive || propertyType == typeof(string))
|
else if (propertyType.IsPrimitive)
|
||||||
{
|
{
|
||||||
valueControl = new TextBox();
|
valueControl = new TextBox();
|
||||||
valueControl.SetBinding(TextBox.TextProperty, property.Name);
|
valueControl.SetBinding(TextBox.TextProperty, property.Name);
|
||||||
}
|
}
|
||||||
|
else if (propertyType == typeof(string))
|
||||||
|
{
|
||||||
|
var tb = new TextBox();
|
||||||
|
tb.TextWrapping = TextWrapping.Wrap;
|
||||||
|
tb.AcceptsReturn = true;
|
||||||
|
tb.AcceptsTab = true;
|
||||||
|
tb.SpellCheck.IsEnabled = true;
|
||||||
|
tb.SetBinding(TextBox.TextProperty, property.Name);
|
||||||
|
valueControl = tb;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var button = new Button
|
var button = new Button
|
||||||
|
Reference in New Issue
Block a user