Compare commits

...

17 Commits

Author SHA1 Message Date
Pieter-Jan Briers
1f95fe6782 Fix XamlIL problems when publishing 2020-12-25 18:34:19 +01:00
DrSmugleaf
07c1f9e1af Replace MaybeNullWhen(false) with NotNullWhen(true) in AppearanceComponent (#1461) 2020-12-25 15:17:54 +01:00
Vera Aguilera Puerto
826dce6659 Fix custom MIDI soundfont loading when mounting content from zip
Fixes #1466
2020-12-24 03:54:06 +01:00
Vera Aguilera Puerto
cdf714f3ba Fix stereo ogg audio not playing correctly.
Only half of the samples were being read.
2020-12-23 16:21:42 +01:00
Pieter-Jan Briers
671ca7959c Throw debug info if the RichTextEntry assert fails. 2020-12-23 15:12:47 +01:00
Pieter-Jan Briers
b7a1345d3a XAML compilation improvements.
Prevent double XAML-ifying causing corrupt dlls.

Use MSBuild dependencies to reduce unecessary xamlil builds.
2020-12-23 12:01:35 +01:00
Pieter-Jan Briers
835b6ebdba Probably fix build forgetting that nuget exists. 2020-12-21 12:14:28 +01:00
Pieter-Jan Briers
0ecabd6553 Fix XamlIL locking build. 2020-12-21 12:13:45 +01:00
Pieter-Jan Briers
feaa69f825 Fix build of injector. 2020-12-21 04:05:34 +01:00
Pieter-Jan Briers
857904a3d9 Update dependencies of name generator. 2020-12-21 04:05:25 +01:00
Pieter-Jan Briers
0b37418477 Fix injectors UsingTask. 2020-12-21 03:31:11 +01:00
Pieter-Jan Briers
f234ecb2c3 Make Robust.Client.Injectors NS2.0
So that it works out of the box with Framework MSBuild.
2020-12-21 03:16:13 +01:00
Pieter-Jan Briers
b449959865 Clean up bad project reference in Robust.Server 2020-12-21 03:15:45 +01:00
Pieter-Jan Briers
8f870403d2 Managed implementation of HttpListener. (#1460) 2020-12-21 02:51:04 +01:00
Paul Ritter
d94f702601 Xaml UI (#1446)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2020-12-20 23:52:36 +01:00
DrSmugleaf
e78ab8f922 Add YamlObjectSerializer.NodeToType with generic argument (#1456) 2020-12-20 20:46:56 +01:00
20kdc
6972000293 Make the "softness" of soft shadows adjustible per-light. (#1454)
Note: Thanks to the nature of YAML properties in RobustToolbox, this commit is only an API blocker if the Softness property is directly manipulated from code, which is unlikely.
2020-12-19 21:44:47 +01:00
45 changed files with 2057 additions and 183 deletions

6
.gitmodules vendored
View File

@@ -4,6 +4,12 @@
[submodule "Lidgren.Network"]
path = Lidgren.Network/Lidgren.Network
url = https://github.com/space-wizards/lidgren-network-gen3.git
[submodule "XamlX"]
path = XamlX
url = https://github.com/space-wizards/XamlX
[submodule "Robust.LoaderApi"]
path = Robust.LoaderApi
url = https://github.com/space-wizards/Robust.LoaderApi.git
[submodule "ManagedHttpListener"]
path = ManagedHttpListener
url = https://github.com/space-wizards/ManagedHttpListener.git

60
MSBuild/XamlIL.targets Normal file
View File

@@ -0,0 +1,60 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<PropertyGroup>
<RobustUseExternalMSBuild>true</RobustUseExternalMSBuild>
<_RobustUseExternalMSBuild>$(RobustUseExternalMSBuild)</_RobustUseExternalMSBuild>
<_RobustUseExternalMSBuild Condition="'$(_RobustForceInternalMSBuild)' == 'true'">false</_RobustUseExternalMSBuild>
</PropertyGroup>
<ItemGroup>
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<EmbeddedResource Include="**\*.xaml"/>
<AdditionalFiles Include="**\*.xaml"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false"/>
</ItemGroup>
<UsingTask
Condition="'$(_RobustUseExternalMSBuild)' != 'true' And $(DesignTimeBuild) != true"
TaskName="CompileRobustXamlTask"
AssemblyFile="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(Configuration)\netstandard2.0\Robust.Client.Injectors.dll"/>
<Target
Name="CompileRobustXaml"
Condition="Exists('@(IntermediateAssembly)')"
AfterTargets="AfterCompile"
Inputs="@(IntermediateAssembly);@(ReferencePathWithRefAssemblies)"
Outputs="$(IntermediateOutputPath)XAML/doot">
<PropertyGroup>
<RobustXamlReferencesTemporaryFilePath Condition="'$(RobustXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)XAML/references</RobustXamlReferencesTemporaryFilePath>
<RobustXamlOriginalCopyFilePath Condition="'$(RobustXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)XAML/original.dll</RobustXamlOriginalCopyFilePath>
</PropertyGroup>
<WriteLinesToFile
Condition="'$(_RobustForceInternalMSBuild)' != 'true'"
File="$(RobustXamlReferencesTemporaryFilePath)"
Lines="@(ReferencePathWithRefAssemblies)"
Overwrite="true"/>
<!--
UpdateBuildIndicator is done so that we can use MSBuild Inputs and Outputs on the target
to avoid unecessary execution of this target
Saves compile time if e.g. ONLY Robust.Client changes (Content.Client doesn't have to re-xaml).
-->
<CompileRobustXamlTask
Condition="'$(_RobustUseExternalMSBuild)' != 'true'"
AssemblyFile="@(IntermediateAssembly)"
ReferencesFilePath="$(RobustXamlReferencesTemporaryFilePath)"
OriginalCopyPath="$(RobustXamlOriginalCopyFilePath)"
ProjectDirectory="$(MSBuildProjectDirectory)"
AssemblyOriginatorKeyFile="$(AssemblyOriginatorKeyFile)"
SignAssembly="$(SignAssembly)"
DelaySign="$(DelaySign)"
UpdateBuildIndicator="$(IntermediateOutputPath)XAML/doot"/>
<Exec
Condition="'$(_RobustUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileRobustXaml /p:_RobustForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
</Target>
</Project>

1
ManagedHttpListener Submodule

Submodule ManagedHttpListener added at f2aa590fec

View File

@@ -13,7 +13,7 @@ highp float createOcclusion(highp vec2 diff)
// Change soft shadow size based on distance from primary occluder.
highp float distRatio = (ourDist - occlDist.x) / occlDist.x / 2.0;
perpendicular *= distRatio;
perpendicular *= distRatio * lightSoftness;
// Totally not hacky PCF on top of VSM.
highp float occlusion = smoothstep(0.1, 1.0, ChebyshevUpperBound(occlDist, ourDist));

View File

@@ -13,6 +13,7 @@ uniform highp vec4 lightColor;
uniform highp vec2 lightCenter;
uniform highp float lightRange;
uniform highp float lightPower;
uniform highp float lightSoftness;
uniform highp float lightIndex;
uniform sampler2D shadowMap;

View File

@@ -0,0 +1,101 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
namespace Robust.Build.Tasks
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
/// </summary>
public class CompileRobustXamlTask : ITask
{
public bool Execute()
{
//Debugger.Launch();
OutputPath = OutputPath ?? AssemblyFile;
var outputPdb = GetPdbPath(OutputPath);
var input = AssemblyFile;
var inputPdb = GetPdbPath(input);
// Make a copy and delete the original file to prevent MSBuild from thinking that everything is OK
if (OriginalCopyPath != null)
{
File.Copy(AssemblyFile, OriginalCopyPath, true);
input = OriginalCopyPath;
File.Delete(AssemblyFile);
if (File.Exists(inputPdb))
{
var copyPdb = GetPdbPath(OriginalCopyPath);
File.Copy(inputPdb, copyPdb, true);
File.Delete(inputPdb);
inputPdb = copyPdb;
}
}
var msg = $"CompileRobustXamlTask -> AssemblyFile:{AssemblyFile}, ProjectDirectory:{ProjectDirectory}, OutputPath:{OutputPath}";
BuildEngine.LogMessage(msg, MessageImportance.High);
var res = XamlCompiler.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath,
(SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null);
if (!res.success)
return false;
if (!res.writtentofile)
{
File.Copy(input, OutputPath, true);
if(File.Exists(inputPdb))
File.Copy(inputPdb, outputPdb, true);
}
if (!string.IsNullOrEmpty(UpdateBuildIndicator))
{
if (!File.Exists(UpdateBuildIndicator))
{
File.Create(UpdateBuildIndicator).Dispose();
}
else
{
File.SetLastWriteTime(UpdateBuildIndicator, DateTime.Now);
}
}
return true;
}
[Required]
public string ReferencesFilePath { get; set; }
[Required]
public string ProjectDirectory { get; set; }
[Required]
public string AssemblyFile { get; set; }
[Required]
public string OriginalCopyPath { get; set; }
public string OutputPath { get; set; }
public string UpdateBuildIndicator { get; set; }
public string AssemblyOriginatorKeyFile { get; set; }
public bool SignAssembly { get; set; }
public bool DelaySign { get; set; }
// shamelessly copied from avalonia
string GetPdbPath(string p)
{
var d = Path.GetDirectoryName(p);
var f = Path.GetFileNameWithoutExtension(p);
var rv = f + ".pdb";
if (d != null)
rv = Path.Combine(d, rv);
return rv;
}
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using Microsoft.Build.Framework;
namespace Robust.Build.Tasks
{
/// <summary>
/// Taken from https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/Extensions.cs
/// </summary>
public static class Extensions
{
//shamefully copied from avalonia
public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp)
{
engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp));
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections;
using System.IO;
using Microsoft.Build.Framework;
namespace Robust.Build.Tasks
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/Program.cs
/// </summary>
class Program
{
static int Main(string[] args)
{
if (args.Length != 3)
{
Console.Error.WriteLine("expected: input references output");
return 1;
}
return new CompileRobustXamlTask
{
AssemblyFile = args[0],
ReferencesFilePath = args[1],
OutputPath = args[2],
BuildEngine = new ConsoleBuildEngine(),
ProjectDirectory = Directory.GetCurrentDirectory()
}.Execute() ? 0 : 2;
}
}
class ConsoleBuildEngine : IBuildEngine
{
public void LogErrorEvent(BuildErrorEventArgs e)
{
Console.WriteLine($"ERROR: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogWarningEvent(BuildWarningEventArgs e)
{
Console.WriteLine($"WARNING: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogMessageEvent(BuildMessageEventArgs e)
{
Console.WriteLine($"MESSAGE: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogCustomEvent(CustomBuildEventArgs e)
{
Console.WriteLine($"CUSTOM: {e.Message}");
}
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties,
IDictionary targetOutputs) => throw new NotSupportedException();
public bool ContinueOnError { get; }
public int LineNumberOfTaskNode { get; }
public int ColumnNumberOfTaskNode { get; }
public string ProjectFileOfTaskNode { get; }
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="16.8.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\XamlX\src\XamlX.IL.Cecil\XamlX.IL.Cecil.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,201 @@
using System.Linq;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
/// <summary>
/// Emitters & Transformers based on:
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
/// </summary>
public class RobustXamlILCompiler : XamlILCompiler
{
public RobustXamlILCompiler(TransformerConfiguration configuration, XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> emitMappings, bool fillWithDefaults) : base(configuration, emitMappings, fillWithDefaults)
{
Transformers.Add(new AddNameScopeRegistration());
Transformers.Add(new RobustMarkRootObjectScopeNode());
Emitters.Add(new AddNameScopeRegistration.Emitter());
Emitters.Add(new RobustMarkRootObjectScopeNode.Emitter());
}
class AddNameScopeRegistration : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is XamlPropertyAssignmentNode pa)
{
if (pa.Property.Name == "Name"
&& pa.Property.DeclaringType.FullName == "Robust.Client.UserInterface.Control")
{
if (context.ParentNodes().FirstOrDefault() is XamlManipulationGroupNode mg
&& mg.Children.OfType<RobustNameScopeRegistrationXamlIlNode>().Any())
return node;
IXamlAstValueNode value = null;
for (var c = 0; c < pa.Values.Count; c++)
if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String))
{
value = pa.Values[c];
if (!(value is XamlAstTextNode))
{
var local = new XamlAstCompilerLocalNode(value);
// Wrap original in local initialization
pa.Values[c] = new XamlAstLocalInitializationNodeEmitter(value, value, local);
// Use local
value = local;
}
break;
}
if (value != null)
{
var objectType = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().FirstOrDefault()?.Type.GetClrType();
return new XamlManipulationGroupNode(pa)
{
Children =
{
pa,
new RobustNameScopeRegistrationXamlIlNode(value, objectType)
}
};
}
}
/*else if (pa.Property.CustomAttributes.Select(attr => attr.Type).Intersect(context.Configuration.TypeMappings.DeferredContentPropertyAttributes).Any())
{
pa.Values[pa.Values.Count - 1] =
new NestedScopeMetadataNode(pa.Values[pa.Values.Count - 1]);
}*/
}
return node;
}
class RobustNameScopeRegistrationXamlIlNode : XamlAstNode, IXamlAstManipulationNode
{
public IXamlAstValueNode Name { get; set; }
public IXamlType TargetType { get; }
public RobustNameScopeRegistrationXamlIlNode(IXamlAstValueNode name, IXamlType targetType) : base(name)
{
TargetType = targetType;
Name = name;
}
public override void VisitChildren(IXamlAstVisitor visitor)
=> Name = (IXamlAstValueNode)Name.Visit(visitor);
}
internal class Emitter : IXamlAstLocalsNodeEmitter<IXamlILEmitter, XamlILNodeEmitResult>
{
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
if (node is RobustNameScopeRegistrationXamlIlNode registration)
{
var scopeField = context.RuntimeContext.ContextType.Fields.First(f =>
f.Name == XamlCompiler.ContextNameScopeFieldName);
var namescopeRegisterFunction = context.Configuration.TypeSystem
.FindType("Robust.Client.UserInterface.XAML.NameScope").Methods
.First(m => m.Name == "Register");
using (var targetLoc = context.GetLocalOfType(context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control")))
{
codeGen
// var target = {pop}
.Stloc(targetLoc.Local)
// _context.NameScope.Register(Name, target)
.Ldloc(context.ContextLocal)
.Ldfld(scopeField);
context.Emit(registration.Name, codeGen, registration.Name.Type.GetClrType());
codeGen
.Ldloc(targetLoc.Local)
.EmitCall(namescopeRegisterFunction, true);
}
return XamlILNodeEmitResult.Void(1);
}
return default;
}
}
}
class RobustMarkRootObjectScopeNode : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (!context.ParentNodes().Any()
&& node is XamlValueWithManipulationNode mnode)
{
mnode.Manipulation = new XamlManipulationGroupNode(mnode,
new[]
{
mnode.Manipulation,
new HandleRootObjectScopeNode(mnode)
});
}
return node;
}
class HandleRootObjectScopeNode : XamlAstNode, IXamlAstManipulationNode
{
public HandleRootObjectScopeNode(IXamlLineInfo lineInfo) : base(lineInfo)
{
}
}
internal class Emitter : IXamlILAstNodeEmitter
{
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
if (!(node is HandleRootObjectScopeNode))
{
return null;
}
var controlType = context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control");
var next = codeGen.DefineLabel();
var dontAbsorb = codeGen.DefineLabel();
var end = codeGen.DefineLabel();
var contextScopeField = context.RuntimeContext.ContextType.Fields.First(f =>
f.Name == XamlCompiler.ContextNameScopeFieldName);
var controlNameScopeField = controlType.Fields.First(f => f.Name == "NameScope");
var nameScopeType = context.Configuration.TypeSystem
.FindType("Robust.Client.UserInterface.XAML.NameScope");
var nameScopeCompleteMethod = nameScopeType.Methods.First(m => m.Name == "Complete");
var nameScopeAbsorbMethod = nameScopeType.Methods.First(m => m.Name == "Absorb");
using (var local = codeGen.LocalsPool.GetLocal(controlType))
{
codeGen
.Isinst(controlType)
.Dup()
.Stloc(local.Local) //store control in local field
.Brfalse(next) //if control is null, move to next (this should never happen but whatev, avalonia does it)
.Ldloc(context.ContextLocal)
.Ldfld(contextScopeField)
.Ldloc(local.Local) //load control from local field
.Ldfld(controlNameScopeField) //load namescope field from control
.EmitCall(nameScopeAbsorbMethod, true)
.Ldloc(local.Local) //load control
.Ldloc(context.ContextLocal) //load contextObject
.Ldfld(contextScopeField) //load namescope field from context obj
.Stfld(controlNameScopeField) //store namescope field in control
.MarkLabel(next)
.Ldloc(context.ContextLocal)
.Ldfld(contextScopeField)
.EmitCall(nameScopeCompleteMethod, true); //set the namescope as complete
}
return XamlILNodeEmitResult.Void(1);
}
}
}
}
}

View File

@@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
/// <summary>
/// Helpers taken from:
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
/// </summary>
public partial class XamlCompiler
{
static bool CheckXamlName(IResource r) => r.Name.ToLowerInvariant().EndsWith(".xaml")
|| r.Name.ToLowerInvariant().EndsWith(".paml")
|| r.Name.ToLowerInvariant().EndsWith(".axaml");
private static bool MatchThisCall(Collection<Instruction> instructions, int idx)
{
var i = instructions[idx];
// A "normal" way of passing `this` to a static method:
// ldarg.0
// call void [Avalonia.Markup.Xaml]Avalonia.Markup.Xaml.AvaloniaXamlLoader::Load(object)
return i.OpCode == OpCodes.Ldarg_0 || (i.OpCode == OpCodes.Ldarg && i.Operand?.Equals(0) == true);
}
interface IResource : IFileSource
{
string Uri { get; }
string Name { get; }
void Remove();
}
interface IResourceGroup
{
string Name { get; }
IEnumerable<IResource> Resources { get; }
}
class EmbeddedResources : IResourceGroup
{
private readonly AssemblyDefinition _asm;
public string Name => "EmbeddedResource";
public IEnumerable<IResource> Resources => _asm.MainModule.Resources.OfType<EmbeddedResource>()
.Select(r => new WrappedResource(_asm, r)).ToList();
public EmbeddedResources(AssemblyDefinition asm)
{
_asm = asm;
}
class WrappedResource : IResource
{
private readonly AssemblyDefinition _asm;
private readonly EmbeddedResource _res;
public WrappedResource(AssemblyDefinition asm, EmbeddedResource res)
{
_asm = asm;
_res = res;
}
public string Uri => $"resm:{Name}?assembly={_asm.Name.Name}";
public string Name => _res.Name;
public string FilePath => Name;
public byte[] FileContents => _res.GetResourceData();
public void Remove() => _asm.MainModule.Resources.Remove(_res);
}
}
}
}

View File

@@ -0,0 +1,327 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Parsers;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Robust.Build.Tasks
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
/// Adjusted for our UI-Framework
/// </summary>
public partial class XamlCompiler
{
public static (bool success, bool writtentofile) Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory, string output, string strongNameKey)
{
var typeSystem = new CecilTypeSystem(references
.Where(r => !r.ToLowerInvariant().EndsWith("robust.build.tasks.dll"))
.Concat(new[] { input }), input);
var asm = typeSystem.TargetAssemblyDefinition;
if (asm.MainModule.GetType("CompiledRobustXaml", "XamlIlContext") != null)
{
// If this type exists, the assembly has already been processed by us.
// Do not run again, it would corrupt the file.
// This *shouldn't* be possible due to Inputs/Outputs dependencies in the build system,
// but better safe than sorry eh?
engine.LogWarningEvent(new BuildWarningEventArgs("XAMLIL", "", "", 0, 0, 0, 0, "Ran twice on same assembly file; ignoring.", "", ""));
return (true, false);
}
var compileRes = CompileCore(engine, typeSystem);
if (compileRes == null)
return (true, false);
if (compileRes == false)
return (false, false);
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
if (!string.IsNullOrWhiteSpace(strongNameKey))
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
asm.Write(output, writerParameters);
return (true, true);
}
static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem)
{
var asm = typeSystem.TargetAssemblyDefinition;
var embrsc = new EmbeddedResources(asm);
if (embrsc.Resources.Count(CheckXamlName) == 0)
// Nothing to do
return null;
var xamlLanguage = new XamlLanguageTypeMappings(typeSystem)
{
XmlnsAttributes =
{
typeSystem.GetType("Robust.Client.UserInterface.XAML.XmlnsDefinitionAttribute"),
},
ContentAttributes =
{
typeSystem.GetType("Robust.Client.UserInterface.XAML.ContentAttribute")
},
UsableDuringInitializationAttributes =
{
typeSystem.GetType("Robust.Client.UserInterface.XAML.UsableDuringInitializationAttribute")
},
DeferredContentPropertyAttributes =
{
typeSystem.GetType("Robust.Client.UserInterface.XAML.DeferredContentAttribute")
},
RootObjectProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestRootObjectProvider"),
UriContextProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestUriContext"),
ProvideValueTarget = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestProvideValueTarget"),
};
var emitConfig = new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>
{
ContextTypeBuilderCallback = (b,c) => EmitNameScopeField(xamlLanguage, typeSystem, b, c)
};
var transformerconfig = new TransformerConfiguration(
typeSystem,
typeSystem.TargetAssembly,
xamlLanguage,
XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage));
var contextDef = new TypeDefinition("CompiledRobustXaml", "XamlIlContext",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(contextDef);
var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
xamlLanguage, emitConfig);
var compiler =
new RobustXamlILCompiler(transformerconfig, emitConfig, true);
var loaderDispatcherDef = new TypeDefinition("CompiledRobustXaml", "!XamlLoader",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
var loaderDispatcherMethod = new MethodDefinition("TryLoad",
MethodAttributes.Static | MethodAttributes.Public,
asm.MainModule.TypeSystem.Object)
{
Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)}
};
loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
asm.MainModule.Types.Add(loaderDispatcherDef);
var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
m =>
m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 2 &&
m.ReturnType.FullName == "System.Boolean"
&& m.Parameters[0].ParameterType.FullName == "System.String"
&& m.Parameters[1].ParameterType.FullName == "System.String"));
bool CompileGroup(IResourceGroup group)
{
var typeDef = new TypeDefinition("CompiledRobustXaml", "!" + group.Name, TypeAttributes.Class,
asm.MainModule.TypeSystem.Object);
//typeDef.CustomAttributes.Add(new CustomAttribute(ed));
asm.MainModule.Types.Add(typeDef);
var builder = typeSystem.CreateTypeBuilder(typeDef);
foreach (var res in group.Resources.Where(CheckXamlName))
{
try
{
engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", MessageImportance.Low);
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
var parsed = XDocumentXamlParser.Parse(xaml);
var initialRoot = (XamlAstObjectNode) parsed.Root;
var classDirective = initialRoot.Children.OfType<XamlAstXmlDirective>()
.FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class");
string classname;
if (classDirective != null && classDirective.Values[0] is XamlAstTextNode tn)
{
classname = tn.Text;
}
else
{
classname = res.Name.Replace(".xaml","");
}
var classType = typeSystem.TargetAssembly.FindType(classname);
if (classType == null)
throw new Exception($"Unable to find type '{classname}'");
compiler.Transform(parsed);
var populateName = $"Populate:{res.Name}";
var buildName = $"Build:{res.Name}";
var classTypeDefinition = typeSystem.GetTypeReference(classType).Resolve();
var populateBuilder = typeSystem.CreateTypeBuilder(classTypeDefinition);
compiler.Compile(parsed, contextClass,
compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
classTypeDefinition == null),
compiler.DefineBuildMethod(builder, parsed, buildName, true),
null,
(closureName, closureBaseType) =>
populateBuilder.DefineSubType(closureBaseType, closureName, false),
res.Uri, res
);
//add compiled populate method
var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve().Methods
.First(m => m.Name == populateName);
const string TrampolineName = "!XamlIlPopulateTrampoline";
var trampoline = new MethodDefinition(TrampolineName,
MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
classTypeDefinition.Methods.Add(trampoline);
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
var foundXamlLoader = false;
// Find RobustXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
foreach (var method in classTypeDefinition.Methods
.Where(m => !m.Attributes.HasFlag(MethodAttributes.Static)))
{
var i = method.Body.Instructions;
for (var c = 1; c < i.Count; c++)
{
if (i[c].OpCode == OpCodes.Call)
{
var op = i[c].Operand as MethodReference;
if (op != null
&& op.Name == TrampolineName)
{
foundXamlLoader = true;
break;
}
if (op != null
&& op.Name == "Load"
&& op.Parameters.Count == 1
&& op.Parameters[0].ParameterType.FullName == "System.Object"
&& op.DeclaringType.FullName == "Robust.Client.UserInterface.XAML.RobustXamlLoader")
{
if (MatchThisCall(i, c - 1))
{
i[c].Operand = trampoline;
foundXamlLoader = true;
}
}
}
}
}
if (!foundXamlLoader)
{
var ctors = classTypeDefinition.GetConstructors()
.Where(c => !c.IsStatic).ToList();
// We can inject xaml loader into default constructor
if (ctors.Count == 1 && ctors[0].Body.Instructions.Count(o=>o.OpCode != OpCodes.Nop) == 3)
{
var i = ctors[0].Body.Instructions;
var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret));
i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline));
i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
}
else
{
throw new InvalidProgramException(
$"No call to RobustXamlLoader.Load(this) call found anywhere in the type {classType.FullName} and type seems to have custom constructors.");
}
}
//add compiled build method
var compiledBuildMethod = typeSystem.GetTypeReference(builder).Resolve().Methods
.First(m => m.Name == buildName);
var parameterlessCtor = classTypeDefinition.GetConstructors()
.FirstOrDefault(c => c.IsPublic && !c.IsStatic && !c.HasParameters);
if (compiledBuildMethod != null && parameterlessCtor != null)
{
var i = loaderDispatcherMethod.Body.Instructions;
var nop = Instruction.Create(OpCodes.Nop);
i.Add(Instruction.Create(OpCodes.Ldarg_0));
i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
i.Add(Instruction.Create(OpCodes.Call, stringEquals));
i.Add(Instruction.Create(OpCodes.Brfalse, nop));
if (parameterlessCtor != null)
i.Add(Instruction.Create(OpCodes.Newobj, parameterlessCtor));
else
{
i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
}
i.Add(Instruction.Create(OpCodes.Ret));
i.Add(nop);
}
}
catch (Exception e)
{
engine.LogWarningEvent(new BuildWarningEventArgs("XAMLIL", "", res.Uri, 0, 0, 0, 0,
e.ToString(), "", "CompileRobustXaml"));
}
}
return true;
}
if (embrsc.Resources.Count(CheckXamlName) != 0)
{
if (!CompileGroup(embrsc))
return false;
}
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
return true;
}
public const string ContextNameScopeFieldName = "RobustNameScope";
private static void EmitNameScopeField(XamlLanguageTypeMappings xamlLanguage, CecilTypeSystem typeSystem, IXamlTypeBuilder<IXamlILEmitter> typeBuilder, IXamlILEmitter constructor)
{
var nameScopeType = typeSystem.FindType("Robust.Client.UserInterface.XAML.NameScope");
var field = typeBuilder.DefineField(nameScopeType,
ContextNameScopeFieldName, true, false);
constructor
.Ldarg_0()
.Newobj(nameScopeType.GetConstructor())
.Stfld(field);
}
}
interface IResource : IFileSource
{
string Uri { get; }
string Name { get; }
void Remove();
}
interface IResourceGroup
{
string Name { get; }
IEnumerable<IResource> Resources { get; }
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Robust.Client.NameGenerator
{
/// <summary>
/// Taken from https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/NameReferenceSyntaxReceiver.cs
/// </summary>
internal class NameReferenceSyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new List<ClassDeclarationSyntax>();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax &&
classDeclarationSyntax.AttributeLists.Count > 0)
CandidateClasses.Add(classDeclarationSyntax);
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Compile Link="XamlX\filename" Include="../XamlX/src/XamlX/**/*.cs" />
<Compile Remove="../XamlX/src/XamlX/**/SreTypeSystem.cs" />
<Compile Remove="../XamlX/src/XamlX/obj/**" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,287 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using XamlX.TypeSystem;
namespace Robust.Client.NameGenerator
{
/// <summary>
/// Taken from https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/Infrastructure/RoslynTypeSystem.cs
/// </summary>
public class RoslynTypeSystem : IXamlTypeSystem
{
private readonly List<IXamlAssembly> _assemblies = new List<IXamlAssembly>();
public RoslynTypeSystem(CSharpCompilation compilation)
{
_assemblies.Add(new RoslynAssembly(compilation.Assembly));
var assemblySymbols = compilation
.References
.Select(compilation.GetAssemblyOrModuleSymbol)
.OfType<IAssemblySymbol>()
.Select(assembly => new RoslynAssembly(assembly))
.ToList();
_assemblies.AddRange(assemblySymbols);
}
public IReadOnlyList<IXamlAssembly> Assemblies => _assemblies;
public IXamlAssembly FindAssembly(string substring) => _assemblies[0];
public IXamlType FindType(string name)
{
foreach (var assembly in _assemblies)
{
var type = assembly.FindType(name);
if (type != null)
return type;
}
return null;
}
public IXamlType FindType(string name, string assembly)
{
foreach (var assemblyInstance in _assemblies)
{
var type = assemblyInstance.FindType(name);
if (type != null)
return type;
}
return null;
}
}
public class RoslynAssembly : IXamlAssembly
{
private readonly IAssemblySymbol _symbol;
public RoslynAssembly(IAssemblySymbol symbol) => _symbol = symbol;
public bool Equals(IXamlAssembly other) =>
other is RoslynAssembly roslynAssembly &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynAssembly._symbol);
public string Name => _symbol.Name;
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes =>
_symbol.GetAttributes()
.Select(data => new RoslynAttribute(data, this))
.ToList();
public IXamlType FindType(string fullName)
{
var type = _symbol.GetTypeByMetadataName(fullName);
return type is null ? null : new RoslynType(type, this);
}
}
public class RoslynAttribute : IXamlCustomAttribute
{
private readonly AttributeData _data;
private readonly RoslynAssembly _assembly;
public RoslynAttribute(AttributeData data, RoslynAssembly assembly)
{
_data = data;
_assembly = assembly;
}
public bool Equals(IXamlCustomAttribute other) =>
other is RoslynAttribute attribute &&
_data == attribute._data;
public IXamlType Type => new RoslynType(_data.AttributeClass, _assembly);
public List<object> Parameters =>
_data.ConstructorArguments
.Select(argument => argument.Value)
.ToList();
public Dictionary<string, object> Properties =>
_data.NamedArguments.ToDictionary(
pair => pair.Key,
pair => pair.Value.Value);
}
public class RoslynType : IXamlType
{
private static readonly SymbolDisplayFormat SymbolDisplayFormat = new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters |
SymbolDisplayGenericsOptions.IncludeTypeConstraints |
SymbolDisplayGenericsOptions.IncludeVariance);
private readonly RoslynAssembly _assembly;
private readonly INamedTypeSymbol _symbol;
public RoslynType(INamedTypeSymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlType other) =>
other is RoslynType roslynType &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynType._symbol);
public object Id => _symbol;
public string Name => _symbol.Name;
public string Namespace => _symbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat);
public string FullName => $"{Namespace}.{Name}";
public IXamlAssembly Assembly => _assembly;
public IReadOnlyList<IXamlProperty> Properties =>
_symbol.GetMembers()
.Where(member => member.Kind == SymbolKind.Property)
.OfType<IPropertySymbol>()
.Select(property => new RoslynProperty(property, _assembly))
.ToList();
public IReadOnlyList<IXamlEventInfo> Events { get; } = new List<IXamlEventInfo>();
public IReadOnlyList<IXamlField> Fields { get; } = new List<IXamlField>();
public IReadOnlyList<IXamlMethod> Methods { get; } = new List<IXamlMethod>();
public IReadOnlyList<IXamlConstructor> Constructors =>
_symbol.Constructors
.Select(method => new RoslynConstructor(method, _assembly))
.ToList();
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
public IReadOnlyList<IXamlType> GenericArguments { get; } = new List<IXamlType>();
public bool IsAssignableFrom(IXamlType type) => type == this;
public IXamlType MakeGenericType(IReadOnlyList<IXamlType> typeArguments) => this;
public IXamlType GenericTypeDefinition => this;
public bool IsArray => false;
public IXamlType ArrayElementType { get; } = null;
public IXamlType MakeArrayType(int dimensions) => null;
public IXamlType BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly);
public bool IsValueType { get; } = false;
public bool IsEnum { get; } = false;
public IReadOnlyList<IXamlType> Interfaces =>
_symbol.AllInterfaces
.Select(abstraction => new RoslynType(abstraction, _assembly))
.ToList();
public bool IsInterface => _symbol.IsAbstract;
public IXamlType GetEnumUnderlyingType() => null;
public IReadOnlyList<IXamlType> GenericParameters { get; } = new List<IXamlType>();
}
public class RoslynConstructor : IXamlConstructor
{
private readonly IMethodSymbol _symbol;
private readonly RoslynAssembly _assembly;
public RoslynConstructor(IMethodSymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlConstructor other) =>
other is RoslynConstructor roslynConstructor &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynConstructor._symbol);
public bool IsPublic => true;
public bool IsStatic => false;
public IReadOnlyList<IXamlType> Parameters =>
_symbol.Parameters
.Select(parameter => parameter.Type)
.OfType<INamedTypeSymbol>()
.Select(type => new RoslynType(type, _assembly))
.ToList();
}
public class RoslynProperty : IXamlProperty
{
private readonly IPropertySymbol _symbol;
private readonly RoslynAssembly _assembly;
public RoslynProperty(IPropertySymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlProperty other) =>
other is RoslynProperty roslynProperty &&
SymbolEqualityComparer.Default.Equals(_symbol, roslynProperty._symbol);
public string Name => _symbol.Name;
public IXamlType PropertyType =>
_symbol.Type is INamedTypeSymbol namedTypeSymbol
? new RoslynType(namedTypeSymbol, _assembly)
: null;
public IXamlMethod Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly);
public IXamlMethod Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly);
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
public IReadOnlyList<IXamlType> IndexerParameters { get; } = new List<IXamlType>();
}
public class RoslynMethod : IXamlMethod
{
private readonly IMethodSymbol _symbol;
private readonly RoslynAssembly _assembly;
public RoslynMethod(IMethodSymbol symbol, RoslynAssembly assembly)
{
_symbol = symbol;
_assembly = assembly;
}
public bool Equals(IXamlMethod other) =>
other is RoslynMethod roslynMethod &&
SymbolEqualityComparer.Default.Equals(roslynMethod._symbol, _symbol);
public string Name => _symbol.Name;
public bool IsPublic => true;
public bool IsStatic => false;
public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly);
public IReadOnlyList<IXamlType> Parameters =>
_symbol.Parameters.Select(parameter => parameter.Type)
.OfType<INamedTypeSymbol>()
.Select(type => new RoslynType(type, _assembly))
.ToList();
public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType, _assembly);
public IXamlMethod MakeGenericMethod(IReadOnlyList<IXamlType> typeArguments) => null;
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
}
}

