Compare commits

..

11 Commits

Author SHA1 Message Date
zznty
aa7426dfa2 push to new nuget
All checks were successful
Release / Get Version (push) Successful in 5s
Release / Build and Publish Nuget (Torch.API) (push) Successful in 2m0s
Release / Build and Publish Nuget (Torch) (push) Successful in 3m6s
Release / Build and Publish Nuget (Torch.Server) (push) Successful in 3m29s
Release / Build and Publish Package (push) Successful in 4m59s
2024-11-09 19:33:43 +07:00
zznty
7ea0a4f8b9 add optional restart save timeout 2024-11-09 19:28:27 +07:00
zznty
d15985e918 ensure restart save is exclusive to prevent save corruption 2024-11-09 19:06:10 +07:00
zznty
58340e0f5d fix reoccurring entry dll path in args when application restarts 2024-11-09 19:05:40 +07:00
zznty
67bb3fd00c update autoupdate default config
All checks were successful
Release / Get Version (push) Successful in 6s
Release / Build and Publish Package (push) Successful in 8m10s
Release / Build and Publish Nuget (push) Successful in 2m1s
2024-11-01 07:15:49 +07:00
90546a78c2 Update README.md
Some checks failed
Release / Build and Publish Nuget (push) Has been cancelled
Release / Build and Publish Package (push) Has been cancelled
Release / Get Version (push) Has been cancelled
2024-10-27 13:54:31 +00:00
zznty
d301955609 move ui lifetime to a manager for better lifetime control
All checks were successful
Release / Get Version (push) Successful in 4s
Release / Build and Publish Nuget (push) Successful in 2m21s
Release / Build and Publish Package (push) Successful in 2m58s
2024-10-23 16:52:10 +07:00
zznty
264daf7515 fix torch not restarting when start occurs second time 2024-10-23 16:51:41 +07:00
zznty
f75ef55405 add dynamic world config
All checks were successful
Release / Get Version (push) Successful in 3m36s
Release / Build and Publish Nuget (push) Successful in 8m20s
Release / Build and Publish Package (push) Successful in 8m21s
2024-10-20 07:40:34 +07:00
Bishbash777
5804165d2b 205 fixes. (#590)
All checks were successful
Release / Get Version (push) Successful in 7s
Release / Build and Publish Nuget (push) Successful in 2m33s
Release / Build and Publish Package (push) Successful in 3m9s
(cherry picked from commit e41a26dbd2fb966b21757fa0248f6b4a4e716aad)
2024-10-15 01:18:02 +07:00
zznty
1960d2a0de fix world path being truncated if missing trailing slash
Some checks failed
Release / Get Version (push) Failing after 1s
Release / Build and Publish Nuget (push) Has been skipped
Release / Build and Publish Package (push) Has been skipped
2024-10-14 23:20:49 +07:00
30 changed files with 301 additions and 459 deletions

View File

@@ -5,7 +5,6 @@ on:
branches: [master]
env:
BUILD_CONFIGURATION: Release
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
@@ -33,36 +32,24 @@ jobs:
name: Build and Publish Nuget
runs-on: ubuntu-latest
needs: [get-version]
strategy:
matrix:
project: [ Torch.API, Torch, Torch.Server ]
steps:
- uses: actions/checkout@master
name: Checkout
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v4
name: Setup dotnet
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore Torch.Server/Torch.Server.csproj --locked-mode
run: dotnet restore ${{ matrix.project }}/${{ matrix.project }}.csproj --locked-mode
- name: Build
run: dotnet build Torch.Server/Torch.Server.csproj --no-restore -c ${{ env.BUILD_CONFIGURATION }} -p:Version="${{ needs.get-version.outputs.version }}" -p:AssemblyVersion="${{ needs.get-version.outputs.version }}"
- name: Pack
run: dotnet pack ${{ matrix.project }}/${{ matrix.project }}.csproj -o pack --no-restore -p:Version="${{ needs.get-version.outputs.version }}" -p:AssemblyVersion="${{ needs.get-version.outputs.version }}"
- run: dotnet pack -c Release ./Torch.API/Torch.API.csproj -o pack -p:Version="${{ needs.get-version.outputs.version }}" -p:AssemblyVersion="${{ needs.get-version.outputs.version }}" --no-build
- run: dotnet pack -c Release ./Torch/Torch.csproj -o pack -p:Version="${{ needs.get-version.outputs.version }}" -p:AssemblyVersion="${{ needs.get-version.outputs.version }}" --no-build
- run: dotnet pack -c Release ./Torch.Server/Torch.Server.csproj -o pack -p:Version="${{ needs.get-version.outputs.version }}" -p:AssemblyVersion="${{ needs.get-version.outputs.version }}" --no-build
- name: Install Sleet
run: dotnet tool install -g sleet --version 5.1.3
- name: Push Nuget Package
env:
SLEET_FEED_TYPE: s3
SLEET_FEED_PATH: https://nuget.storage.yandexcloud.net
SLEET_FEED_BUCKETNAME: nuget
SLEET_FEED_SERVICEURL: https://storage.yandexcloud.net
SLEET_FEED_ACCESSKEYID: ${{ secrets.S3_KEY_ID }}
SLEET_FEED_SECRETACCESSKEY: ${{ secrets.S3_KEY }}
run: /root/.dotnet/tools/sleet push ./pack
- name: Push
run: dotnet nuget push -s https://ng.zznty.ru/v3/index.json -k ${{ secrets.NUGET_API_KEY }} ./pack/${{ matrix.project }}.${{ needs.get-version.outputs.version }}.nupkg
build:
name: Build and Publish Package
@@ -72,16 +59,14 @@ jobs:
- uses: actions/checkout@master
name: Checkout
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v4
name: Setup dotnet
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore Torch.Server/Torch.Server.csproj --locked-mode -r win-x64
- name: Publish
run: dotnet publish Torch.Server/Torch.Server.csproj --no-restore --sc -r win-x64 -c ${{ env.BUILD_CONFIGURATION }} -o ./publish -p:Version="${{ needs.get-version.outputs.version }}" -p:AssemblyVersion="${{ needs.get-version.outputs.version }}"
run: dotnet publish Torch.Server/Torch.Server.csproj --no-restore --sc -r win-x64 -o ./publish -p:Version="${{ needs.get-version.outputs.version }}" -p:AssemblyVersion="${{ needs.get-version.outputs.version }}"
- uses: vimtor/action-zip@v1
name: Zip Release

View File

@@ -11,7 +11,7 @@ Torch is the successor to SE Server Extender and gives server admins the tools t
* Extensible using the Torch plugin system
### Fork Difference
* .NET 6.0 runtime
* .NET 8.0 runtime
* Optimized in-game scripts (also newer compiler & language versions)
* Better configuration via cli arguments, environment variables or xml config
* Designed to run multiple instance from same install directory without having you to waste disk space
@@ -19,7 +19,9 @@ Torch is the successor to SE Server Extender and gives server admins the tools t
### Discord
If you have any questions or issues please join our [discord](https://discord.gg/UyYFSe3TyQ)
If you have any questions or issues please join our discord
[![](https://dcbadge.vercel.app/api/server/VAb2zgXHAN)](https://discord.gg/VAb2zgXHAN)
### Installation
@@ -30,7 +32,7 @@ If you have any questions or issues please join our [discord](https://discord.gg
# Building
As a regular dotnet project with cli by running `dotnet build Torch.Server/Torch.Server.csproj`, with VS 2022 or higher, with JB Rider or Fleet.
As a regular dotnet project with cli by running `dotnet build Torch.Server/Torch.Server.csproj`, with VS 2022 or higher or JB Rider.
If you have a more enjoyable server experience because of Torch, please consider supporting us on Patreon. (https://www.patreon.com/TorchSE)

View File

@@ -34,6 +34,7 @@ namespace Torch
string LoginToken { get; set; }
UpdateSource UpdateSource { get; set; }
List<string> Packages { get; set; }
int RestartSaveTimeout { get; set; }
void Save(string path = null);
}

View File

@@ -33,6 +33,11 @@
/// <summary>
/// The save operation timed out
/// </summary>
TimedOut = -5
TimedOut = -5,
/// <summary>
/// Another save operation is in progress
/// </summary>
ConcurrentSaveInProgress = -6
}
}

View File

@@ -18,7 +18,7 @@
<PackageReference Include="NuGet.Commands" Version="6.11.0" />
<PackageReference Include="NuGet.DependencyResolver.Core" Version="6.11.0" />
<PackageReference Include="SemanticVersioning" Version="2.0.2" />
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.204.18">
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.*">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="System.Linq.Async" Version="6.0.1" />

View File

@@ -54,9 +54,9 @@
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.204.18, )",
"resolved": "1.204.18",
"contentHash": "GT7/9CBMx4jjor41zLOOl87YYM/JdJD8xp9ccXyuhP2oUaz25H3ZmCQuGeAuZNENKru1a/7hZrId4PwlMDGoew==",
"requested": "[1.*, )",
"resolved": "1.205.23",
"contentHash": "J7mF5hY39PzzCZps6vhIRzKiq8vD6Af9TgumTJR068vjEi+BzyeEFhqX+cl2Dd1ngOmsBtGWc5m+vxgTfs5YuA==",
"dependencies": {
"SharpDX": "4.2.0-keen-cringe",
"protobuf-net": "1.0.0"

View File

@@ -468,8 +468,8 @@
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Transitive",
"resolved": "1.204.18",
"contentHash": "GT7/9CBMx4jjor41zLOOl87YYM/JdJD8xp9ccXyuhP2oUaz25H3ZmCQuGeAuZNENKru1a/7hZrId4PwlMDGoew==",
"resolved": "1.205.23",
"contentHash": "J7mF5hY39PzzCZps6vhIRzKiq8vD6Af9TgumTJR068vjEi+BzyeEFhqX+cl2Dd1ngOmsBtGWc5m+vxgTfs5YuA==",
"dependencies": {
"SharpDX": "4.2.0-keen-cringe",
"protobuf-net": "1.0.0"
@@ -630,7 +630,7 @@
"NuGet.Commands": "[6.11.0, )",
"NuGet.DependencyResolver.Core": "[6.11.0, )",
"SemanticVersioning": "[2.0.2, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.204.18, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )",
"System.Linq.Async": "[6.0.1, )"
}
},

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace Torch.Server;
internal static class ApplicationRestartHelper
{
public static void RestartApplication()
{
var exe = Path.Combine(AppContext.BaseDirectory, "Torch.Server.exe");
var args = Environment.GetCommandLineArgs().ToList();
args.RemoveAt(0); // dll entry path
args.RemoveConfigItem("--tempAutostart");
args.RemoveConfigItem("--waitForPid");
args.AddRange([
"--waitForPid", Environment.ProcessId.ToString(),
"--tempAutostart", "true"
]);
Process.Start(new ProcessStartInfo(exe, args));
}
private static void RemoveConfigItem(this List<string> list, string item)
{
var index = list.BinarySearch(item, StringComparer.OrdinalIgnoreCase);
if (index < 0) return;
list.RemoveAt(index);
list.RemoveAt(index);
}
}

View File

@@ -94,27 +94,11 @@ namespace Torch.Server
_server.Init();
var uiThread = new Thread(() =>
{
var ui = new TorchUI(_server);
if (!Config.Autostart && !Config.TempAutostart) return;
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
ui.ShowDialog();
});
uiThread.SetApartmentState(ApartmentState.STA);
uiThread.Start();
if (Config.Autostart || Config.TempAutostart)
{
Config.TempAutostart = false;
_server.Start();
}
uiThread.Join();
}
}
public static async Task RunSteamCmdAsync(IConfiguration configuration)