View File

@@ -0,0 +1,251 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Parsers;
using XamlX.Transform;
using XamlX.Transform.Transformers;
using XamlX.TypeSystem;
namespace Robust.Client.NameGenerator
{
/// <summary>
/// Based on https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/NameReferenceGenerator.cs
/// Adjusted for our UI-Framework & needs.
/// </summary>
[Generator]
public class XamlUiPartialClassGenerator : ISourceGenerator
{
private const string AttributeName = "Robust.Client.AutoGenerated.GenerateTypedNameReferencesAttribute";
private const string AttributeFile = "GenerateTypedNameReferencesAttribute";
private const string AttributeCode = @"// <auto-generated />
using System;
namespace Robust.Client.AutoGenerated
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class GenerateTypedNameReferencesAttribute : Attribute { }
}
";
class NameVisitor : IXamlAstVisitor
{
private List<(string name, string type)> _names = new List<(string name, string type)>();
public static List<(string name, string type)> GetNames(IXamlAstNode node)
{
var visitor = new NameVisitor();
node.Visit(visitor);
return visitor._names;
}
private bool IsControl(IXamlType type) => type.FullName != "System.Object" &&
(type.FullName == "Robust.Client.UserInterface.Control" ||
IsControl(type.BaseType));
public IXamlAstNode Visit(IXamlAstNode node)
{
if (node is XamlAstObjectNode objectNode)
{
var clrtype = objectNode.Type.GetClrType();
var isControl = IsControl(clrtype);
//clrtype.Interfaces.Any(i =>
//i.IsInterface && i.FullName == "Robust.Client.UserInterface.IControl");
if (!isControl)
return node;
foreach (var child in objectNode.Children)
{
if (child is XamlAstXamlPropertyValueNode propertyValueNode &&
propertyValueNode.Property is XamlAstNamePropertyReference namedProperty &&
namedProperty.Name == "Name" &&
propertyValueNode.Values.Count > 0 &&
propertyValueNode.Values[0] is XamlAstTextNode text)
{
var reg = (text.Text, $@"{clrtype.Namespace}.{clrtype.Name}");
if (!_names.Contains(reg))
{
_names.Add(reg);
}
}
}
}
return node;
}
public void Push(IXamlAstNode node) { }
public void Pop() { }
}
private static string GenerateSourceCode(
INamedTypeSymbol classSymbol,
string xamlFile,
CSharpCompilation comp)
{
var className = classSymbol.Name;
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString();
var parsed = XDocumentXamlParser.Parse(xamlFile);
var typeSystem = new RoslynTypeSystem(comp);
var compiler =
new XamlILCompiler(
new TransformerConfiguration(typeSystem, typeSystem.Assemblies[0],
new XamlLanguageTypeMappings(typeSystem)),
new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>(), false);
compiler.Transformers.Add(new TypeReferenceResolver());
compiler.Transform(parsed);
var initialRoot = (XamlAstObjectNode) parsed.Root;
var names = NameVisitor.GetNames(initialRoot);
//var names = NameVisitor.GetNames((XamlAstObjectNode)XDocumentXamlParser.Parse(xamlFile).Root);
var namedControls = names.Select(info => " " +
$"protected global::{info.type} {info.name} => " +
$"this.FindControl<global::{info.type}>(\"{info.name}\");");
return $@"// <auto-generated />
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace {nameSpace}
{{
partial class {className}
{{
{string.Join("\n", namedControls)}
}}
}}
";
}
public void Execute(GeneratorExecutionContext context)
{
var comp = (CSharpCompilation) context.Compilation;
if(comp.GetTypeByMetadataName(AttributeName) == null)
context.AddSource(AttributeFile, SourceText.From(AttributeCode, Encoding.UTF8));
if (!(context.SyntaxReceiver is NameReferenceSyntaxReceiver receiver))
{
return;
}
var symbols = UnpackAnnotatedTypes(context, comp, receiver);
if(symbols == null)
return;
foreach (var typeSymbol in symbols)
{
var xamlFileName = $"{typeSymbol.Name}.xaml";
var relevantXamlFile = context.AdditionalFiles.FirstOrDefault(t => t.Path.EndsWith(xamlFileName));
if (relevantXamlFile == null)
{
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"RXN0001",
$"Unable to discover the relevant Robust XAML file for {typeSymbol}.",
"Unable to discover the relevant Robust XAML file " +
$"expected at {xamlFileName}",
"Usage",
DiagnosticSeverity.Error,
true),
typeSymbol.Locations[0]));
return;
}
var txt = relevantXamlFile.GetText()?.ToString();
if (txt == null)
{
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"RXN0002",
$"Unexpected empty Xaml-File was found at {xamlFileName}",
"Expected Content due to a Class with the same name being annotated with [GenerateTypedNameReferences].",
"Usage",
DiagnosticSeverity.Error,
true),
Location.Create(xamlFileName, new TextSpan(0,0), new LinePositionSpan(new LinePosition(0,0),new LinePosition(0,0)))));
return;
}
try
{
var sourceCode = GenerateSourceCode(typeSymbol, txt, comp);
context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8));
}
catch (Exception e)
{
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"AXN0003",
"Unhandled exception occured while generating typed Name references.",
$"Unhandled exception occured while generating typed Name references: {e}",
"Usage",
DiagnosticSeverity.Error,
true),
typeSymbol.Locations[0]));
return;
}
}
}
private IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(in GeneratorExecutionContext context, CSharpCompilation comp, NameReferenceSyntaxReceiver receiver)
{
var options = (CSharpParseOptions) comp.SyntaxTrees[0].Options;
var compilation =
comp.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(AttributeCode, Encoding.UTF8), options));
var symbols = new List<INamedTypeSymbol>();
var attributeSymbol = compilation.GetTypeByMetadataName(AttributeName);
foreach (var candidateClass in receiver.CandidateClasses)
{
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
var typeSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(candidateClass);
var relevantAttribute = typeSymbol.GetAttributes().FirstOrDefault(attr =>
attr.AttributeClass != null &&
attr.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
if (relevantAttribute == null)
{
continue;
}
var isPartial = candidateClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
if (isPartial)
{
symbols.Add(typeSymbol);
}
else
{
var missingPartialKeywordMessage =
$"The type {typeSymbol.Name} should be declared with the 'partial' keyword " +
"as it is annotated with the [GenerateTypedNameReferences] attribute.";
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"RXN0004",
missingPartialKeywordMessage,
missingPartialKeywordMessage,
"Usage",
DiagnosticSeverity.Error,
true),
Location.None));
return null;
}
}
return symbols;
}
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new NameReferenceSyntaxReceiver());
}
}
}