View File

@@ -134,7 +134,7 @@ namespace Torch.Server.Managers
worldPath = DedicatedConfig.Worlds.First().WorldPath;
else
// make sure we won't end up with a file path when we expect it to be a directory
worldPath = Path.EndsInDirectorySeparator(worldPath) ? worldPath : Path.GetDirectoryName(worldPath);
worldPath = worldPath.EndsWith(".sbc") ? Path.GetDirectoryName(worldPath) : worldPath;
SelectWorld(worldPath, false);
@@ -235,7 +235,7 @@ namespace Torch.Server.Managers
try
{
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
MyObjectBuilderSerializerKeen.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
if (checkpoint == null)
{
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {Torch.Config.InstancePath})");
@@ -250,7 +250,7 @@ namespace Torch.Server.Managers
Log.Debug("Loaded mod list from world");
if (!modsOnly)
DedicatedConfig.SessionSettings = new SessionSettingsViewModel(checkpoint.Settings);
DedicatedConfig.SessionSettings = new DynamicViewModel<MyObjectBuilder_SessionSettings>(checkpoint.Settings);
}
catch (Exception e)
{
@@ -368,14 +368,14 @@ namespace Torch.Server.Managers
public void LoadSandbox()
{
if (!MyObjectBuilderSerializer.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint))
if (!MyObjectBuilderSerializerKeen.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint))
throw new SerializationException("Error reading checkpoint, see keen log for details");
Checkpoint = new CheckpointViewModel(checkpoint);
// migrate old saves
if (File.Exists(_worldConfigPath))
{
if (!MyObjectBuilderSerializer.DeserializeXML(_worldConfigPath, out MyObjectBuilder_WorldConfiguration worldConfig))
if (!MyObjectBuilderSerializerKeen.DeserializeXML(_worldConfigPath, out MyObjectBuilder_WorldConfiguration worldConfig))
throw new SerializationException("Error reading settings, see keen log for details");
WorldConfiguration = new WorldConfigurationViewModel(worldConfig);
}

View File

@@ -0,0 +1,37 @@
using System.Threading;
using System.Windows.Threading;
using JetBrains.Annotations;
using Torch.Managers;
namespace Torch.Server.Managers;
public class UiManager(TorchServer torchInstance) : Manager(torchInstance)
{
[CanBeNull] private Thread _uiThread;
[CanBeNull] private Dispatcher _dispatcher;
public override void Attach()
{
_uiThread = new Thread(() =>
{
var ui = new TorchUI(torchInstance);
_dispatcher = Dispatcher.CurrentDispatcher;
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(_dispatcher));
ui.ShowDialog();
});
_uiThread.SetApartmentState(ApartmentState.STA);
_uiThread.Start();
}
public override void Detach()
{
_dispatcher?.InvokeShutdown();
if (Thread.CurrentThread != _uiThread)
_uiThread?.Join();
}
}

View File

@@ -114,14 +114,17 @@ public class TorchConfig : ViewModel, ITorchConfig
public UpdateSource UpdateSource { get; set; } = new()
{
Repository = "PveTeam/Torch",
Url = "https://api.github.com",
Repository = "PvE/Torch",
Url = "https://git.zznty.ru/api/v1",
SourceType = UpdateSourceType.Github
};
[Display(Name = "Packages", Description = "Packages to install and use.", GroupName = "Server")]
public List<string> Packages { get; set; } = new();
[Display(Name = "Restart Save Timeout", Description = "Timeout for save operation on restart in seconds.", GroupName = "Server")]
public int RestartSaveTimeout { get; set; }
// for backward compatibility
public void Save(string path = null) => Initializer.Instance?.ConfigPersistent?.Save(path);
}

View File

@@ -56,6 +56,8 @@ namespace Torch.Server
if (config.EntityManagerEnabled)
AddManager(new EntityControlManager(this));
AddManager(new RemoteAPIManager(this));
if (!ApplicationContext.Current.IsService && !config.NoGui)
AddManager(new UiManager(this));
var sessionManager = Managers.GetManager<ITorchSessionManager>();
sessionManager.AddFactory(_ => new MultiplayerManagerDedicated(this));
@@ -185,7 +187,7 @@ namespace Torch.Server
if (IsRunning || HasRun)
{
Restart();
Restart(false);
return;
}
@@ -246,7 +248,8 @@ namespace Torch.Server
{
if (save)
{
var saveResult = Save().Result;
var saveResult = Save(Config.RestartSaveTimeout == 0 ? -1 : Config.RestartSaveTimeout,
exclusive: true).Result;
if (saveResult is not (GameSaveResult.Success or GameSaveResult.TimedOut))
{
Log.Error("Save failed due to {Reason}. Restart aborted!", saveResult);
@@ -267,24 +270,7 @@ namespace Torch.Server
)
return;
var exe = Path.Combine(AppContext.BaseDirectory, "Torch.Server.exe");
var args = Environment.GetCommandLineArgs();
for (var i = 0; i < args.Length; i++)
{
if (args[i].Contains(' '))
args[i] = $"\"{args[i]}\"";
if (!args[i].Contains("--tempAutostart", StringComparison.InvariantCultureIgnoreCase) &&
!args[i].Contains("--waitForPid", StringComparison.InvariantCultureIgnoreCase))
continue;
args[i] = string.Empty;
args[++i] = string.Empty;
}
Process.Start(exe, $"--waitForPid {Environment.ProcessId} --tempAutostart true {string.Join(" ", args)}");
ApplicationRestartHelper.RestartApplication();
})
{
Name = "Restart thread"

View File

@@ -33,24 +33,7 @@ internal class UnhandledExceptionHandler
Console.WriteLine("Restarting in 5 seconds.");
Thread.Sleep(5000);
var exe = Path.Combine(AppContext.BaseDirectory, "Torch.Server.exe");
var args = Environment.GetCommandLineArgs();
for (var i = 0; i < args.Length; i++)
{
if (args[i].Contains(' '))
args[i] = $"\"{args[i]}\"";
if (!args[i].Contains("--tempAutostart", StringComparison.InvariantCultureIgnoreCase) &&
!args[i].Contains("--waitForPid", StringComparison.InvariantCultureIgnoreCase))
continue;
args[i] = string.Empty;
args[++i] = string.Empty;
}
Process.Start(exe, $"--waitForPid {Environment.ProcessId} --tempAutostart true {string.Join(" ", args)}");
ApplicationRestartHelper.RestartApplication();
}
else
{

View File

@@ -1,15 +1,17 @@
namespace Torch.Server.ViewModels
using VRage.Game;
namespace Torch.Server.ViewModels
{
public class BlockLimitViewModel : ViewModel
{
private SessionSettingsViewModel _sessionSettings;
private DynamicViewModel<MyObjectBuilder_SessionSettings> _sessionSettings;
public string BlockType { get; set; }
public short Limit { get; set; }
//public CommandBinding Delete { get; } = new CommandBinding(new DeleteCommand());
public BlockLimitViewModel(SessionSettingsViewModel sessionSettings, string blockType, short limit)
public BlockLimitViewModel(DynamicViewModel<MyObjectBuilder_SessionSettings> sessionSettings, string blockType, short limit)
{
_sessionSettings = sessionSettings;
BlockType = blockType;

View File

@@ -18,7 +18,7 @@ namespace Torch.Server.ViewModels
{
_config = configDedicated;
_config.IgnoreLastSession = true;
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
SessionSettings = new DynamicViewModel<MyObjectBuilder_SessionSettings>(_config.SessionSettings);
Task.Run(() => UpdateAllModInfosAsync());
}
@@ -48,7 +48,7 @@ namespace Torch.Server.ViewModels
return true;
}
public SessionSettingsViewModel SessionSettings { get; set; }
public DynamicViewModel<MyObjectBuilder_SessionSettings> SessionSettings { get; set; }
public MtObservableList<WorldViewModel> Worlds { get; } = new MtObservableList<WorldViewModel>();
private WorldViewModel _selectedWorld;

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
using JetBrains.Annotations;
namespace Torch.Server.ViewModels;
public class DynamicViewModel<T>(T obj) : ViewModel
{
private readonly T _obj = obj;
public ViewModel Wrapper { get; } = CreateProxy(obj);
public static implicit operator T(DynamicViewModel<T> obj) => obj._obj;
private static ViewModel CreateProxy(T obj)
{
if (DynamicViewModel.Proxies.TryGetValue(typeof(T), out var proxy))
return (ViewModel)Activator.CreateInstance(proxy, obj);
var viewModelSetMethod = AccessTools.GetDeclaredMethods(typeof(ViewModel))
.First(b => b.Name == "SetValue" && b.GetParameters()[0].ParameterType.IsByRef);
DynamicViewModel.ModuleBuilder ??=
AssemblyBuilder.DefineDynamicAssembly(new("Torch.Server.ViewModels.Generated"), AssemblyBuilderAccess.Run)
.DefineDynamicModule("Torch.Server.ViewModels.Generated");
var type = DynamicViewModel.ModuleBuilder.DefineType($"{typeof(T).FullName}ViewModel",
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, typeof(ViewModel));
var instanceField = type.DefineField("_instance", typeof(T), FieldAttributes.Private | FieldAttributes.InitOnly);
var ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, [typeof(T)]);
{
var il = ctor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, instanceField);
il.Emit(OpCodes.Ret);
}
foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public))
{
var prop = type.DefineProperty(field.Name, PropertyAttributes.None, field.FieldType, null);
foreach (var customAttribute in field.GetCustomAttributesData())
{
var hasCustomArgs = customAttribute.NamedArguments?.Any() ?? false;
var customArgsField = hasCustomArgs && customAttribute.NamedArguments[0].IsField;
var constructorArgs = customAttribute.ConstructorArguments.Select(b => b.Value).ToArray();
CustomAttributeBuilder attributeBuilder;
if (!hasCustomArgs)
attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs);
else if (customArgsField)
attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs,
customAttribute.NamedArguments.Select(b => (FieldInfo)b.MemberInfo).ToArray(),
customAttribute.NamedArguments.Select(b => b.TypedValue.Value).ToArray());
else
attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs,
customAttribute.NamedArguments.Select(b => (PropertyInfo)b.MemberInfo).ToArray(),
customAttribute.NamedArguments.Select(b => b.TypedValue.Value).ToArray());
prop.SetCustomAttribute(attributeBuilder);
}
var getMethod = type.DefineMethod($"get_{field.Name}", MethodAttributes.Public | MethodAttributes.HideBySig, field.FieldType, Type.EmptyTypes);
{
var il = getMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, instanceField);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Ret);
}
prop.SetGetMethod(getMethod);
if (field.IsInitOnly) continue;
var setMethod = type.DefineMethod($"set_{field.Name}", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(void),
[field.FieldType]);
{
var il = setMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldfld, instanceField);
il.Emit(OpCodes.Ldflda, field);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldstr, field.Name);
il.Emit(OpCodes.Callvirt, viewModelSetMethod.MakeGenericMethod(field.FieldType));
il.Emit(OpCodes.Ret);
}
prop.SetSetMethod(setMethod);
}
proxy = type.CreateType();
DynamicViewModel.Proxies[typeof(T)] = proxy;
return (ViewModel)Activator.CreateInstance(proxy, obj);
}
}
file static class DynamicViewModel
{
public static readonly Dictionary<Type, Type> Proxies = [];
[CanBeNull] public static ModuleBuilder ModuleBuilder;
}

View File