View File

@@ -104,7 +104,7 @@ namespace Robust.Client.Audio.Midi
private const string OsxSoundfont =
"/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls";
private const string FallbackSoundfont = "/Resources/Midi/fallback.sf2";
private const string FallbackSoundfont = "/Midi/fallback.sf2";
private readonly ResourceLoaderCallbacks _soundfontLoaderCallbacks = new();
@@ -207,13 +207,10 @@ namespace Robust.Client.Audio.Midi
var renderer = new MidiRenderer(_settings!, soundfontLoader);
foreach (var file in _resourceManager.ContentFindFiles(new ResourcePath("/Audio/MidiCustom/")))
foreach (var file in _resourceManager.ContentFindFiles(("/Audio/MidiCustom/")))
{
if (file.Extension != "sf2" && file.Extension != "dls") continue;
if (_resourceManager.TryGetDiskFilePath(file, out var path))
{
renderer.LoadSoundfont(path);
}
renderer.LoadSoundfont(file.ToString());
}
// Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont.
@@ -383,9 +380,10 @@ namespace Robust.Client.Audio.Midi
public override IntPtr Open(string filename)
{
Stream? stream;
if (filename.StartsWith("/Resources/"))
var resourceCache = IoCManager.Resolve<IResourceCache>();
if (resourceCache.ContentFileExists(filename))
{
if (!IoCManager.Resolve<IResourceCache>().TryContentFileRead(filename.Substring(10), out stream))
if (!resourceCache.TryContentFileRead(filename, out stream))
return IntPtr.Zero;
}
else if (File.Exists(filename))

View File

@@ -115,6 +115,7 @@ namespace Robust.Client
IoCManager.Register<IViewVariablesManagerInternal, ViewVariablesManager>();
IoCManager.Register<IClientConGroupController, ClientConGroupController>();
IoCManager.Register<IScriptClient, ScriptClient>();
//IoCManager.Register<IXamlCompiler, XamlCompiler>();
}
}
}

View File

@@ -54,17 +54,17 @@ namespace Robust.Client.GameObjects
return (T) data[key];
}
public override bool TryGetData<T>(Enum key, [MaybeNullWhen(false)] out T data)
public override bool TryGetData<T>(Enum key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
public override bool TryGetData<T>(string key, [MaybeNullWhen(false)] out T data)
public override bool TryGetData<T>(string key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
internal bool TryGetData<T>(object key, [MaybeNullWhen(false)] out T data)
internal bool TryGetData<T>(object key, [NotNullWhen(true)] out T data)
{
if (this.data.TryGetValue(key, out var dat))
{
@@ -72,7 +72,7 @@ namespace Robust.Client.GameObjects
return true;
}
data = default;
data = default!;
return false;
}

View File

@@ -83,6 +83,18 @@ namespace Robust.Client.GameObjects
set => _energy = value;
}
/// <summary>
/// Soft shadow strength multiplier.
/// Has no effect if soft shadows are not enabled.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[Animatable]
public float Softness
{
get => _softness;
set => _softness = value;
}
[ViewVariables(VVAccess.ReadWrite)]
public bool VisibleNested
{
@@ -115,6 +127,7 @@ namespace Robust.Client.GameObjects
private bool _maskAutoRotate;
private Angle _rotation;
private float _energy;
private float _softness;
/// <summary>
/// Radius, in meters.
@@ -167,6 +180,7 @@ namespace Robust.Client.GameObjects
serializer.DataFieldCached(ref _color, "color", Color.White);
serializer.DataFieldCached(ref _enabled, "enabled", true);
serializer.DataFieldCached(ref _energy, "energy", 1f);
serializer.DataFieldCached(ref _softness, "softness", 1f);
serializer.DataFieldCached(ref _maskAutoRotate, "autoRot", false);
serializer.DataFieldCached(ref _visibleNested, "nestedvisible", true);

View File

@@ -18,7 +18,7 @@ namespace Robust.Client.Graphics.Clyde
while (readSamples < totalSamples)
{
var read = vorbis.ReadSamples(buffer, readSamples * channels, (int)totalSamples - readSamples);
var read = vorbis.ReadSamples(buffer, readSamples * channels, buffer.Length - readSamples);
if (read == 0)
{
break;

View File

@@ -377,6 +377,7 @@ namespace Robust.Client.Graphics.Clyde
var lastRange = float.NaN;
var lastPower = float.NaN;
var lastColor = new Color(float.NaN, float.NaN, float.NaN, float.NaN);
var lastSoftness = float.NaN;
Texture? lastMask = null;
for (var i = 0; i < count; i++)
@@ -424,6 +425,12 @@ namespace Robust.Client.Graphics.Clyde
lightShader.SetUniformMaybe("lightColor", lastColor);
}
if (_enableSoftShadows && !MathHelper.CloseTo(lastSoftness, component.Softness))
{
lastSoftness = component.Softness;
lightShader.SetUniformMaybe("lightSoftness", lastSoftness);
}
lightShader.SetUniformMaybe("lightCenter", lightPos);
lightShader.SetUniformMaybe("lightIndex", (i + 0.5f) / ShadowTexture.Height);

View File

@@ -45,4 +45,10 @@
<EmbeddedResource Include="Graphics\Clyde\Shaders\*" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Engine.targets" />
<PropertyGroup>
<RobustToolsPath>../Tools</RobustToolsPath>
</PropertyGroup>
<Target Name="RobustAfterBuild" AfterTargets="Build" />
<Import Project="..\MSBuild\XamlIL.targets" />
</Project>

View File

@@ -6,6 +6,7 @@ using JetBrains.Annotations;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Animations;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
@@ -49,6 +50,45 @@ namespace Robust.Client.UserInterface
[ViewVariables]
public Control? Parent { get; private set; }
public NameScope? NameScope;
//public void AttachNameScope(Dictionary<string, Control> nameScope)
//{
// _nameScope = nameScope;
//}
public NameScope? FindNameScope()
{
foreach (var control in this.GetSelfAndLogicalAncestors())
{
if (control.NameScope != null) return control.NameScope;
}
return null;
}
public T FindControl<T>(string name) where T : Control
{
var nameScope = FindNameScope();
if (nameScope == null)
{
throw new ArgumentException("No Namespace found for Control");
}
var value = nameScope.Find(name);
if (value == null)
{
throw new ArgumentException($"No Control with the name {name} found");
}
if (value is not T ret)
{
throw new ArgumentException($"Control with name {name} had invalid type {value.GetType()}");
}
return ret;
}
internal IUserInterfaceManagerInternal UserInterfaceManagerInternal { get; }
/// <summary>
@@ -62,6 +102,9 @@ namespace Robust.Client.UserInterface
[ViewVariables]
public OrderedChildCollection Children { get; }
[Content]
public virtual ICollection<Control> XamlChildren { get; protected set; }
[ViewVariables] public int ChildCount => _orderedChildren.Count;
/// <summary>
@@ -394,6 +437,7 @@ namespace Robust.Client.UserInterface
UserInterfaceManagerInternal = IoCManager.Resolve<IUserInterfaceManagerInternal>();
StyleClasses = new StyleClassCollection(this);
Children = new OrderedChildCollection(this);
XamlChildren = Children;
}
/// <summary>

View File

@@ -0,0 +1,15 @@
<cc:SS14Window xmlns:cc="clr-namespace:Robust.Client.UserInterface.CustomControls"
xmlns:c="clr-namespace:Robust.Client.UserInterface.Controls">
<c:PanelContainer StyleClasses="windowPanel"/>
<c:VBoxContainer SeparationOverride="0">
<c:PanelContainer Name="WindowHeader" StyleClasses="windowHeader">
<c:HBoxContainer>
<c:MarginContainer MarginLeftOverride="5" SizeFlagsHorizontal="FillExpand">
<c:Label Name="TitleLabel" StyleIdentifier="foo" ClipText="True" Text="Exemplary Window Title Here" VAlign="Center" StyleClasses="windowTitle"/>
</c:MarginContainer>
<c:TextureButton Name="CloseButton" StyleClasses="windowCloseButton" SizeFlagsVertical="ShrinkCenter"></c:TextureButton>
</c:HBoxContainer>
</c:PanelContainer>
<c:MarginContainer Name="ContentsContainer" MarginBottomOverride="10" MarginLeftOverride="10" MarginRightOverride="10" MarginTopOverride="10" RectClipContent="True" SizeFlagsVertical="FillExpand" />
</c:VBoxContainer>
</cc:SS14Window>

View File

@@ -1,12 +1,17 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.CustomControls
{
[GenerateTypedNameReferences]
// ReSharper disable once InconsistentNaming
public class SS14Window : BaseWindow
public partial class SS14Window : BaseWindow
{
public const string StyleClassWindowTitle = "windowTitle";
public const string StyleClassWindowPanel = "windowPanel";
@@ -17,70 +22,19 @@ namespace Robust.Client.UserInterface.CustomControls
public SS14Window()
{
RobustXamlLoader.Load(this);
MouseFilter = MouseFilterMode.Stop;
AddChild(new PanelContainer
{
StyleClasses = {StyleClassWindowPanel}
});
WindowHeader.CustomMinimumSize = (0, HEADER_SIZE_Y);
AddChild(new VBoxContainer
{
SeparationOverride = 0,
Children =
{
new PanelContainer
{
StyleClasses = {StyleClassWindowHeader},
CustomMinimumSize = (0, HEADER_SIZE_Y),
Children =
{
new HBoxContainer
{
Children =
{
new MarginContainer
{
MarginLeftOverride = 5,
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
(TitleLabel = new Label
{
StyleIdentifier = "foo",
ClipText = true,
Text = "Exemplary Window Title Here",
VAlign = Label.VAlignMode.Center,
StyleClasses = {StyleClassWindowTitle}
})
}
},
(CloseButton = new TextureButton
{
StyleClasses = {StyleClassWindowCloseButton},
SizeFlagsVertical = SizeFlags.ShrinkCenter
})
}
}
}
},
(Contents = new MarginContainer
{
MarginBottomOverride = 10,
MarginLeftOverride = 10,
MarginRightOverride = 10,
MarginTopOverride = 10,
RectClipContent = true,
SizeFlagsVertical = SizeFlags.FillExpand
})
}
});
Contents = ContentsContainer;
CloseButton.OnPressed += CloseButtonPressed;
XamlChildren = new SS14ContentCollection(this);
}
public MarginContainer Contents { get; private set; }
private TextureButton CloseButton;
//private TextureButton CloseButton;
private const int DRAG_MARGIN_SIZE = 7;
@@ -103,7 +57,7 @@ namespace Robust.Client.UserInterface.CustomControls
}
}
private Label TitleLabel;
//private Label TitleLabel;
public string? Title
{
@@ -177,5 +131,91 @@ namespace Robust.Client.UserInterface.CustomControls
return mode;
}
public class SS14ContentCollection : ICollection<Control>, IReadOnlyCollection<Control>
{
private readonly SS14Window Owner;
public SS14ContentCollection(SS14Window owner)
{
Owner = owner;
}
public Enumerator GetEnumerator()
{
return new(Owner);
}
IEnumerator<Control> IEnumerable<Control>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Add(Control item)
{
Owner.Contents.AddChild(item);
}
public void Clear()
{
Owner.Contents.RemoveAllChildren();
}
public bool Contains(Control item)
{
return item?.Parent == Owner.Contents;
}
public void CopyTo(Control[] array, int arrayIndex)
{
Owner.Contents.Children.CopyTo(array, arrayIndex);
}
public bool Remove(Control item)
{
if (item?.Parent != Owner.Contents)
{
return false;
}
DebugTools.AssertNotNull(Owner?.Contents);
Owner!.Contents.RemoveChild(item);
return true;
}
int ICollection<Control>.Count => Owner.Contents.ChildCount;
int IReadOnlyCollection<Control>.Count => Owner.Contents.ChildCount;
public bool IsReadOnly => false;
public struct Enumerator : IEnumerator<Control>
{
private OrderedChildCollection.Enumerator _enumerator;
internal Enumerator(SS14Window ss14Window)
{
_enumerator = ss14Window.Contents.Children.GetEnumerator();
}
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset()
{
((IEnumerator) _enumerator).Reset();
}
public Control Current => _enumerator.Current;
object IEnumerator.Current => Current;
public void Dispose()
{
_enumerator.Dispose();
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace Robust.Client.UserInterface
{
public static class LogicalExtensions
{
public static IEnumerable<Control> GetSelfAndLogicalAncestors(this Control control)
{
Control? c = control;
while (c != null)
{
yield return c;
c = c.Parent;
}
}
}
}

View File

@@ -4,6 +4,7 @@ using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -172,8 +173,29 @@ namespace Robust.Client.UserInterface
// This needs to happen because word wrapping doesn't get checked for the last word.
if (posX > maxSizeX)
{
DebugTools.Assert(wordStartBreakIndex.HasValue,
"wordStartBreakIndex can only be null if the word begins at a new line, in which case this branch shouldn't be reached as the word would be split due to being longer than a single line.");
if (!wordStartBreakIndex.HasValue)
{
Logger.Error(
"Assert fail inside RichTextEntry.Update, " +
"wordStartBreakIndex is null on method end w/ word wrap required. " +
"Dumping relevant stuff. Send this to PJB.");
Logger.Error($"Message: {Message}");
Logger.Error($"maxSizeX: {maxSizeX}");
Logger.Error($"maxUsedWidth: {maxUsedWidth}");
Logger.Error($"breakIndexCounter: {breakIndexCounter}");
Logger.Error("wordStartBreakIndex: null (duh)");
Logger.Error($"wordSizePixels: {wordSizePixels}");
Logger.Error($"posX: {posX}");
Logger.Error($"lastChar: {lastChar}");
Logger.Error($"forceSplitData: {forceSplitData}");
Logger.Error($"LineBreaks: {string.Join(", ", LineBreaks)}");
throw new Exception(
"wordStartBreakIndex can only be null if the word begins at a new line," +
"in which case this branch shouldn't be reached as" +
"the word would be split due to being longer than a single line.");
}
LineBreaks.Add(wordStartBreakIndex!.Value.index);
Height += font.GetLineHeight(uiScale);
maxUsedWidth = Math.Max(maxUsedWidth, wordStartBreakIndex.Value.lineSize);

View File

@@ -0,0 +1,42 @@
using System;
namespace Robust.Client.UserInterface.XAML
{
public class ContentAttribute : Attribute
{
}
public class XmlnsDefinitionAttribute : Attribute
{
public XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace)
{
}
}
public class UsableDuringInitializationAttribute : Attribute
{
public UsableDuringInitializationAttribute(bool usable)
{
}
}
public class DeferredContentAttribute : Attribute
{
}
public interface ITestRootObjectProvider
{
object RootObject { get; }
}
public interface ITestProvideValueTarget
{
object TargetObject { get; }
object TargetProperty { get; }
}
public interface ITestUriContext
{
Uri BaseUri { get; set; }
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
namespace Robust.Client.UserInterface.XAML
{
/// <summary>
/// Implements a name scope.
/// </summary>
public class NameScope
{
public bool IsCompleted { get; private set; }
private readonly Dictionary<string, Control> _inner = new Dictionary<string, Control>();
public void Register(string name, Control element)
{
if (IsCompleted)
throw new InvalidOperationException("NameScope is completed, no further registrations are allowed");
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
if (_inner.TryGetValue(name, out Control? existing))
{
if (existing != element)
{
throw new ArgumentException($"Control with the name '{name}' already registered.");
}
}
else
{
_inner.Add(name, element);
}
}
public void Absorb(NameScope? nameScope)
{
if (nameScope == null) return;
foreach (var (name, control) in nameScope._inner)
{
try
{
Register(name, control);
}
catch (Exception e)
{
throw new ArgumentException($"Exception occured when trying to absorb NameScope (at name {name})", e);
}
}
}
public Control? Find(string name)
{
if (name == null)
throw new ArgumentNullException(nameof(name));
_inner.TryGetValue(name, out var result);
return result;
}
public void Complete()
{
IsCompleted = true;
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace Robust.Client.UserInterface.XAML
{
public class RobustXamlLoader
{
public static void Load(object obj)
{
throw new Exception(
$"No precompiled XAML found for {obj.GetType()}, make sure to specify Class or name your class the same as your .xaml ");
}
}
}

View File

@@ -40,17 +40,17 @@ namespace Robust.Server.GameObjects
return (T)data[key];
}
public override bool TryGetData<T>(Enum key, [MaybeNullWhen(false)] out T data)
public override bool TryGetData<T>(Enum key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
public override bool TryGetData<T>(string key, [MaybeNullWhen(false)] out T data)
public override bool TryGetData<T>(string key, [NotNullWhen(true)] out T data)
{
return TryGetData(key, out data);
}
bool TryGetData<T>(object key, [MaybeNullWhen(false)] out T data)
private bool TryGetData<T>(object key, [NotNullWhen(true)] out T data)
{
if (this.data.TryGetValue(key, out var dat))
{

View File

@@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json.Linq;
namespace Robust.Server.Interfaces.ServerStatus
{
public delegate bool StatusHostHandler(
HttpMethod method,
HttpListenerRequest request,
HttpListenerResponse response);
IStatusHandlerContext context);
public interface IStatusHost
{
@@ -32,4 +33,30 @@ namespace Robust.Server.Interfaces.ServerStatus
/// </summary>
event Action<JObject> OnInfoRequest;
}
public interface IStatusHandlerContext
{
HttpMethod RequestMethod { get; }
IPEndPoint RemoteEndPoint { get; }
Uri Url { get; }
bool IsGetLike { get; }
IReadOnlyDictionary<string, StringValues> RequestHeaders { get; }
[return: MaybeNull]
public T RequestBodyJson<T>();
void Respond(
string text,
HttpStatusCode code = HttpStatusCode.OK,
string contentType = "text/plain");
void Respond(
string text,
int code = 200,
string contentType = "text/plain");
void RespondError(HttpStatusCode code);
void RespondJson(object jsonData, HttpStatusCode code = HttpStatusCode.OK);
}
}

View File

@@ -14,9 +14,11 @@
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.0" />
<PackageReference Include="prometheus-net" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Loki" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\ManagedHttpListener\src\System.Net.HttpListener.csproj" />
<ProjectReference Include="..\Robust.Physics\Robust.Physics.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj" />

View File

@@ -1,11 +1,8 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Robust.Server.Interfaces.ServerStatus;
using Robust.Shared;
using Robust.Shared.Utility;
namespace Robust.Server.ServerStatus
{
@@ -20,32 +17,24 @@ namespace Robust.Server.ServerStatus
AddHandler(HandleInfo);
}
private static bool HandleTeapot(HttpMethod method, HttpListenerRequest request, HttpListenerResponse response)
private static bool HandleTeapot(IStatusHandlerContext context)
{
if (!method.IsGetLike() || request.Url!.AbsolutePath != "/teapot")
if (!context.IsGetLike || context.Url!.AbsolutePath != "/teapot")
{
return false;
}
response.Respond(method, "I am a teapot.", (HttpStatusCode) 418);
context.Respond("I am a teapot.", (HttpStatusCode) 418);
return true;
}
private bool HandleStatus(HttpMethod method, HttpListenerRequest request, HttpListenerResponse response)
private bool HandleStatus(IStatusHandlerContext context)
{
if (!method.IsGetLike() || request.Url!.AbsolutePath != "/status")
if (!context.IsGetLike || context.Url!.AbsolutePath != "/status")
{
return false;
}
response.StatusCode = (int) HttpStatusCode.OK;
response.ContentType = "application/json";
if (method == HttpMethod.Head)
{
return true;
}
var jObject = new JObject
{
// We need to send at LEAST name and player count to have the launcher work with us.
@@ -56,32 +45,18 @@ namespace Robust.Server.ServerStatus
OnStatusRequest?.Invoke(jObject);
using var streamWriter = new StreamWriter(response.OutputStream, EncodingHelpers.UTF8);
using var jsonWriter = new JsonTextWriter(streamWriter);
JsonSerializer.Serialize(jsonWriter, jObject);
jsonWriter.Flush();
context.RespondJson(jObject);
return true;
}
private bool HandleInfo(HttpMethod method, HttpListenerRequest request, HttpListenerResponse response)
private bool HandleInfo(IStatusHandlerContext context)
{
if (!method.IsGetLike() || request.Url!.AbsolutePath != "/info")
if (!context.IsGetLike || context.Url!.AbsolutePath != "/info")
{
return false;
}
response.StatusCode = (int) HttpStatusCode.OK;
response.ContentType = "application/json";
if (method == HttpMethod.Head)
{
return true;
}
var downloadUrl = _configurationManager.GetCVar(CVars.BuildDownloadUrl);
JObject? buildInfo;
@@ -125,13 +100,7 @@ namespace Robust.Server.ServerStatus
OnInfoRequest?.Invoke(jObject);
using var streamWriter = new StreamWriter(response.OutputStream, EncodingHelpers.UTF8);
using var jsonWriter = new JsonTextWriter(streamWriter);
JsonSerializer.Serialize(jsonWriter, jObject);
jsonWriter.Flush();
context.RespondJson(jObject);
return true;
}

View File

@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Player;
using Robust.Server.Interfaces.ServerStatus;
using Robust.Shared;
@@ -18,6 +19,9 @@ using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
using HttpListener = ManagedHttpListener.HttpListener;
using HttpListenerContext = ManagedHttpListener.HttpListenerContext;
// This entire file is NIHing a REST server because pulling in libraries is effort.
// Also it was fun to write.
@@ -32,7 +36,6 @@ namespace Robust.Server.ServerStatus
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IServerNetManager _netManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IBaseServer _baseServer = default!;
private static readonly JsonSerializer JsonSerializer = new();
private readonly List<StatusHostHandler> _handlers = new();
@@ -44,17 +47,16 @@ namespace Robust.Server.ServerStatus
public Task ProcessRequestAsync(HttpListenerContext context)
{
var response = context.Response;
var request = context.Request;
var method = new HttpMethod(request.HttpMethod);
var apiContext = (IStatusHandlerContext) new ContextImpl(context);
_httpSawmill.Info($"{method} {context.Request.Url?.PathAndQuery} from {request.RemoteEndPoint}");
_httpSawmill.Info(
$"{apiContext.RequestMethod} {apiContext.Url.PathAndQuery} from {apiContext.RemoteEndPoint}");
try
{
foreach (var handler in _handlers)
{
if (handler(method, request, response))
if (handler(apiContext))
{
return Task.CompletedTask;
}
@@ -62,11 +64,11 @@ namespace Robust.Server.ServerStatus
// No handler returned true, assume no handlers care about this.
// 404.
response.Respond(method, "Not Found", HttpStatusCode.NotFound);
apiContext.Respond("Not Found", HttpStatusCode.NotFound);
}
catch (Exception e)
{
response.Respond(method, "Internal Server Error", HttpStatusCode.InternalServerError);
apiContext.Respond("Internal Server Error", HttpStatusCode.InternalServerError);
_httpSawmill.Error($"Exception in StatusHost: {e}");
}
@@ -195,5 +197,78 @@ namespace Robust.Server.ServerStatus
[JsonProperty("fork_id")] public string ForkId = default!;
[JsonProperty("version")] public string Version = default!;
}
private sealed class ContextImpl : IStatusHandlerContext
{
private readonly HttpListenerContext _context;
public HttpMethod RequestMethod { get; }
public IPEndPoint RemoteEndPoint => _context.Request.RemoteEndPoint!;
public Uri Url => _context.Request.Url!;
public bool IsGetLike => RequestMethod == HttpMethod.Head || RequestMethod == HttpMethod.Get;
public IReadOnlyDictionary<string, StringValues> RequestHeaders { get; }
public ContextImpl(HttpListenerContext context)
{
_context = context;
RequestMethod = new HttpMethod(context.Request.HttpMethod!);
var headers = new Dictionary<string, StringValues>();
foreach (string? key in context.Request.Headers.Keys)
{
if (key == null)
continue;
headers.Add(key, context.Request.Headers.GetValues(key));
}
RequestHeaders = headers;
}
[return: MaybeNull]
public T RequestBodyJson<T>()
{
using var streamReader = new StreamReader(_context.Request.InputStream, EncodingHelpers.UTF8);
using var jsonReader = new JsonTextReader(streamReader);
var serializer = new JsonSerializer();
return serializer.Deserialize<T>(jsonReader);
}
public void Respond(string text, HttpStatusCode code = HttpStatusCode.OK, string contentType = "text/plain")
{
Respond(text, (int) code, contentType);
}
public void Respond(string text, int code = 200, string contentType = "text/plain")
{
_context.Response.StatusCode = code;
_context.Response.ContentType = contentType;
if (RequestMethod == HttpMethod.Head)
{
return;
}
using var writer = new StreamWriter(_context.Response.OutputStream, EncodingHelpers.UTF8);
writer.Write(text);
}
public void RespondError(HttpStatusCode code)
{
Respond(code.ToString(), code);
}
public void RespondJson(object jsonData, HttpStatusCode code = HttpStatusCode.OK)
{
using var streamWriter = new StreamWriter(_context.Response.OutputStream, EncodingHelpers.UTF8);
using var jsonWriter = new JsonTextWriter(streamWriter);
JsonSerializer.Serialize(jsonWriter, jsonData);
jsonWriter.Flush();
}
}
}
}

View File

@@ -1,7 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using Robust.Shared.Utility;
@@ -9,41 +8,6 @@ namespace Robust.Server.ServerStatus
{
public static class StatusHostHelpers
{
public static bool IsGetLike(this HttpMethod method)
{
return method == HttpMethod.Get || method == HttpMethod.Head;
}
public static void Respond(
this HttpListenerResponse response,
HttpMethod method,
string text,
HttpStatusCode code = HttpStatusCode.OK,
string contentType = "text/plain")
{
response.Respond(method, text, (int) code, contentType);
}
public static void Respond(
this HttpListenerResponse response,
HttpMethod method,
string text,
int code = 200,
string contentType = "text/plain")
{
response.StatusCode = code;
response.ContentType = contentType;
if (method == HttpMethod.Head)
{
return;
}
using var writer = new StreamWriter(response.OutputStream, EncodingHelpers.UTF8);
writer.Write(text);
}
[return: MaybeNull]
public static T GetFromJson<T>(this HttpListenerRequest request)
{

View File

@@ -45,9 +45,9 @@ namespace Robust.Server.ServerStatus
_statusHost.AddHandler(ShutdownHandler);
}
private bool UpdateHandler(HttpMethod method, HttpListenerRequest request, HttpListenerResponse response)
private bool UpdateHandler(IStatusHandlerContext context)
{
if (method != HttpMethod.Post || request.Url!.AbsolutePath != "/update")
if (context.RequestMethod != HttpMethod.Post || context.Url!.AbsolutePath != "/update")
{
return false;
}
@@ -58,26 +58,26 @@ namespace Robust.Server.ServerStatus
return false;
}
var auth = request.Headers["WatchdogToken"];
var auth = context.RequestHeaders["WatchdogToken"];
if (auth != _watchdogToken)
{
// Holy shit nobody read these logs please.
_sawmill.Info(@"Failed auth: ""{0}"" vs ""{1}""", auth, _watchdogToken);
response.StatusCode = (int) HttpStatusCode.Unauthorized;
context.RespondError(HttpStatusCode.Unauthorized);
return true;
}
_taskManager.RunOnMainThread(() => UpdateReceived?.Invoke());
response.StatusCode = (int) HttpStatusCode.OK;
context.Respond("Success", HttpStatusCode.OK);
return true;
}
private bool ShutdownHandler(HttpMethod method, HttpListenerRequest request, HttpListenerResponse response)
private bool ShutdownHandler(IStatusHandlerContext context)
{
if (method != HttpMethod.Post || request.Url!.AbsolutePath != "/shutdown")
if (context.RequestMethod != HttpMethod.Post || context.Url!.AbsolutePath != "/shutdown")
{
return false;
}
@@ -88,22 +88,21 @@ namespace Robust.Server.ServerStatus
return false;
}
var auth = request.Headers["WatchdogToken"];
var auth = context.RequestHeaders["WatchdogToken"];
if (auth != _watchdogToken)
{
_sawmill.Warning(
"received POST /shutdown with invalid authentication token. Ignoring {0}, {1}", auth,
_watchdogToken);
response.StatusCode = (int) HttpStatusCode.Unauthorized;
context.RespondError(HttpStatusCode.Unauthorized);
return true;
}
ShutdownParameters? parameters = null;
try
{
parameters = request.GetFromJson<ShutdownParameters>();
parameters = context.RequestBodyJson<ShutdownParameters>();
}
catch (JsonSerializationException)
{
@@ -112,14 +111,14 @@ namespace Robust.Server.ServerStatus
if (parameters == null)
{
response.StatusCode = (int) HttpStatusCode.BadRequest;
context.RespondError(HttpStatusCode.BadRequest);
return true;
}
_taskManager.RunOnMainThread(() => _baseServer.Shutdown(parameters.Reason));
response.StatusCode = (int) HttpStatusCode.OK;
context.Respond("Success", HttpStatusCode.OK);
return true;
}

View File

@@ -260,6 +260,11 @@ namespace Robust.Shared.ContentPack
// See II.14.2 in ECMA-335.
return;
}
case MTypeDefined:
{
// Valid for this to show up, safe to ignore.
return;
}
default:
{
throw new ArgumentOutOfRangeException();
@@ -471,6 +476,20 @@ namespace Robust.Shared.ContentPack
break;
}
case HandleKind.TypeDefinition:
{
try
{
parent = GetTypeFromDefinition(reader, (TypeDefinitionHandle) memRef.Parent);
}
catch (UnsupportedMetadataException u)
{
errors.Add(new SandboxError(u));
return null;
}
break;
}
case HandleKind.TypeSpecification:
{
var typeSpec = reader.GetTypeSpecification((TypeSpecificationHandle) memRef.Parent);
@@ -501,7 +520,7 @@ namespace Robust.Shared.ContentPack
default:
{
errors.Add(new SandboxError(
$"Unsupported member ref parent type: {memRef.Parent}. Name: {memName}"));
$"Unsupported member ref parent type: {memRef.Parent.Kind}. Name: {memName}"));
return null;
}
}

View File

@@ -297,6 +297,12 @@ Types:
IEnumerable: { All: True }
IEnumerator: { All: True }
IReadOnlyList`1: { All: True }
System.ComponentModel:
PropertyDescriptor: { }
ISite: { All: True }
IComponent: { All: True }
IContainer: { All: True }
ITypeDescriptorContext: { All: True }
System.Diagnostics.CodeAnalysis:
AllowNullAttribute: { All: True }
DisallowNullAttribute: { All: True }
@@ -651,6 +657,7 @@ Types:
CancellationTokenSource: { All: True }
Interlocked: { All: True }
System:
IServiceProvider: { All: True }
Action: { All: True }
Action`1: { All: True }
Action`2: { All: True }
@@ -1132,6 +1139,7 @@ Types:
# Content should never touch that.
Methods:
- "bool Equals(object)"
- "bool Equals(System.Type)"
- "bool get_ContainsGenericParameters()"
- "bool get_HasElementType()"
- "bool get_IsAbstract()"
@@ -1251,6 +1259,7 @@ Types:
UInt32: { All: True }
UInt64: { All: True }
UIntPtr: { All: True }
Uri: { All: True }
ValueTuple: { All: True }
ValueTuple`1: { All: True }
ValueTuple`2: { All: True }

View File

@@ -22,8 +22,8 @@ namespace Robust.Shared.GameObjects.Components.Appearance
public abstract T GetData<T>(string key);
public abstract T GetData<T>(Enum key);
public abstract bool TryGetData<T>(string key, [MaybeNullWhen(false)] out T data);
public abstract bool TryGetData<T>(Enum key, [MaybeNullWhen(false)] out T data);
public abstract bool TryGetData<T>(string key, [NotNullWhen(true)] out T data);
public abstract bool TryGetData<T>(Enum key, [NotNullWhen(true)] out T data);
[Serializable, NetSerializable]
protected class AppearanceComponentState : ComponentState

View File

@@ -614,6 +614,11 @@ namespace Robust.Shared.Serialization
throw new ArgumentException($"Type {type.FullName} is not supported.", nameof(type));
}
public T NodeToType<T>(YamlNode node)
{
return (T) NodeToType(typeof(T), node);
}
private static Type ResolveConcreteType(Type baseType, string typeName)
{
var reflection = IoCManager.Resolve<IReflectionManager>();

View File

@@ -29,6 +29,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Robust.LoaderApi", "Robust.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.LoaderApi", "Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj", "{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ManagedHttpListener", "ManagedHttpListener", "{15D28C35-25F6-4EA8-8D53-29DA7C8A24A7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.HttpListener", "ManagedHttpListener\src\System.Net.HttpListener.csproj", "{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.Injectors", "Robust.Client.Injectors\Robust.Client.Injectors.csproj", "{EEF2C805-5E03-41EA-A916-49C1DD15EF41}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.NameGenerator", "Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj", "{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XamlX", "XamlX", "{1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX", "XamlX\src\XamlX\XamlX.csproj", "{D73768A2-BFCD-4916-8F52-4034C28F345C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.IL.Cecil", "XamlX\src\XamlX.IL.Cecil\XamlX.IL.Cecil.csproj", "{1CDC9C4F-668E-47A3-8A44-216E95644BEB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.Runtime", "XamlX\src\XamlX.Runtime\XamlX.Runtime.csproj", "{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -125,6 +140,54 @@ Global
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|Any CPU.Build.0 = Release|Any CPU
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|x64.ActiveCfg = Release|Any CPU
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|x64.Build.0 = Release|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Debug|x64.ActiveCfg = Debug|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Debug|x64.Build.0 = Debug|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Release|Any CPU.Build.0 = Release|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Release|x64.ActiveCfg = Release|Any CPU
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}.Release|x64.Build.0 = Release|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|x64.ActiveCfg = Debug|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|x64.Build.0 = Debug|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|Any CPU.Build.0 = Release|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|x64.ActiveCfg = Release|Any CPU
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|x64.Build.0 = Release|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|x64.ActiveCfg = Debug|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|x64.Build.0 = Debug|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|Any CPU.Build.0 = Release|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|x64.ActiveCfg = Release|Any CPU
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|x64.Build.0 = Release|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|x64.ActiveCfg = Debug|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|x64.Build.0 = Debug|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|Any CPU.Build.0 = Release|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|x64.ActiveCfg = Release|Any CPU
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|x64.Build.0 = Release|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|x64.ActiveCfg = Debug|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|x64.Build.0 = Debug|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|Any CPU.Build.0 = Release|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|x64.ActiveCfg = Release|Any CPU
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|x64.Build.0 = Release|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|x64.ActiveCfg = Debug|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|x64.Build.0 = Debug|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|Any CPU.Build.0 = Release|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.ActiveCfg = Release|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -132,6 +195,10 @@ Global
GlobalSection(NestedProjects) = preSolution
{ECBCE1D8-05C2-4881-9446-197C4C8E1C14} = {9143C8DD-A989-4089-9149-C50D12189FE4}
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15} = {805C8FD2-0C32-4DA8-BC4B-143BA5D48FF4}
{C3EB43AF-31FD-48F5-A4FB-552D0F13948B} = {15D28C35-25F6-4EA8-8D53-29DA7C8A24A7}
{D73768A2-BFCD-4916-8F52-4034C28F345C} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
{1CDC9C4F-668E-47A3-8A44-216E95644BEB} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {57757344-0FF4-4842-8A68-141CAA18A35D}

1
XamlX Submodule

Submodule XamlX added at dca5a5f8c2

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
</packageSources>
</configuration>