@@ -1,321 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel;
using VRage.Game;
using VRage.Library.Utils;
namespace Torch.Server.ViewModels
{
public class SessionSettingsViewModel : ViewModel
{
private MyObjectBuilder_SessionSettings _settings;
[Torch.Views.Display(Description = "The type of the game mode.", Name = "Game Mode", GroupName = "Others")]
public MyGameModeEnum GameMode { get => _settings.GameMode; set => SetValue(ref _settings.GameMode, value); }
[Torch.Views.Display(Description = "The type of the game online mode.", Name = "Online Mode", GroupName = "Others")]
public MyOnlineModeEnum OnlineMode { get => _settings.OnlineMode; set => SetValue(ref _settings.OnlineMode, value); }
[Torch.Views.Display(Description = "The multiplier for character inventory size.", Name = "Character Inventory Size", GroupName = "Multipliers")]
public float CharacterInventorySizeMultiplier { get => _settings.InventorySizeMultiplier; set => SetValue(ref _settings.InventorySizeMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for block inventory size.", Name = "Block Inventory Size", GroupName = "Multipliers")]
public float BlockInventorySizeMultiplier { get => _settings.BlocksInventorySizeMultiplier; set => SetValue(ref _settings.BlocksInventorySizeMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for assembler speed.", Name = "Assembler Speed", GroupName = "Multipliers")]
public float AssemblerSpeedMultiplier { get => _settings.AssemblerSpeedMultiplier; set => SetValue(ref _settings.AssemblerSpeedMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for assembler efficiency.", Name = "Assembler Efficiency", GroupName = "Multipliers")]
public float AssemblerEfficiencyMultiplier { get => _settings.AssemblerEfficiencyMultiplier; set => SetValue(ref _settings.AssemblerEfficiencyMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for refinery speed.", Name = "Refinery Speed", GroupName = "Multipliers")]
public float RefinerySpeedMultiplier { get => _settings.RefinerySpeedMultiplier; set => SetValue(ref _settings.RefinerySpeedMultiplier, value); }
[Torch.Views.Display(Description = "The maximum number of connected players.", Name = "Max Players", GroupName = "Players")]
public short MaxPlayers { get => _settings.MaxPlayers; set => SetValue(ref _settings.MaxPlayers, value); }
[Torch.Views.Display(Description = "The maximum number of existing floating objects.", Name = "Max Floating Objects", GroupName = "Environment")]
public short MaxFloatingObjects { get => _settings.MaxFloatingObjects; set => SetValue(ref _settings.MaxFloatingObjects, value); }
[Torch.Views.Display(Description = "The maximum number of backup saves.", Name = "Max Backup Saves", GroupName = "Others")]
public short MaxBackupSaves { get => _settings.MaxBackupSaves; set => SetValue(ref _settings.MaxBackupSaves, value); }
[Torch.Views.Display(Description = "The maximum number of blocks in one grid.", Name = "Max Grid Blocks", GroupName = "Block Limits")]
public int MaxGridSize { get => _settings.MaxGridSize; set => SetValue(ref _settings.MaxGridSize, value); }
[Torch.Views.Display(Description = "The maximum number of blocks per player.", Name = "Max Blocks per Player", GroupName = "Block Limits")]
public int MaxBlocksPerPlayer { get => _settings.MaxBlocksPerPlayer; set => SetValue(ref _settings.MaxBlocksPerPlayer, value); }
[Torch.Views.Display(Description = "The total number of Performance Cost Units in the world.", Name = "World PCU", GroupName = "Block Limits")]
public int TotalPCU { get => _settings.TotalPCU; set => SetValue(ref _settings.TotalPCU, value); }
[Torch.Views.Display(Description = "The maximum number of existing factions in the world.", Name = "Max Factions Count", GroupName = "Block Limits")]
public int MaxFactionsCount { get => _settings.MaxFactionsCount; set => SetValue(ref _settings.MaxFactionsCount, value); }
[Torch.Views.Display(Description = "Defines block limits mode.", Name = "Block Limits Mode", GroupName = "Block Limits")]
public MyBlockLimitsEnabledEnum BlockLimitsEnabled { get => _settings.BlockLimitsEnabled; set => SetValue(ref _settings.BlockLimitsEnabled, value); }
[Torch.Views.Display(Description = "Enables possibility to remove grid remotely from the world by an author.", Name = "Enable Remote Grid Removal", GroupName = "Others")]
public bool EnableRemoteBlockRemoval { get => _settings.EnableRemoteBlockRemoval; set => SetValue(ref _settings.EnableRemoteBlockRemoval, value); }
[Torch.Views.Display(Description = "Defines hostility of the environment.", Name = "Environment Hostility", GroupName = "Environment")]
public MyEnvironmentHostilityEnum EnvironmentHostility { get => _settings.EnvironmentHostility; set => SetValue(ref _settings.EnvironmentHostility, value); }
[Torch.Views.Display(Description = "Enables auto healing of the character.", Name = "Auto Healing", GroupName = "Players")]
public bool AutoHealing { get => _settings.AutoHealing; set => SetValue(ref _settings.AutoHealing, value); }
[Torch.Views.Display(Description = "Enables copy and paste feature.", Name = "Enable Copy & Paste", GroupName = "Players")]
public bool EnableCopyPaste { get => _settings.EnableCopyPaste; set => SetValue(ref _settings.EnableCopyPaste, value); }
[Torch.Views.Display(Description = "Enables weapons.", Name = "Enable Weapons", GroupName = "Others")]
public bool WeaponsEnabled { get => _settings.WeaponsEnabled; set => SetValue(ref _settings.WeaponsEnabled, value); }
[Torch.Views.Display(Description = "", Name = "Show Player Names on HUD", GroupName = "Players")]
public bool ShowPlayerNamesOnHud { get => _settings.ShowPlayerNamesOnHud; set => SetValue(ref _settings.ShowPlayerNamesOnHud, value); }
[Torch.Views.Display(Description = "Enables thruster damage.", Name = "Enable Thruster Damage", GroupName = "Others")]
public bool ThrusterDamage { get => _settings.ThrusterDamage; set => SetValue(ref _settings.ThrusterDamage, value); }
[Torch.Views.Display(Description = "Enables spawning of cargo ships.", Name = "Enable Cargo Ships", GroupName = "NPCs")]
public bool CargoShipsEnabled { get => _settings.CargoShipsEnabled; set => SetValue(ref _settings.CargoShipsEnabled, value); }
[Torch.Views.Display(Description = "Enables spectator camera.", Name = "Enable Spectator Camera", GroupName = "Others")]
public bool EnableSpectator { get => _settings.EnableSpectator; set => SetValue(ref _settings.EnableSpectator, value); }
/// <summary>
/// Size of the edge of the world area cube.
/// Don't use directly, as it is error-prone (it's km instead of m and edge size instead of half-extent)
/// Rather use MyEntities.WorldHalfExtent()
/// </summary>
[Torch.Views.Display(Description = "Defines the size of the world.", Name = "World Size [km]", GroupName = "Environment")]
public int WorldSizeKm { get => _settings.WorldSizeKm; set => SetValue(ref _settings.WorldSizeKm, value); }
[Torch.Views.Display(Description = "When enabled respawn ship is removed after player logout.", Name = "Remove Respawn Ships on Logoff", GroupName = "Others")]
public bool RespawnShipDelete { get => _settings.RespawnShipDelete; set => SetValue(ref _settings.RespawnShipDelete, value); }
[Torch.Views.Display(Description = "", Name = "Reset Ownership", GroupName = "Players")]
public bool ResetOwnership { get => _settings.ResetOwnership; set => SetValue(ref _settings.ResetOwnership, value); }
[Torch.Views.Display(Description = "The multiplier for welder speed.", Name = "Welder Speed", GroupName = "Multipliers")]
public float WelderSpeedMultiplier { get => _settings.WelderSpeedMultiplier; set => SetValue(ref _settings.WelderSpeedMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for grinder speed.", Name = "Grinder Speed", GroupName = "Multipliers")]
public float GrinderSpeedMultiplier { get => _settings.GrinderSpeedMultiplier; set => SetValue(ref _settings.GrinderSpeedMultiplier, value); }
[Torch.Views.Display(Description = "Enables realistic sounds.", Name = "Enable Realistic Sound", GroupName = "Environment")]
public bool RealisticSound { get => _settings.RealisticSound; set => SetValue(ref _settings.RealisticSound, value); }
[Torch.Views.Display(Description = "The multiplier for hacking speed.", Name = "Hacking Speed", GroupName = "Multipliers")]
public float HackSpeedMultiplier { get => _settings.HackSpeedMultiplier; set => SetValue(ref _settings.HackSpeedMultiplier, value); }
[Torch.Views.Display(Description = "Enables permanent death.", Name = "Permanent Death", GroupName = "Players")]
public bool? PermanentDeath { get => _settings.PermanentDeath; set => SetValue(ref _settings.PermanentDeath, value); }
[Torch.Views.Display(Description = "Defines autosave interval.", Name = "Autosave Interval [mins]", GroupName = "Others")]
public uint AutoSaveInMinutes { get => _settings.AutoSaveInMinutes; set => SetValue(ref _settings.AutoSaveInMinutes, value); }
[Torch.Views.Display(Description = "Enables saving from the menu.", Name = "Enable Saving from Menu", GroupName = "Others")]
public bool EnableSaving { get => _settings.EnableSaving; set => SetValue(ref _settings.EnableSaving, value); }
[Torch.Views.Display(Description = "Enables respawn screen.", Name = "Enable Respawn Screen in the Game", GroupName = "Players")]
public bool StartInRespawnScreen { get => _settings.StartInRespawnScreen; set => SetValue(ref _settings.StartInRespawnScreen, value); }
[Torch.Views.Display(Description = "Enables research.", Name = "Enable Research", GroupName = "Players")]
public bool EnableResearch { get => _settings.EnableResearch; set => SetValue(ref _settings.EnableResearch, value); }
[Torch.Views.Display(Description = "Enables Good.bot hints.", Name = "Enable Good.bot hints", GroupName = "Players")]
public bool EnableGoodBotHints { get => _settings.EnableGoodBotHints; set => SetValue(ref _settings.EnableGoodBotHints, value); }
[Torch.Views.Display(Description = "Enables infinite ammunition in survival game mode.", Name = "Enable Infinite Ammunition in Survival", GroupName = "Others")]
public bool InfiniteAmmo { get => _settings.InfiniteAmmo; set => SetValue(ref _settings.InfiniteAmmo, value); }
[Torch.Views.Display(Description = "Enables drop containers (unknown signals).", Name = "Enable Drop Containers", GroupName = "Others")]
public bool EnableContainerDrops { get => _settings.EnableContainerDrops; set => SetValue(ref _settings.EnableContainerDrops, value); }
[Torch.Views.Display(Description = "The multiplier for respawn ship timer.", Name = "Respawn Ship Time Multiplier", GroupName = "Players")]
public float SpawnShipTimeMultiplier { get => _settings.SpawnShipTimeMultiplier; set => SetValue(ref _settings.SpawnShipTimeMultiplier, value); }
[Torch.Views.Display(Description = "Defines density of the procedurally generated content.", Name = "Procedural Density", GroupName = "Environment")]
public float ProceduralDensity { get => _settings.ProceduralDensity; set => SetValue(ref _settings.ProceduralDensity, value); }
[Torch.Views.Display(Description = "Defines unique starting seed for the procedurally generated content.", Name = "Procedural Seed", GroupName = "Environment")]
public int ProceduralSeed { get => _settings.ProceduralSeed; set => SetValue(ref _settings.ProceduralSeed, value); }
[Torch.Views.Display(Description = "Enables destruction feature for the blocks.", Name = "Enable Destructible Blocks", GroupName = "Environment")]
public bool DestructibleBlocks { get => _settings.DestructibleBlocks; set => SetValue(ref _settings.DestructibleBlocks, value); }
[Torch.Views.Display(Description = "Enables in game scripts.", Name = "Enable Ingame Scripts", GroupName = "Others")]
public bool EnableIngameScripts { get => _settings.EnableIngameScripts; set => SetValue(ref _settings.EnableIngameScripts, value); }
[Torch.Views.Display(Description = "", Name = "Flora Density Multiplier", GroupName = "Environment")]
public float FloraDensityMultiplier { get => _settings.FloraDensityMultiplier; set => SetValue(ref _settings.FloraDensityMultiplier, value); }
[Torch.Views.Display(Description = "Enables tool shake feature.", Name = "Enable Tool Shake", GroupName = "Players")]
[DefaultValue(false)]
public bool EnableToolShake { get => _settings.EnableToolShake; set => SetValue(ref _settings.EnableToolShake, value); }
[Torch.Views.Display(Description = "", Name = "Voxel Generator Version", GroupName = "Environment")]
public int VoxelGeneratorVersion { get => _settings.VoxelGeneratorVersion; set => SetValue(ref _settings.VoxelGeneratorVersion, value); }
[Torch.Views.Display(Description = "Enables oxygen in the world.", Name = "Enable Oxygen", GroupName = "Environment")]
public bool EnableOxygen { get => _settings.EnableOxygen; set => SetValue(ref _settings.EnableOxygen, value); }
[Torch.Views.Display(Description = "Enables airtightness in the world.", Name = "Enable Airtightness", GroupName = "Environment")]
public bool EnableOxygenPressurization { get => _settings.EnableOxygenPressurization; set => SetValue(ref _settings.EnableOxygenPressurization, value); }
[Torch.Views.Display(Description = "Enables 3rd person camera.", Name = "Enable 3rd Person Camera", GroupName = "Players")]
public bool Enable3rdPersonView { get => _settings.Enable3rdPersonView; set => SetValue(ref _settings.Enable3rdPersonView, value); }
[Torch.Views.Display(Description = "Enables random encounters in the world.", Name = "Enable Encounters", GroupName = "NPCs")]
public bool EnableEncounters { get => _settings.EnableEncounters; set => SetValue(ref _settings.EnableEncounters, value); }
[Torch.Views.Display(Description = "Enables possibility of converting grid to station.", Name = "Enable Convert to Station", GroupName = "Others")]
public bool EnableConvertToStation { get => _settings.EnableConvertToStation; set => SetValue(ref _settings.EnableConvertToStation, value); }
[Torch.Views.Display(Description = "Enables possibility of station grid inside voxel.", Name = "Enable Station Grid with Voxel", GroupName = "Environment")]
public bool StationVoxelSupport { get => _settings.StationVoxelSupport; set => SetValue(ref _settings.StationVoxelSupport, value); }
[Torch.Views.Display(Description = "Enables sun rotation.", Name = "Enable Sun Rotation", GroupName = "Environment")]
public bool EnableSunRotation { get => _settings.EnableSunRotation; set => SetValue(ref _settings.EnableSunRotation, value); }
[Torch.Views.Display(Description = "Enables respawn ships.", Name = "Enable Respawn Ships", GroupName = "Others")]
public bool EnableRespawnShips { get => _settings.EnableRespawnShips; set => SetValue(ref _settings.EnableRespawnShips, value); }
[Torch.Views.Display(Description = "", Name = "Physics Iterations", GroupName = "Environment")]
public int PhysicsIterations { get => _settings.PhysicsIterations; set => SetValue(ref _settings.PhysicsIterations, value); }
[Torch.Views.Display(Description = "Defines interval of one rotation of the sun.", Name = "Sun Rotation Interval", GroupName = "Environment")]
public float SunRotationIntervalMinutes { get => _settings.SunRotationIntervalMinutes; set => SetValue(ref _settings.SunRotationIntervalMinutes, value); }
[Torch.Views.Display(Description = "Enables jetpack.", Name = "Enable Jetpack", GroupName = "Players")]
public bool EnableJetpack { get => _settings.EnableJetpack; set => SetValue(ref _settings.EnableJetpack, value); }
[Torch.Views.Display(Description = "Enables spawning with tools in the inventory.", Name = "Spawn with Tools", GroupName = "Players")]
public bool SpawnWithTools { get => _settings.SpawnWithTools; set => SetValue(ref _settings.SpawnWithTools, value); }
[Torch.Views.Display(Description = "Enables voxel destructions.", Name = "Enable Voxel Destruction", GroupName = "Environment")]
public bool EnableVoxelDestruction { get => _settings.EnableVoxelDestruction; set => SetValue(ref _settings.EnableVoxelDestruction, value); }
[Torch.Views.Display(Description = "Enables spawning of drones in the world.", Name = "Enable Drones", GroupName = "NPCs")]
public bool EnableDrones { get => _settings.EnableDrones; set => SetValue(ref _settings.EnableDrones, value); }
[Torch.Views.Display(Description = "Enables spawning of wolves in the world.", Name = "Enable Wolves", GroupName = "NPCs")]
public bool EnableWolfs { get => _settings.EnableWolfs; set => SetValue(ref _settings.EnableWolfs, value); }
[Torch.Views.Display(Description = "Enables spawning of spiders in the world.", Name = "Enable Spiders", GroupName = "NPCs")]
public bool EnableSpiders { get => _settings.EnableSpiders; set => SetValue(ref _settings.EnableSpiders, value); }
[Torch.Views.Display(Name = "Block Type World Limits", GroupName = "Block Limits")]
public Dictionary<string, short> BlockTypeLimits { get => _settings.BlockTypeLimits.Dictionary; set => SetValue(x => _settings.BlockTypeLimits.Dictionary = x, value); }
[Torch.Views.Display(Description = "Enables scripter role for administration.", Name = "Enable Scripter Role", GroupName = "Others")]
public bool EnableScripterRole { get => _settings.EnableScripterRole; set => SetValue(ref _settings.EnableScripterRole, value); }
[Torch.Views.Display(Description = "Defines minimum respawn time for drop containers.", Name = "Min Drop Container Respawn Time", GroupName = "Others")]
public int MinDropContainerRespawnTime { get => _settings.MinDropContainerRespawnTime; set => SetValue(ref _settings.MinDropContainerRespawnTime, value); }
[Torch.Views.Display(Description = "Defines maximum respawn time for drop containers.", Name = "Max Drop Container Respawn Time", GroupName = "Others")]
public int MaxDropContainerRespawnTime { get => _settings.MaxDropContainerRespawnTime; set => SetValue(ref _settings.MaxDropContainerRespawnTime, value); }
[Torch.Views.Display(Description = "Enables friendly fire for turrets.", Name = "Enable Turrets Friendly Fire", GroupName = "Environment")]
public bool EnableTurretsFriendlyFire { get => _settings.EnableTurretsFriendlyFire; set => SetValue(ref _settings.EnableTurretsFriendlyFire, value); }
[Torch.Views.Display(Description = "Enables sub-grid damage.", Name = "Enable Sub-Grid Damage", GroupName = "Environment")]
public bool EnableSubgridDamage { get => _settings.EnableSubgridDamage; set => SetValue(ref _settings.EnableSubgridDamage, value); }
[Torch.Views.Display(Description = "Defines synchronization distance in multiplayer. High distance can slow down server drastically. Use with caution.", Name = "Sync Distance", GroupName = "Environment")]
public int SyncDistance { get => _settings.SyncDistance; set => SetValue(ref _settings.SyncDistance, value); }
[Torch.Views.Display(Description = "Defines render distance for clients in multiplayer. High distance can slow down client FPS. Values larger than SyncDistance may not work as expected.", Name = "View Distance", GroupName = "Environment")]
public int ViewDistance { get => _settings.ViewDistance; set => SetValue(ref _settings.ViewDistance, value);}
[Torch.Views.Display(Description = "Enables experimental mode.", Name = "Experimental Mode", GroupName = "Others")]
public bool ExperimentalMode { get => _settings.ExperimentalMode; set => SetValue(ref _settings.ExperimentalMode, value); }
[Torch.Views.Display(Description = "Enables adaptive simulation quality system. This system is useful if you have a lot of voxel deformations in the world and low simulation speed.", Name = "Adaptive Simulation Quality", GroupName = "Others")]
public bool AdaptiveSimulationQuality { get => _settings.AdaptiveSimulationQuality; set => SetValue(ref _settings.AdaptiveSimulationQuality, value); }
[Torch.Views.Display(Description = "Enables voxel hand.", Name = "Enable voxel hand", GroupName = "Others")]
public bool EnableVoxelHand { get => _settings.EnableVoxelHand; set => SetValue(ref _settings.EnableVoxelHand, value); }
[Torch.Views.Display(Description = "Enables trash removal system.", Name = "Trash Removal Enabled", GroupName = "Trash Removal")]
public bool TrashRemovalEnabled { get => _settings.TrashRemovalEnabled; set => SetValue(ref _settings.TrashRemovalEnabled, value); }
[Torch.Views.Display(Description = "Defines flags for trash removal system.", Name = "Trash Removal Flags", GroupName = "Trash Removal")]
public MyTrashRemovalFlags TrashFlagsValue { get => (MyTrashRemovalFlags)_settings.TrashFlagsValue; set => SetValue(ref _settings.TrashFlagsValue, (int)value); }
[Torch.Views.Display(Description = "Defines block count threshold for trash removal system.", Name = "Block Count Threshold", GroupName = "Trash Removal")]
public int BlockCountThreshold { get => _settings.BlockCountThreshold; set => SetValue(ref _settings.BlockCountThreshold, value); }
[Torch.Views.Display(Description = "Defines player distance threshold for trash removal system.", Name = "Player Distance Threshold [m]", GroupName = "Trash Removal")]
public float PlayerDistanceThreshold { get => _settings.PlayerDistanceThreshold; set => SetValue(ref _settings.PlayerDistanceThreshold, value); }
[Torch.Views.Display(Description = "By setting this, server will keep number of grids around this value. \n !WARNING! It ignores Powered and Fixed flags, Block Count and lowers Distance from player.\n Set to 0 to disable.", Name = "Optimal Grid Count", GroupName = "Trash Removal")]
public int OptimalGridCount { get => _settings.OptimalGridCount; set => SetValue(ref _settings.OptimalGridCount, value); }
[Torch.Views.Display(Description = "Defines player inactivity threshold for trash removal system. \n !WARNING! This will remove all grids of the player.\n Set to 0 to disable.", Name = "Player Inactivity Threshold [hours]", GroupName = "Trash Removal")]
public float PlayerInactivityThreshold { get => _settings.PlayerInactivityThreshold; set => SetValue(ref _settings.PlayerInactivityThreshold, value); }
[Torch.Views.Display(Description = "Defines character removal threshold for trash removal system. If player disconnects it will remove his character after this time.\n Set to 0 to disable.", Name = "Character Removal Threshold [mins]", GroupName = "Trash Removal")]
public int PlayerCharacterRemovalThreshold { get => _settings.PlayerCharacterRemovalThreshold; set => SetValue(ref _settings.PlayerCharacterRemovalThreshold, value); }
[Torch.Views.Display(Description = "Sets optimal distance in meters when spawning new players near others.", Name = "Optimal Spawn Distance", GroupName = "Players")]
public float OptimalSpawnDistance { get => _settings.OptimalSpawnDistance; set => SetValue(ref _settings.OptimalSpawnDistance, value); }
[Torch.Views.Display(Description = "Enables automatic respawn at nearest available respawn point.", Name = "Enable Auto Respawn", GroupName = "Players")]
public bool EnableAutoRespawn { get => _settings.EnableAutorespawn; set => SetValue(ref _settings.EnableAutorespawn, value); }
[Torch.Views.Display(Description = "The number of NPC factions generated on the start of the world.", Name = "NPC Factions Count", GroupName = "NPCs")]
public int TradeFactionsCount { get => _settings.TradeFactionsCount; set => SetValue(ref _settings.TradeFactionsCount, value); }
[Torch.Views.Display(Description = "The inner radius [m] (center is in 0,0,0), where stations can spawn. Does not affect planet-bound stations (surface Outposts and Orbital stations).", Name = "Stations Inner Radius", GroupName = "NPCs")]
public double StationsDistanceInnerRadius { get => _settings.StationsDistanceInnerRadius; set => SetValue(ref _settings.StationsDistanceInnerRadius, value); }
[Torch.Views.Display(Description = "The outer radius [m] (center is in 0,0,0), where stations can spawn. Does not affect planet-bound stations (surface Outposts and Orbital stations).", Name = "Stations Outer Radius Start", GroupName = "NPCs")]
public double StationsDistanceOuterRadiusStart { get => _settings.StationsDistanceOuterRadiusStart; set => SetValue(ref _settings.StationsDistanceOuterRadiusStart, value); }
[Torch.Views.Display(Description = "The outer radius [m] (center is in 0,0,0), where stations can spawn. Does not affect planet-bound stations (surface Outposts and Orbital stations).", Name = "Stations Outer Radius End", GroupName = "NPCs")]
public double StationsDistanceOuterRadiusEnd { get => _settings.StationsDistanceOuterRadiusEnd; set => SetValue(ref _settings.StationsDistanceOuterRadiusEnd, value); }
[Torch.Views.Display(Description = "Time period between two economy updates in seconds.", Name = "Economy tick time", GroupName = "NPCs")]
public int EconomyTickInSeconds { get => _settings.EconomyTickInSeconds; set => SetValue(ref _settings.EconomyTickInSeconds, value); }
[Torch.Views.Display(Description = "If enabled bounty contracts will be available on stations.", Name = "Enable Bounty Contracts", GroupName = "Players")]
public bool EnableBountyContracts { get => _settings.EnableBountyContracts; set => SetValue(ref _settings.EnableBountyContracts, value); }
[Torch.Views.Display(Description = "Resource deposits count coefficient for generated world content (voxel generator version > 2).", Name = "Deposits Count Coefficient", GroupName = "Environment")]
public float DepositsCountCoefficient { get => _settings.DepositsCountCoefficient; set => SetValue(ref _settings.DepositsCountCoefficient, value); }
[Torch.Views.Display(Description = "Resource deposit size denominator for generated world content (voxel generator version > 2).", Name = "Deposit Size Denominator", GroupName = "Environment")]
public float DepositSideDenominator { get => _settings.DepositSizeDenominator; set => SetValue(ref _settings.DepositSizeDenominator, value); }
[Torch.Views.Display(Description = "Enables economy features.", Name = "Enable Economy", GroupName = "NPCs")]
public bool EnableEconomy { get => _settings.EnableEconomy; set => SetValue(ref _settings.EnableEconomy, value); }
[Torch.Views.Display(Description = "Enables system for voxel reverting.", Name = "Enable Voxel Reverting", GroupName = "Trash Removal")]
public bool VoxelTrashRemovalEnabled { get => _settings.VoxelTrashRemovalEnabled; set => SetValue(ref _settings.VoxelTrashRemovalEnabled, value); }
[Torch.Views.Display(Description = "Allows super gridding exploit to be used.", Name = "Enable Supergridding", GroupName = "Others")]
public bool EnableSupergridding { get => _settings.EnableSupergridding; set => SetValue(ref _settings.EnableSupergridding, value); }
[Torch.Views.Display(Description = "Enables Selective Physics", Name = "Enable Selective Physics", GroupName = "Others")]
public bool EnableSelectivePhysics { get => _settings.EnableSelectivePhysicsUpdates; set => SetValue(ref _settings.EnableSelectivePhysicsUpdates, value); }
[Torch.Views.Display(Description = "Allows steam's family sharing", Name = "Enable Family Sharing", GroupName = "Players")]
public bool EnableFamilySharing { get => _settings.FamilySharing; set => SetValue(ref _settings.FamilySharing, value); }
[Torch.Views.Display(Description = "Enables PCU trading", Name = "Enable PCU Trading", GroupName = "Block Limits")]
public bool EnablePCUTrading { get => _settings.EnablePcuTrading; set => SetValue(ref _settings.EnablePcuTrading, value); }
[Torch.Views.Display(Description = "Enables system for weather", Name = "Enable Weather System", GroupName = "Others")]
public bool EnableWeatherSystem { get => _settings.WeatherSystem; set => SetValue(ref _settings.WeatherSystem, value); }
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
{
_settings = settings;
}
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
{
return viewModel._settings;
}
}
}

View File

@@ -6,12 +6,12 @@ namespace Torch.Server.ViewModels
public class WorldConfigurationViewModel : ViewModel
{
private readonly MyObjectBuilder_WorldConfiguration _worldConfiguration;
private SessionSettingsViewModel _sessionSettings;
private DynamicViewModel<MyObjectBuilder_SessionSettings> _sessionSettings;
public WorldConfigurationViewModel(MyObjectBuilder_WorldConfiguration worldConfiguration)
{
_worldConfiguration = worldConfiguration;
_sessionSettings = new SessionSettingsViewModel(worldConfiguration.Settings);
_sessionSettings = new DynamicViewModel<MyObjectBuilder_SessionSettings>(worldConfiguration.Settings);
}
public static implicit operator MyObjectBuilder_WorldConfiguration(WorldConfigurationViewModel model)
@@ -21,7 +21,7 @@ namespace Torch.Server.ViewModels
public List<MyObjectBuilder_Checkpoint.ModItem> Mods { get => _worldConfiguration.Mods; set => SetValue(ref _worldConfiguration.Mods, value); }
public SessionSettingsViewModel Settings
public DynamicViewModel<MyObjectBuilder_SessionSettings> Settings
{
get => _sessionSettings;
set

View File

@@ -130,7 +130,7 @@
</Grid>
<TabControl Grid.Column="1" Margin="3">
<TabItem Header="World">
<views:PropertyGrid DataContext="{Binding SessionSettings}" />
<views:PropertyGrid DataContext="{Binding SessionSettings.Wrapper}" />
</TabItem>
<TabItem Header="Torch">
<views:PropertyGrid x:Name="TorchSettings" />

View File

@@ -101,7 +101,7 @@ namespace Torch.Server
{
var selected = (PremadeCheckpointItem)PremadeCheckpoints.SelectedItem;
_currentItem = selected;
SettingsView.DataContext = new SessionSettingsViewModel(_currentItem.Checkpoint.Settings);
SettingsView.DataContext = new DynamicViewModel<MyObjectBuilder_SessionSettings>(_currentItem.Checkpoint.Settings).Wrapper;
}
}

View File

@@ -472,8 +472,8 @@
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Transitive",
"resolved": "1.204.18",
"contentHash": "GT7/9CBMx4jjor41zLOOl87YYM/JdJD8xp9ccXyuhP2oUaz25H3ZmCQuGeAuZNENKru1a/7hZrId4PwlMDGoew==",
"resolved": "1.205.23",
"contentHash": "J7mF5hY39PzzCZps6vhIRzKiq8vD6Af9TgumTJR068vjEi+BzyeEFhqX+cl2Dd1ngOmsBtGWc5m+vxgTfs5YuA==",
"dependencies": {
"SharpDX": "4.2.0-keen-cringe",
"protobuf-net": "1.0.0"
@@ -581,7 +581,7 @@
"NuGet.Commands": "[6.11.0, )",
"NuGet.DependencyResolver.Core": "[6.11.0, )",
"SemanticVersioning": "[2.0.2, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.204.18, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )",
"System.Linq.Async": "[6.0.1, )"
}
}

View File

@@ -313,8 +313,8 @@
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Transitive",
"resolved": "1.204.18",
"contentHash": "GT7/9CBMx4jjor41zLOOl87YYM/JdJD8xp9ccXyuhP2oUaz25H3ZmCQuGeAuZNENKru1a/7hZrId4PwlMDGoew==",
"resolved": "1.205.23",
"contentHash": "J7mF5hY39PzzCZps6vhIRzKiq8vD6Af9TgumTJR068vjEi+BzyeEFhqX+cl2Dd1ngOmsBtGWc5m+vxgTfs5YuA==",
"dependencies": {
"SharpDX": "4.2.0-keen-cringe",
"protobuf-net": "1.0.0"
@@ -454,7 +454,7 @@
"NuGet.Commands": "[6.11.0, )",
"NuGet.DependencyResolver.Core": "[6.11.0, )",
"SemanticVersioning": "[2.0.2, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.204.18, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )",
"System.Linq.Async": "[6.0.1, )"
}
}

View File

@@ -10,6 +10,7 @@ using Torch.API;
using Torch.API.Managers;
using Torch.Utils;
using VRage.Game;
using VRage.GameServices;
using Color = VRageMath.Color;
namespace Torch.Managers.ChatManager
@@ -76,7 +77,7 @@ namespace Torch.Managers.ChatManager
_chatMessageRecievedReplacer = _chatMessageReceivedFactory.Invoke();
_scriptedChatMessageRecievedReplacer = _scriptedChatMessageReceivedFactory.Invoke();
_chatMessageRecievedReplacer.Replace(
new Action<ulong, string, ChatChannel, long, string, ulong?>(Multiplayer_ChatMessageReceived),
new Action<ulong, string, ChatChannel, long, ChatMessageCustomData?>(Multiplayer_ChatMessageReceived),
MyMultiplayer.Static);
_scriptedChatMessageRecievedReplacer.Replace(
new Action<string, string, string, Color>(Multiplayer_ScriptedChatMessageReceived),
@@ -133,14 +134,14 @@ namespace Torch.Managers.ChatManager
}
private void Multiplayer_ChatMessageReceived(ulong steamUserId, string messageText, ChatChannel channel, long targetId, string customAuthorName, ulong? customSenderId = null)
private void Multiplayer_ChatMessageReceived(ulong steamUserId, string messageText, ChatChannel channel, long targetId, ChatMessageCustomData? customData )
{
#pragma warning disable CS0612
var torchMsg = new TorchChatMessage(steamUserId, messageText, channel, targetId,
#pragma warning restore CS0612
(steamUserId == MyGameService.UserId) ? MyFontEnum.DarkBlue : MyFontEnum.Blue);
if (!RaiseMessageRecieved(torchMsg) && HasHud)
_hudChatMessageReceived.Invoke(MyHud.Chat, steamUserId, messageText, channel, targetId, customAuthorName, customSenderId);
_hudChatMessageReceived.Invoke(MyHud.Chat, steamUserId, messageText, channel, targetId, customData);
}
private void Multiplayer_ScriptedChatMessageReceived(string message, string author, string font, Color color)
@@ -165,7 +166,7 @@ namespace Torch.Managers.ChatManager
protected static bool HasHud => !Sandbox.Engine.Platform.Game.IsDedicated;
[ReflectedMethod(Name = _hudChatMessageReceivedName)]
private static Action<MyHudChat, ulong, string, ChatChannel, long, string, ulong?> _hudChatMessageReceived = null!;
private static Action<MyHudChat, ulong, string, ChatChannel, long, ChatMessageCustomData?> _hudChatMessageReceived = null!;
[ReflectedMethod(Name = _hudChatScriptedMessageReceivedName)]
private static Action<MyHudChat, string, string, string, Color> _hudChatScriptedMessageReceived = null!;

View File

@@ -65,7 +65,8 @@ internal static class AssemblyRewriter
{
using var assembly = AssemblyDefinition.ReadAssembly(inputStream, new()
{
AssemblyResolver = resolver
AssemblyResolver = resolver,
ReadSymbols = true
});
foreach (var fieldDefinition in FindAllToRewrite(assembly.MainModule))
{

View File

@@ -293,15 +293,20 @@ namespace Torch
{
}
private bool _disposed = false;
/// <inheritdoc />
public virtual void Destroy()
{
if (_disposed) return;
Game.SignalDestroy();
if (!Game.WaitFor(VRageGame.GameState.Destroyed))
Log.Warn("Failed to wait for the game to be destroyed");
Game = null;
Managers.Detach();
_disposed = true;
}
#endregion
@@ -325,7 +330,7 @@ namespace Torch
if (MyAsyncSaving.InProgress || _inProgressSaves > 0)
{
Log.Error("Failed to save game, game is already saving");
return null;
return Task.FromResult(GameSaveResult.ConcurrentSaveInProgress);
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using JetBrains.Annotations;
namespace Torch.Views
{
@@ -19,21 +20,18 @@ namespace Torch.Views
private PropertyInfo _property;
private object _obj;
public void EditEnum(PropertyInfo prop, object obj)
public void EditEnum(PropertyInfo prop, object obj, [CanBeNull] Type propertyType = null)
{
if (!prop.PropertyType.IsEnum || prop.PropertyType.GetCustomAttribute<FlagsAttribute>() == null)
throw new ArgumentException("Type is not a flags enum");
_property = prop;
_obj = obj;
_flags = new List<Flag>();
var initial = (int)Convert.ChangeType(prop.GetValue(obj), typeof(int));
foreach (var value in Enum.GetValues(prop.PropertyType))
foreach (var value in Enum.GetValues(propertyType ??= prop.PropertyType))
{
var val = (int)Convert.ChangeType(value, typeof(int));
_flags.Add(new Flag
{
Name = Enum.GetName(prop.PropertyType, value),
Name = Enum.GetName(propertyType, value),
Value = val,
IsChecked = (initial & val) > 0
});

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows;
@@ -9,6 +10,7 @@ using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using VRage.Serialization;
using VRage.Utils;
namespace Torch.Views
{
@@ -65,6 +67,11 @@ namespace Torch.Views
foreach (var property in properties)
{
var browsableAttribute = property.GetCustomAttribute<BrowsableAttribute>();
if (browsableAttribute is { Browsable: false })
continue;
//Attempt to load our custom DisplayAttribute
var a = property.GetCustomAttribute<DisplayAttribute>();
//If not found and IgnoreDisplay is not set, fall back to system DisplayAttribute
@@ -73,7 +80,7 @@ namespace Torch.Views
if (!IgnoreDisplay && a == null || a?.Visible == false)
continue;
descriptors[property] = a;
string category = a?.GroupName ?? "Misc";
string category = a?.GroupName ?? property.GetCustomAttribute<CategoryAttribute>()?.Category ?? "Misc";
if (!categories.TryGetValue(category, out List<PropertyInfo> l))
{
@@ -154,9 +161,9 @@ namespace Torch.Views
valueControl = new CheckBox();
valueControl.SetBinding(CheckBox.IsCheckedProperty, property.Name);
}
else if (propertyType.IsEnum)
else if (propertyType.IsEnum || property.HasAttribute<MyFlagEnumAttribute>())
{
var isFlags = propertyType.GetCustomAttribute<FlagsAttribute>() != null;
var isFlags = property.HasAttribute<MyFlagEnumAttribute>() || propertyType.HasAttribute<FlagsAttribute>();
if (isFlags)
{
@@ -299,11 +306,13 @@ namespace Torch.Views
var propName = btn.GetBindingExpression(DataContextProperty).ParentBinding.Path.Path;
var propInfo = DataContext.GetType().GetProperty(propName);
var propTypeOverride = propInfo?.GetCustomAttribute<MyFlagEnumAttribute>()?.EnumType;
new FlagsEditorDialog
{
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = Window.GetWindow(this)
}.EditEnum(propInfo, obj);
}.EditEnum(propInfo, obj, propTypeOverride);
}
private void EditDictionary(object dict)

View File

@@ -314,8 +314,8 @@
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Transitive",
"resolved": "1.204.18",
"contentHash": "GT7/9CBMx4jjor41zLOOl87YYM/JdJD8xp9ccXyuhP2oUaz25H3ZmCQuGeAuZNENKru1a/7hZrId4PwlMDGoew==",
"resolved": "1.205.23",
"contentHash": "J7mF5hY39PzzCZps6vhIRzKiq8vD6Af9TgumTJR068vjEi+BzyeEFhqX+cl2Dd1ngOmsBtGWc5m+vxgTfs5YuA==",
"dependencies": {
"SharpDX": "4.2.0-keen-cringe",
"protobuf-net": "1.0.0"
@@ -387,7 +387,7 @@
"NuGet.Commands": "[6.11.0, )",
"NuGet.DependencyResolver.Core": "[6.11.0, )",
"SemanticVersioning": "[2.0.2, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.204.18, )",
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )",
"System.Linq.Async": "[6.0.1, )"
}
}

7
global.json Normal file
View File

@@ -0,0 +1,7 @@
{
"sdk": {
"version": "8.0.0",
"rollForward": "latestFeature",
"allowPrerelease": false
}
}