mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
351 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6038483b1e | ||
|
|
39d98d591c | ||
|
|
01c2fc0730 | ||
|
|
1884bb0067 | ||
|
|
1c368bbaa8 | ||
|
|
d16078a35f | ||
|
|
4dd04207ac | ||
|
|
02af42da30 | ||
|
|
2c75c8b36d | ||
|
|
013e6f7ce4 | ||
|
|
cbd7b62ad7 | ||
|
|
c1396f1c50 | ||
|
|
3ec9e7a734 | ||
|
|
3a1e6e84b1 | ||
|
|
7224419f77 | ||
|
|
056e4de0c1 | ||
|
|
aa90f22e23 | ||
|
|
071234095d | ||
|
|
5b06391159 | ||
|
|
8edd44086b | ||
|
|
ccf212e9cb | ||
|
|
493011d1f9 | ||
|
|
40e193df33 | ||
|
|
5068294d38 | ||
|
|
24054b5e2f | ||
|
|
17869c16cd | ||
|
|
d8aad89c2f | ||
|
|
2a349eb023 | ||
|
|
47ad07b3d2 | ||
|
|
aacf6522b4 | ||
|
|
c73d27b9ae | ||
|
|
f068b30a7c | ||
|
|
5400dddcfc | ||
|
|
6cf5fdc5d6 | ||
|
|
5d46663881 | ||
|
|
8e0f227940 | ||
|
|
73a13fff9a | ||
|
|
de2e505a12 | ||
|
|
a9f7c7a76f | ||
|
|
37401c26c9 | ||
|
|
528cd1e0e5 | ||
|
|
2959456bec | ||
|
|
8951712495 | ||
|
|
d8612aff64 | ||
|
|
e16732eb7b | ||
|
|
91f61bb9de | ||
|
|
ddc91d05ec | ||
|
|
ef22842b90 | ||
|
|
303e2152d2 | ||
|
|
37fc0d0d2a | ||
|
|
53987e1e5d | ||
|
|
3216d7770b | ||
|
|
3203ca2ff4 | ||
|
|
e22254cd51 | ||
|
|
7ed722f669 | ||
|
|
4864096b2a | ||
|
|
5161385de4 | ||
|
|
98e009b38f | ||
|
|
3863ab8f62 | ||
|
|
f576eb5125 | ||
|
|
314742ccd8 | ||
|
|
f9074811f9 | ||
|
|
5f3e1eb378 | ||
|
|
3c1ee20ca1 | ||
|
|
3768f5e68e | ||
|
|
765a560380 | ||
|
|
39ae3ac653 | ||
|
|
e48f4027e5 | ||
|
|
2fa1e98faf | ||
|
|
cedfa0ee2f | ||
|
|
92f44b390e | ||
|
|
65a42f9209 | ||
|
|
ebf53248cf | ||
|
|
289f637e8a | ||
|
|
d7c13f30c8 | ||
|
|
0dac17ae5e | ||
|
|
9a19a774fa | ||
|
|
81f49d5eb2 | ||
|
|
4f3b4ac2d2 | ||
|
|
e428056b52 | ||
|
|
8dc9d2989a | ||
|
|
fd8c90dcbb | ||
|
|
ffe4e5a8ab | ||
|
|
6e5026d270 | ||
|
|
946c4166dc | ||
|
|
7d2fb85a04 | ||
|
|
d6ec078519 | ||
|
|
32256fc4d9 | ||
|
|
37bbdfe7ff | ||
|
|
c906675cdf | ||
|
|
90bb5574c1 | ||
|
|
7b50dcd969 | ||
|
|
8d82f48a8f | ||
|
|
469f9fd219 | ||
|
|
1a5783ab4e | ||
|
|
3d25886d79 | ||
|
|
516b2cd372 | ||
|
|
3cfcfa0be2 | ||
|
|
69328087bd | ||
|
|
1bf8b2a52b | ||
|
|
fc6dc6f4e1 | ||
|
|
31c1feca4e | ||
|
|
3ed1eef2ab | ||
|
|
1394a017bb | ||
|
|
6b0670d5f1 | ||
|
|
f573331541 | ||
|
|
a7218cd3b8 | ||
|
|
f7e8178736 | ||
|
|
31f921e4aa | ||
|
|
aa1c25637c | ||
|
|
71f2c48463 | ||
|
|
d65f4ca898 | ||
|
|
b35568ffe5 | ||
|
|
a0d241e551 | ||
|
|
33a6934582 | ||
|
|
f237a8bbbc | ||
|
|
4bc775c01c | ||
|
|
93b4d81505 | ||
|
|
0afb85a09e | ||
|
|
7b9315cea4 | ||
|
|
dc3af45096 | ||
|
|
00ce0179ae | ||
|
|
81947ba3d8 | ||
|
|
49327279d0 | ||
|
|
0936cf3c7f | ||
|
|
43b75a69c2 | ||
|
|
c17c8d7a11 | ||
|
|
223fd8126f | ||
|
|
1d5559be4a | ||
|
|
0b749ff8bb | ||
|
|
069fa89fcb | ||
|
|
80f9f24243 | ||
|
|
93018c9843 | ||
|
|
e2675271d0 | ||
|
|
d1f7edecef | ||
|
|
b5a3c0b988 | ||
|
|
06e62b031a | ||
|
|
24707b7385 | ||
|
|
ab95f39f9f | ||
|
|
cdd38abab5 | ||
|
|
d751c0b3ab | ||
|
|
2ace0e9e5a | ||
|
|
31716f5104 | ||
|
|
fefcc7cba3 | ||
|
|
30df989e8d | ||
|
|
86bfea6bd4 | ||
|
|
d890f168c2 | ||
|
|
f888a810bf | ||
|
|
16249a4dde | ||
|
|
e33488ba55 | ||
|
|
bfe6eeddb1 | ||
|
|
7f540e741c | ||
|
|
b7855f5af5 | ||
|
|
91391e1205 | ||
|
|
d5199ec459 | ||
|
|
e1e6f4fd10 | ||
|
|
e5b6fccf67 | ||
|
|
95a912c329 | ||
|
|
2b4833fc4e | ||
|
|
b814fc851a | ||
|
|
e87863203b | ||
|
|
33b66d9e18 | ||
|
|
fd406f7897 | ||
|
|
7a836d1018 | ||
|
|
393c15c44a | ||
|
|
a7f31f9ebf | ||
|
|
b332644d48 | ||
|
|
510f7c0e7c | ||
|
|
fdd05e3d3a | ||
|
|
6d41958853 | ||
|
|
cecc4dfcf2 | ||
|
|
4ac40f2e90 | ||
|
|
3e12d44173 | ||
|
|
a42b39bd84 | ||
|
|
22affccf24 | ||
|
|
028724c47b | ||
|
|
0114bff2fc | ||
|
|
4ddbd644eb | ||
|
|
f0366531ef | ||
|
|
6bd5814f4a | ||
|
|
78c132fdab | ||
|
|
460cf57d7c | ||
|
|
a3190a8aca | ||
|
|
6921fb2fbf | ||
|
|
9954d571de | ||
|
|
17fe000a1e | ||
|
|
fba415e765 | ||
|
|
583b7ebf38 | ||
|
|
771a256925 | ||
|
|
ae79e89347 | ||
|
|
6c7eeb95eb | ||
|
|
de0bd1887f | ||
|
|
eb3a815d48 | ||
|
|
e2a4dcdff1 | ||
|
|
68b0d7bf2e | ||
|
|
a9b163992b | ||
|
|
2409965cf8 | ||
|
|
eada37378a | ||
|
|
0f1da1ba2a | ||
|
|
e0cdcd228e | ||
|
|
fdb5e014b5 | ||
|
|
cefcad775b | ||
|
|
e40feac1f1 | ||
|
|
3ef4ac7452 | ||
|
|
93bf1b09e7 | ||
|
|
a1e557e870 | ||
|
|
864adb7445 | ||
|
|
9e3f3f0c1c | ||
|
|
a40c4a435c | ||
|
|
17182dd0e8 | ||
|
|
d8b50044a2 | ||
|
|
4dc396e73d | ||
|
|
6ae0b0e892 | ||
|
|
7162ca3456 | ||
|
|
1b44c1a1b8 | ||
|
|
5b80b33e00 | ||
|
|
f05c1b2395 | ||
|
|
d9b2c73440 | ||
|
|
29a39f8e0a | ||
|
|
2d72a2bdb5 | ||
|
|
91da635631 | ||
|
|
68ab3d504a | ||
|
|
5187040a64 | ||
|
|
e0c63e7ce6 | ||
|
|
0ba2272cab | ||
|
|
2183cd7ca1 | ||
|
|
cbf01f0aa5 | ||
|
|
6973d43006 | ||
|
|
c5a961d9a0 | ||
|
|
b4f7d71ee7 | ||
|
|
cd3684e575 | ||
|
|
317b8ce965 | ||
|
|
ff96140afa | ||
|
|
bade95f6b7 | ||
|
|
69daaca399 | ||
|
|
5af7e60043 | ||
|
|
ab823dcd12 | ||
|
|
b3976eb8d7 | ||
|
|
91759cdd3c | ||
|
|
b000c3178b | ||
|
|
63b3ecdd13 | ||
|
|
a183a98f75 | ||
|
|
bded7ad228 | ||
|
|
75c4f48496 | ||
|
|
49fe2d59cf | ||
|
|
8a5b7f5146 | ||
|
|
ae4f470e1f | ||
|
|
18fcab6f71 | ||
|
|
1ae6c32c62 | ||
|
|
cf6925f19b | ||
|
|
b4c7ffe38a | ||
|
|
d5c2f45f14 | ||
|
|
dcc88d2d36 | ||
|
|
a274b8dfc2 | ||
|
|
f42c1379e0 | ||
|
|
c4fa7e98d4 | ||
|
|
8b9dadffb1 | ||
|
|
5e99c6d04d | ||
|
|
606d232dbb | ||
|
|
df877582a6 | ||
|
|
8ffdb090e6 | ||
|
|
033d6ffb22 | ||
|
|
3eb6e067f9 | ||
|
|
1f64f93ef4 | ||
|
|
7dd617b0d6 | ||
|
|
0567b70704 | ||
|
|
9963e89c14 | ||
|
|
29e0faed88 | ||
|
|
0ce6ff9870 | ||
|
|
dbe9a96dfa | ||
|
|
fc5e3ab69d | ||
|
|
ed4974141c | ||
|
|
3e5efd5ed0 | ||
|
|
e1110eadb4 | ||
|
|
06ace83a73 | ||
|
|
cd01ca924b | ||
|
|
38ad8ce132 | ||
|
|
ee440c2df9 | ||
|
|
32f3c863fb | ||
|
|
b00e0bef5a | ||
|
|
0a09b27918 | ||
|
|
c9f6a4e32a | ||
|
|
b205a14f69 | ||
|
|
d5f3292e0a | ||
|
|
561e4b330e | ||
|
|
36a5d102ff | ||
|
|
b9c39e0953 | ||
|
|
ad4c8be132 | ||
|
|
988cbf9a87 | ||
|
|
e26512001a | ||
|
|
8e97982f1e | ||
|
|
3ca686298e | ||
|
|
5e914cb13a | ||
|
|
a1bdfca8ba | ||
|
|
79deaca409 | ||
|
|
2eeb21431b | ||
|
|
c4062bcae9 | ||
|
|
cd3a85ea04 | ||
|
|
d15b5c7f22 | ||
|
|
18bbe2271d | ||
|
|
ee2b7a3a66 | ||
|
|
ca36671131 | ||
|
|
604a1a6960 | ||
|
|
2898f5396f | ||
|
|
39541639c5 | ||
|
|
50981ad1a1 | ||
|
|
0cbfbeffae | ||
|
|
e603153016 | ||
|
|
e7c417ca0c | ||
|
|
a3989f28eb | ||
|
|
38ace3c348 | ||
|
|
0e00170f45 | ||
|
|
261ee96cad | ||
|
|
2c851885db | ||
|
|
849be86455 | ||
|
|
ffd5c120be | ||
|
|
76b15dda70 | ||
|
|
0bf7f519ad | ||
|
|
f97f325a36 | ||
|
|
24315fa787 | ||
|
|
792179657b | ||
|
|
9b92bcf911 | ||
|
|
86e34ea27b | ||
|
|
62cf778958 | ||
|
|
5d667e44c3 | ||
|
|
6d84b8741c | ||
|
|
d08ca59b75 | ||
|
|
f06b046c1c | ||
|
|
bb412a6906 | ||
|
|
1baee3004c | ||
|
|
f18068c13a | ||
|
|
0ba00a1845 | ||
|
|
28caf0d74c | ||
|
|
ecbb32b70b | ||
|
|
8e2a9cc597 | ||
|
|
a7eb8201c9 | ||
|
|
1f95fe6782 | ||
|
|
07c1f9e1af | ||
|
|
826dce6659 | ||
|
|
cdf714f3ba | ||
|
|
671ca7959c | ||
|
|
b7a1345d3a | ||
|
|
835b6ebdba | ||
|
|
0ecabd6553 | ||
|
|
feaa69f825 | ||
|
|
857904a3d9 | ||
|
|
0b37418477 | ||
|
|
f234ecb2c3 | ||
|
|
b449959865 | ||
|
|
8f870403d2 | ||
|
|
d94f702601 |
17
.github/CODEOWNERS
vendored
17
.github/CODEOWNERS
vendored
@@ -1,14 +1,7 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
# Last match in file takes precedence.
|
||||
|
||||
# These owners will be the default owners for everything in the repo.
|
||||
# * @defunkt
|
||||
* @Acruid @PJB3005 @Silvertorch5
|
||||
# Be they Fluent translations or Freemarker templates, I know them both!
|
||||
*.ftl @RemieRichards
|
||||
|
||||
# Order is important. The last matching pattern has the most precedence.
|
||||
# So if a pull request only touches javascript files, only these owners
|
||||
# will be requested to review.
|
||||
# *.js @octocat @github/js
|
||||
|
||||
# You can also use email addresses if you prefer.
|
||||
# docs/* docs@example.com
|
||||
# Ping for all PRs
|
||||
* @Acruid @PJB3005 @Silvertorch5
|
||||
20
.github/workflows/codeql-analysis.yml
vendored
20
.github/workflows/codeql-analysis.yml
vendored
@@ -11,14 +11,14 @@
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '30 18 * * 6'
|
||||
#on:
|
||||
# push:
|
||||
# branches: [ master ]
|
||||
# pull_request:
|
||||
# # The branches below must be a subset of the branches above
|
||||
# branches: [ master ]
|
||||
# schedule:
|
||||
# - cron: '30 18 * * 6'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -38,12 +38,12 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 5.0.100
|
||||
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -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
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: 4a5cedacb2...5fc11c2b2b
5
MSBuild/Robust.Analyzers.targets
Normal file
5
MSBuild/Robust.Analyzers.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Analyzers\Robust.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,2 +1,3 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<Import Project="Robust.Analyzers.targets" />
|
||||
</Project>
|
||||
|
||||
65
MSBuild/XamlIL.targets
Normal file
65
MSBuild/XamlIL.targets
Normal file
@@ -0,0 +1,65 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<PropertyGroup>
|
||||
<!-- Avoid MSBuild adding a None entry for XAML files because they'd show up TWICE in the project view. -->
|
||||
<DefaultItemExcludes>**/*.xaml</DefaultItemExcludes>
|
||||
<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"/>
|
||||
|
||||
<PropertyGroup>
|
||||
<DOTNET_HOST_PATH Condition="'$(DOTNET_HOST_PATH)' == ''">dotnet</DOTNET_HOST_PATH>
|
||||
</PropertyGroup>
|
||||
<Exec
|
||||
Condition="'$(_RobustUseExternalMSBuild)' == 'true'"
|
||||
Command=""$(DOTNET_HOST_PATH)" 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
1
ManagedHttpListener
Submodule
Submodule ManagedHttpListener added at f2aa590fec
Submodule NetSerializer updated: 60d23c01f9...2d4f8b5611
@@ -17,6 +17,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
// On net472, we rely on Mono's DllMap for this. See the .dll.config file.
|
||||
NativeLibrary.SetDllImportResolver(typeof(GLFWNative).Assembly, (name, assembly, path) =>
|
||||
{
|
||||
// Please keep in sync with what Robust.Shared/DllMapHelper.cs does.
|
||||
if (name != "glfw3.dll")
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
|
||||
@@ -574,29 +574,22 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: NGetText
|
||||
- name: Fluent.Net
|
||||
license: |
|
||||
The MIT License (MIT)
|
||||
blushingpenguin and Contributors
|
||||
|
||||
Copyright (c) 2012 Vitaly Zilnik
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
|
||||
- name: NetSerializer
|
||||
license: |
|
||||
|
||||
1
Resources/Locale/en-US/ss14window.ftl
Normal file
1
Resources/Locale/en-US/ss14window.ftl
Normal file
@@ -0,0 +1 @@
|
||||
ss14window-placeholder-title = Exemplary Window Title Here
|
||||
@@ -28,5 +28,10 @@ void fragment()
|
||||
|
||||
highp float occlusion = ChebyshevUpperBound(occlDist, ourDist);
|
||||
|
||||
if (occlusion >= 1.0)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
COLOR = vec4(0.0, 0.0, 0.0, 1.0 - occlusion);
|
||||
}
|
||||
|
||||
BIN
Resources/Textures/debugRotation.rsi/direction1.png
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 B |
BIN
Resources/Textures/debugRotation.rsi/direction1.xcf
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction1.xcf
Normal file
Binary file not shown.
BIN
Resources/Textures/debugRotation.rsi/direction4.png
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Resources/Textures/debugRotation.rsi/direction4.xcf
Normal file
BIN
Resources/Textures/debugRotation.rsi/direction4.xcf
Normal file
Binary file not shown.
17
Resources/Textures/debugRotation.rsi/meta.json
Normal file
17
Resources/Textures/debugRotation.rsi/meta.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [{
|
||||
"name": "direction1",
|
||||
"directions": 1,
|
||||
"delays": [[1.0]]
|
||||
}, {
|
||||
"name": "direction4",
|
||||
"directions": 4,
|
||||
"delays": [[1.0], [1.0], [1.0], [1.0]]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/debugRotation.rsi/uvmap.xcf
Normal file
BIN
Resources/Textures/debugRotation.rsi/uvmap.xcf
Normal file
Binary file not shown.
10
Robust.Analyzers/Diagnostics.cs
Normal file
10
Robust.Analyzers/Diagnostics.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Generators
|
||||
{
|
||||
public static class Diagnostics
|
||||
{
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
}
|
||||
}
|
||||
92
Robust.Analyzers/ExplicitInterfaceAnalyzer.cs
Normal file
92
Robust.Analyzers/ExplicitInterfaceAnalyzer.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Document = Microsoft.CodeAnalysis.Document;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ExplicitInterfaceAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
public readonly SyntaxKind[] ExcludedModifiers =
|
||||
{
|
||||
SyntaxKind.VirtualKeyword,
|
||||
SyntaxKind.AbstractKeyword,
|
||||
SyntaxKind.OverrideKeyword
|
||||
};
|
||||
|
||||
public const string DiagnosticId = "RA0000";
|
||||
|
||||
private const string Title = "No explicit interface specified";
|
||||
private const string MessageFormat = "No explicit interface specified";
|
||||
private const string Description = "Make sure to specify the interface in your method-declaration.";
|
||||
private const string Category = "Usage";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
|
||||
|
||||
private const string RequiresExplicitImplementationAttributeMetadataName =
|
||||
"Robust.Shared.Analyzers.RequiresExplicitImplementationAttribute";
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
ISymbol symbol;
|
||||
Location location;
|
||||
switch (context.Node)
|
||||
{
|
||||
//we already have a explicit interface specified, no need to check further
|
||||
case MethodDeclarationSyntax methodDecl when methodDecl.ExplicitInterfaceSpecifier != null || methodDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
|
||||
return;
|
||||
case PropertyDeclarationSyntax propertyDecl when propertyDecl.ExplicitInterfaceSpecifier != null || propertyDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
|
||||
return;
|
||||
|
||||
case MethodDeclarationSyntax methodDecl:
|
||||
symbol = context.SemanticModel.GetDeclaredSymbol(methodDecl);
|
||||
location = methodDecl.Identifier.GetLocation();
|
||||
break;
|
||||
case PropertyDeclarationSyntax propertyDecl:
|
||||
symbol = context.SemanticModel.GetDeclaredSymbol(propertyDecl);
|
||||
location = propertyDecl.Identifier.GetLocation();
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresExplicitImplementationAttributeMetadataName);
|
||||
|
||||
var isInterfaceMember = symbol?.ContainingType.AllInterfaces.Any(
|
||||
i =>
|
||||
i.GetMembers().Any(m => SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(m)))
|
||||
&& i.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))
|
||||
) ?? false;
|
||||
|
||||
if (isInterfaceMember)
|
||||
{
|
||||
//we do not have an explicit interface specified. bad!
|
||||
var diagnostic = Diagnostic.Create(
|
||||
Rule,
|
||||
location);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Robust.Analyzers/MeansImplicitAssigmentSuppressor.cs
Normal file
42
Robust.Analyzers/MeansImplicitAssigmentSuppressor.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Generators;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class MeansImplicitAssigmentSuppressor : DiagnosticSuppressor
|
||||
{
|
||||
const string MeansImplicitAssignmentAttribute = "Robust.Shared.MeansImplicitAssignmentAttribute";
|
||||
|
||||
public override void ReportSuppressions(SuppressionAnalysisContext context)
|
||||
{
|
||||
var implAttr = context.Compilation.GetTypeByMetadataName(MeansImplicitAssignmentAttribute);
|
||||
foreach (var reportedDiagnostic in context.ReportedDiagnostics)
|
||||
{
|
||||
if(reportedDiagnostic.Id != Diagnostics.MeansImplicitAssignment.SuppressedDiagnosticId) continue;
|
||||
|
||||
var node = reportedDiagnostic.Location.SourceTree?.GetRoot(context.CancellationToken).FindNode(reportedDiagnostic.Location.SourceSpan);
|
||||
if (node == null) continue;
|
||||
|
||||
var symbol = context.GetSemanticModel(reportedDiagnostic.Location.SourceTree).GetDeclaredSymbol(node);
|
||||
|
||||
if (symbol == null || !symbol.GetAttributes().Any(a =>
|
||||
a.AttributeClass?.GetAttributes().Any(attr =>
|
||||
SymbolEqualityComparer.Default.Equals(attr.AttributeClass, implAttr)) == true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
context.ReportSuppression(Suppression.Create(
|
||||
Diagnostics.MeansImplicitAssignment,
|
||||
reportedDiagnostic));
|
||||
}
|
||||
}
|
||||
|
||||
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(Diagnostics.MeansImplicitAssignment);
|
||||
}
|
||||
}
|
||||
13
Robust.Analyzers/Robust.Analyzers.csproj
Normal file
13
Robust.Analyzers/Robust.Analyzers.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<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" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
150
Robust.Analyzers/SerializableAnalyzer.cs
Normal file
150
Robust.Analyzers/SerializableAnalyzer.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class SerializableAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
// Metadata of the analyzer
|
||||
public const string DiagnosticId = "RA0001";
|
||||
|
||||
// You could use LocalizedString but it's a little more complicated for this sample
|
||||
private const string Title = "Class not marked as (Net)Serializable";
|
||||
private const string MessageFormat = "Class not marked as (Net)Serializable";
|
||||
private const string Description = "The class should be marked as (Net)Serializable.";
|
||||
private const string Category = "Usage";
|
||||
|
||||
private const string RequiresSerializableAttributeMetadataName = "Robust.Shared.Analyzers.RequiresSerializableAttribute";
|
||||
private const string SerializableAttributeMetadataName = "System.SerializableAttribute";
|
||||
private const string NetSerializableAttributeMetadataName = "Robust.Shared.Serialization.NetSerializableAttribute";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
|
||||
}
|
||||
|
||||
private bool Marked(INamedTypeSymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
|
||||
{
|
||||
if (namedTypeSymbol == null) return false;
|
||||
if (namedTypeSymbol.GetAttributes()
|
||||
.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))) return true;
|
||||
return Marked(namedTypeSymbol.BaseType, attrSymbol);
|
||||
}
|
||||
|
||||
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresSerializableAttributeMetadataName);
|
||||
var classDecl = (ClassDeclarationSyntax) context.Node;
|
||||
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl);
|
||||
if (classSymbol == null) return;
|
||||
|
||||
if (Marked(classSymbol, attrSymbol))
|
||||
{
|
||||
var attributes = classSymbol.GetAttributes();
|
||||
var serAttr = context.Compilation.GetTypeByMetadataName(SerializableAttributeMetadataName);
|
||||
var netSerAttr = context.Compilation.GetTypeByMetadataName(NetSerializableAttributeMetadataName);
|
||||
|
||||
var hasSerAttr = attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, serAttr));
|
||||
var hasNetSerAttr =
|
||||
attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, netSerAttr));
|
||||
|
||||
if (!hasSerAttr || !hasNetSerAttr)
|
||||
{
|
||||
var requiredAttributes = new List<string>();
|
||||
if(!hasSerAttr) requiredAttributes.Add(SerializableAttributeMetadataName);
|
||||
if(!hasNetSerAttr) requiredAttributes.Add(NetSerializableAttributeMetadataName);
|
||||
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(
|
||||
Rule,
|
||||
classDecl.Identifier.GetLocation(),
|
||||
ImmutableDictionary.CreateRange(new Dictionary<string, string>()
|
||||
{
|
||||
{
|
||||
"requiredAttributes", string.Join(",", requiredAttributes)
|
||||
}
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public class SerializableCodeFixProvider : CodeFixProvider
|
||||
{
|
||||
private const string Title = "Annotate class as (Net)Serializable.";
|
||||
|
||||
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
|
||||
foreach (var diagnostic in context.Diagnostics)
|
||||
{
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var classDecl = root.FindToken(span.Start).Parent.AncestorsAndSelf().OfType<ClassDeclarationSyntax>().First();
|
||||
|
||||
if(!diagnostic.Properties.TryGetValue("requiredAttributes", out var requiredAttributes)) return;
|
||||
|
||||
context.RegisterCodeFix(
|
||||
CodeAction.Create(
|
||||
Title,
|
||||
c => FixAsync(context.Document, classDecl, requiredAttributes, c),
|
||||
Title),
|
||||
diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Document> FixAsync(Document document, ClassDeclarationSyntax classDecl,
|
||||
string requiredAttributes, CancellationToken cancellationToken)
|
||||
{
|
||||
var attributes = new List<AttributeSyntax>();
|
||||
var namespaces = new List<string>();
|
||||
foreach (var attribute in requiredAttributes.Split(','))
|
||||
{
|
||||
var tempSplit = attribute.Split('.');
|
||||
namespaces.Add(string.Join(".",tempSplit.Take(tempSplit.Length-1)));
|
||||
var @class = tempSplit.Last();
|
||||
@class = @class.Substring(0, @class.Length - 9); //cut out "Attribute" at the end
|
||||
attributes.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName(@class)));
|
||||
}
|
||||
|
||||
var newClassDecl =
|
||||
classDecl.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(attributes)));
|
||||
|
||||
var root = (CompilationUnitSyntax) await document.GetSyntaxRootAsync(cancellationToken);
|
||||
root = root.ReplaceNode(classDecl, newClassDecl);
|
||||
|
||||
foreach (var ns in namespaces)
|
||||
{
|
||||
if(root.Usings.Any(u => u.Name.ToString() == ns)) continue;
|
||||
root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ns)));
|
||||
}
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(SerializableAnalyzer.DiagnosticId);
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
12
Robust.Benchmarks/Program.cs
Normal file
12
Robust.Benchmarks/Program.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace Robust.Benchmarks
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Robust.Benchmarks/Robust.Benchmarks.csproj
Normal file
18
Robust.Benchmarks/Robust.Benchmarks.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>../bin/Benchmarks</OutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
</Project>
|
||||
11
Robust.Benchmarks/Serialization/DataDefinitionWithString.cs
Normal file
11
Robust.Benchmarks/Serialization/DataDefinitionWithString.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization
|
||||
{
|
||||
[DataDefinition]
|
||||
public class DataDefinitionWithString
|
||||
{
|
||||
[field: DataField("string")]
|
||||
private string StringField { get; } = default!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Server;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization.Read
|
||||
{
|
||||
public class SerializationReadBenchmark
|
||||
{
|
||||
public SerializationReadBenchmark()
|
||||
{
|
||||
IoCManager.InitThread();
|
||||
ServerIoC.RegisterIoC();
|
||||
IoCManager.BuildGraph();
|
||||
|
||||
var assemblies = new[]
|
||||
{
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"),
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Server"),
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Benchmarks")
|
||||
};
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
IoCManager.Resolve<IConfigurationManagerInternal>().LoadCVarsFromAssembly(assembly);
|
||||
}
|
||||
|
||||
IoCManager.Resolve<IReflectionManager>().LoadAssemblies(assemblies);
|
||||
|
||||
SerializationManager = IoCManager.Resolve<ISerializationManager>();
|
||||
SerializationManager.Initialize();
|
||||
|
||||
StringDataDefNode = new MappingDataNode();
|
||||
StringDataDefNode.AddNode(new ValueDataNode("string"), new ValueDataNode("ABC"));
|
||||
|
||||
var yamlStream = new YamlStream();
|
||||
yamlStream.Load(new StringReader(SeedDataDefinition.Prototype));
|
||||
|
||||
SeedNode = yamlStream.Documents[0].RootNode.ToDataNodeCast<SequenceDataNode>().Cast<MappingDataNode>(0);
|
||||
}
|
||||
|
||||
private ISerializationManager SerializationManager { get; }
|
||||
|
||||
private ValueDataNode StringNode { get; } = new("ABC");
|
||||
|
||||
private ValueDataNode IntNode { get; } = new("1");
|
||||
|
||||
private MappingDataNode StringDataDefNode { get; }
|
||||
|
||||
private MappingDataNode SeedNode { get; }
|
||||
|
||||
[Benchmark]
|
||||
public string? ReadString()
|
||||
{
|
||||
return SerializationManager.ReadValue<string>(StringNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int? ReadInteger()
|
||||
{
|
||||
return SerializationManager.ReadValue<int>(IntNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public DataDefinitionWithString? ReadDataDefinitionWithString()
|
||||
{
|
||||
return SerializationManager.ReadValue<DataDefinitionWithString>(StringDataDefNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public SeedDataDefinition? ReadSeedDataDefinition()
|
||||
{
|
||||
return SerializationManager.ReadValue<SeedDataDefinition>(SeedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
184
Robust.Benchmarks/Serialization/SeedDataDefinition.cs
Normal file
184
Robust.Benchmarks/Serialization/SeedDataDefinition.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization
|
||||
{
|
||||
[Prototype("seed")]
|
||||
public class SeedDataDefinition : IPrototype
|
||||
{
|
||||
public const string Prototype = @"
|
||||
- type: seed
|
||||
id: tobacco
|
||||
name: tobacco
|
||||
seedName: tobacco
|
||||
displayName: tobacco plant
|
||||
productPrototypes:
|
||||
- LeavesTobacco
|
||||
harvestRepeat: Repeat
|
||||
lifespan: 75
|
||||
maturation: 5
|
||||
production: 5
|
||||
yield: 2
|
||||
potency: 20
|
||||
growthStages: 3
|
||||
idealLight: 9
|
||||
idealHeat: 298
|
||||
chemicals:
|
||||
chem.Nicotine:
|
||||
Min: 1
|
||||
Max: 10
|
||||
PotencyDivisor: 10";
|
||||
|
||||
private const string SeedPrototype = "SeedBase";
|
||||
|
||||
[ViewVariables]
|
||||
[field: DataField("id", required: true)]
|
||||
public string ID { get; private init; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier of this seed. Do NOT set this.
|
||||
/// </summary>
|
||||
public int Uid { get; internal set; } = -1;
|
||||
|
||||
#region Tracking
|
||||
|
||||
[ViewVariables] [DataField("name")] public string Name { get; set; } = string.Empty;
|
||||
[ViewVariables] [DataField("seedName")] public string SeedName { get; set; } = string.Empty;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("seedNoun")]
|
||||
public string SeedNoun { get; set; } = "seeds";
|
||||
[ViewVariables] [DataField("displayName")] public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("roundStart")]
|
||||
public bool RoundStart { get; private set; } = true;
|
||||
[ViewVariables] [DataField("mysterious")] public bool Mysterious { get; set; }
|
||||
[ViewVariables] [DataField("immutable")] public bool Immutable { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Output
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("productPrototypes")]
|
||||
public List<string> ProductPrototypes { get; set; } = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("chemicals")]
|
||||
public Dictionary<string, SeedChemQuantity> Chemicals { get; set; } = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("consumeGasses")]
|
||||
public Dictionary<Gas, float> ConsumeGasses { get; set; } = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("exudeGasses")]
|
||||
public Dictionary<Gas, float> ExudeGasses { get; set; } = new();
|
||||
#endregion
|
||||
|
||||
#region Tolerances
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("nutrientConsumption")]
|
||||
public float NutrientConsumption { get; set; } = 0.25f;
|
||||
|
||||
[ViewVariables] [DataField("waterConsumption")] public float WaterConsumption { get; set; } = 3f;
|
||||
[ViewVariables] [DataField("idealHeat")] public float IdealHeat { get; set; } = 293f;
|
||||
[ViewVariables] [DataField("heatTolerance")] public float HeatTolerance { get; set; } = 20f;
|
||||
[ViewVariables] [DataField("idealLight")] public float IdealLight { get; set; } = 7f;
|
||||
[ViewVariables] [DataField("lightTolerance")] public float LightTolerance { get; set; } = 5f;
|
||||
[ViewVariables] [DataField("toxinsTolerance")] public float ToxinsTolerance { get; set; } = 4f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("lowPressureTolerance")]
|
||||
public float LowPressureTolerance { get; set; } = 25f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("highPressureTolerance")]
|
||||
public float HighPressureTolerance { get; set; } = 200f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("pestTolerance")]
|
||||
public float PestTolerance { get; set; } = 5f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("weedTolerance")]
|
||||
public float WeedTolerance { get; set; } = 5f;
|
||||
#endregion
|
||||
|
||||
#region General traits
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("endurance")]
|
||||
public float Endurance { get; set; } = 100f;
|
||||
[ViewVariables] [DataField("yield")] public int Yield { get; set; }
|
||||
[ViewVariables] [DataField("lifespan")] public float Lifespan { get; set; }
|
||||
[ViewVariables] [DataField("maturation")] public float Maturation { get; set; }
|
||||
[ViewVariables] [DataField("production")] public float Production { get; set; }
|
||||
[ViewVariables] [DataField("growthStages")] public int GrowthStages { get; set; } = 6;
|
||||
[ViewVariables] [DataField("harvestRepeat")] public HarvestType HarvestRepeat { get; set; } = HarvestType.NoRepeat;
|
||||
|
||||
[ViewVariables] [DataField("potency")] public float Potency { get; set; } = 1f;
|
||||
// No, I'm not removing these.
|
||||
//public PlantSpread Spread { get; set; }
|
||||
//public PlantMutation Mutation { get; set; }
|
||||
//public float AlterTemperature { get; set; }
|
||||
//public PlantCarnivorous Carnivorous { get; set; }
|
||||
//public bool Parasite { get; set; }
|
||||
//public bool Hematophage { get; set; }
|
||||
//public bool Thorny { get; set; }
|
||||
//public bool Stinging { get; set; }
|
||||
[DataField("ligneous")]
|
||||
public bool Ligneous { get; set; }
|
||||
// public bool Teleporting { get; set; }
|
||||
// public PlantJuicy Juicy { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Cosmetics
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("plantRsi", required: true)]
|
||||
public ResourcePath PlantRsi { get; set; } = default!;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("plantIconState")]
|
||||
public string PlantIconState { get; set; } = "produce";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("bioluminescent")]
|
||||
public bool Bioluminescent { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("bioluminescentColor")]
|
||||
public Color BioluminescentColor { get; set; } = Color.White;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("splatPrototype")]
|
||||
public string? SplatPrototype { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum HarvestType
|
||||
{
|
||||
NoRepeat,
|
||||
Repeat
|
||||
}
|
||||
|
||||
public enum Gas {}
|
||||
|
||||
[DataDefinition]
|
||||
public struct SeedChemQuantity
|
||||
{
|
||||
[DataField("Min")]
|
||||
public int Min;
|
||||
[DataField("Max")]
|
||||
public int Max;
|
||||
[DataField("PotencyDivisor")]
|
||||
public int PotencyDivisor;
|
||||
}
|
||||
}
|
||||
101
Robust.Client.Injectors/CompileRobustXamlTask.cs
Normal file
101
Robust.Client.Injectors/CompileRobustXamlTask.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
16
Robust.Client.Injectors/Extensions.cs
Normal file
16
Robust.Client.Injectors/Extensions.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Robust.Client.Injectors/MathParsing.cs
Normal file
37
Robust.Client.Injectors/MathParsing.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using Pidgin;
|
||||
using static Pidgin.Parser;
|
||||
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
public static class MathParsing
|
||||
{
|
||||
public static Parser<char, float> Single { get; } = Real.Select(c => (float) c);
|
||||
|
||||
public static Parser<char, float> Single1 { get; }
|
||||
= Single.Between(SkipWhitespaces);
|
||||
|
||||
public static Parser<char, (float, float)> Single2 { get; }
|
||||
= Single.Before(SkipWhitespaces).Repeat(2).Select(e =>
|
||||
{
|
||||
var arr = e.ToArray();
|
||||
return (arr[0], arr[1]);
|
||||
});
|
||||
|
||||
public static Parser<char, (float, float, float, float)> Single4 { get; }
|
||||
= Single.Before(SkipWhitespaces).Repeat(4).Select(e =>
|
||||
{
|
||||
var arr = e.ToArray();
|
||||
return (arr[0], arr[1], arr[2], arr[3]);
|
||||
});
|
||||
|
||||
public static Parser<char, float[]> Thickness { get; }
|
||||
= SkipWhitespaces.Then(
|
||||
OneOf(
|
||||
Try(Single4.Select(c => new[] {c.Item1, c.Item2, c.Item3, c.Item4})),
|
||||
Try(Single2.Select(c => new[] {c.Item1, c.Item2})),
|
||||
Try(Single1.Select(c => new[] {c}))
|
||||
));
|
||||
}
|
||||
}
|
||||
62
Robust.Client.Injectors/Program.cs
Normal file
62
Robust.Client.Injectors/Program.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
32
Robust.Client.Injectors/RXamlColorAstNode.cs
Normal file
32
Robust.Client.Injectors/RXamlColorAstNode.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection.Emit;
|
||||
using XamlX.Ast;
|
||||
using XamlX.Emit;
|
||||
using XamlX.IL;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
internal class RXamlColorAstNode
|
||||
: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
|
||||
{
|
||||
private readonly IXamlMethod _method;
|
||||
private readonly string _color;
|
||||
|
||||
public RXamlColorAstNode(IXamlLineInfo lineInfo, RXamlWellKnownTypes types, string color) : base(lineInfo)
|
||||
{
|
||||
_color = color;
|
||||
Type = new XamlAstClrTypeReference(lineInfo, types.Color, false);
|
||||
_method = types.ColorFromXaml;
|
||||
}
|
||||
|
||||
public IXamlAstTypeReference Type { get; }
|
||||
|
||||
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
|
||||
{
|
||||
codeGen.Ldstr(_color);
|
||||
codeGen.EmitCall(_method);
|
||||
|
||||
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
|
||||
}
|
||||
}
|
||||
}
|
||||
93
Robust.Client.Injectors/RXamlVecLikeConstAstNode.cs
Normal file
93
Robust.Client.Injectors/RXamlVecLikeConstAstNode.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using XamlX.Ast;
|
||||
using XamlX.Emit;
|
||||
using XamlX.IL;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
public abstract class RXamlVecLikeConstAstNode<T>
|
||||
: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
|
||||
where T : unmanaged
|
||||
{
|
||||
private readonly IXamlConstructor _constructor;
|
||||
protected readonly T[] Values;
|
||||
|
||||
public RXamlVecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, T[] values)
|
||||
: base(lineInfo)
|
||||
{
|
||||
_constructor = constructor;
|
||||
Values = values;
|
||||
|
||||
var @params = constructor.Parameters;
|
||||
|
||||
if (@params.Count != values.Length)
|
||||
throw new ArgumentException("Invalid amount of parameters");
|
||||
|
||||
if (@params.Any(c => c != componentType))
|
||||
throw new ArgumentException("Invalid constructor: not all parameters match component type");
|
||||
|
||||
Type = new XamlAstClrTypeReference(lineInfo, type, false);
|
||||
}
|
||||
|
||||
public IXamlAstTypeReference Type { get; }
|
||||
|
||||
public virtual XamlILNodeEmitResult Emit(
|
||||
XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
codeGen.Newobj(_constructor);
|
||||
|
||||
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RXamlSingleVecLikeConstAstNode : RXamlVecLikeConstAstNode<float>
|
||||
{
|
||||
public RXamlSingleVecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, float[] values)
|
||||
: base(lineInfo, type, constructor, componentType, values)
|
||||
{
|
||||
}
|
||||
|
||||
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
codeGen.Emit(OpCodes.Ldc_R4, value);
|
||||
}
|
||||
|
||||
return base.Emit(context, codeGen);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RXamlInt32VecLikeConstAstNode : RXamlVecLikeConstAstNode<int>
|
||||
{
|
||||
public RXamlInt32VecLikeConstAstNode(
|
||||
IXamlLineInfo lineInfo,
|
||||
IXamlType type, IXamlConstructor constructor,
|
||||
IXamlType componentType, int[] values)
|
||||
: base(lineInfo, type, constructor, componentType, values)
|
||||
{
|
||||
}
|
||||
|
||||
public override XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
|
||||
IXamlILEmitter codeGen)
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
codeGen.Emit(OpCodes.Ldc_I4, value);
|
||||
}
|
||||
|
||||
return base.Emit(context, codeGen);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Robust.Client.Injectors/RXamlWellKnownTypes.cs
Normal file
58
Robust.Client.Injectors/RXamlWellKnownTypes.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Linq;
|
||||
using XamlX.Transform;
|
||||
using XamlX.TypeSystem;
|
||||
|
||||
namespace Robust.Build.Tasks
|
||||
{
|
||||
class RXamlWellKnownTypes
|
||||
{
|
||||
public XamlTypeWellKnownTypes XamlIlTypes { get; }
|
||||
public IXamlType Single { get; }
|
||||
public IXamlType Int32 { get; }
|
||||
public IXamlType Vector2 { get; }
|
||||
public IXamlConstructor Vector2ConstructorFull { get; }
|
||||
public IXamlType Vector2i { get; }
|
||||
public IXamlConstructor Vector2iConstructorFull { get; }
|
||||
public IXamlType Thickness { get; }
|
||||
public IXamlConstructor ThicknessConstructorFull { get; }
|
||||
public IXamlType Color { get; }
|
||||
public IXamlMethod ColorFromXaml { get; }
|
||||
|
||||
public RXamlWellKnownTypes(TransformerConfiguration cfg)
|
||||
{
|
||||
var ts = cfg.TypeSystem;
|
||||
XamlIlTypes = cfg.WellKnownTypes;
|
||||
Single = ts.GetType("System.Single");
|
||||
Int32 = ts.GetType("System.Int32");
|
||||
|
||||
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2", Single, 2);
|
||||
(Vector2i, Vector2iConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2i", Int32, 2);
|
||||
(Thickness, ThicknessConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Thickness", Single, 4);
|
||||
|
||||
(IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
|
||||
{
|
||||
var type = cfg.TypeSystem.GetType(name);
|
||||
var ctor = type.GetConstructor(Enumerable.Repeat(componentType, componentCount).ToList());
|
||||
|
||||
return (type, ctor);
|
||||
}
|
||||
|
||||
Color = cfg.TypeSystem.GetType("Robust.Shared.Maths.Color");
|
||||
ColorFromXaml = Color.GetMethod(new FindMethodMethodSignature("FromXaml", Color, XamlIlTypes.String)
|
||||
{
|
||||
IsStatic = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class RXamlWellKnownTypesExtensions
|
||||
{
|
||||
public static RXamlWellKnownTypes GetRobustTypes(this AstTransformationContext ctx)
|
||||
{
|
||||
if (ctx.TryGetItem<RXamlWellKnownTypes>(out var rv))
|
||||
return rv;
|
||||
ctx.SetItem(rv = new RXamlWellKnownTypes(ctx.Configuration));
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Robust.Client.Injectors/Robust.Client.Injectors.csproj
Normal file
17
Robust.Client.Injectors/Robust.Client.Injectors.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<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" />
|
||||
<PackageReference Include="Pidgin" Version="2.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\XamlX\src\XamlX.IL.Cecil\XamlX.IL.Cecil.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
201
Robust.Client.Injectors/RobustXamlILCompiler.cs
Normal file
201
Robust.Client.Injectors/RobustXamlILCompiler.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Robust.Client.Injectors/XamlCompiler.Helpers.cs
Normal file
78
Robust.Client.Injectors/XamlCompiler.Helpers.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
434
Robust.Client.Injectors/XamlCompiler.cs
Normal file
434
Robust.Client.Injectors/XamlCompiler.cs
Normal file
@@ -0,0 +1,434 @@
|
||||
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 Pidgin;
|
||||
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("Avalonia.Metadata.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), CustomValueConverter);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static bool CustomValueConverter(
|
||||
AstTransformationContext context,
|
||||
IXamlAstValueNode node,
|
||||
IXamlType type,
|
||||
out IXamlAstValueNode result)
|
||||
{
|
||||
if (!(node is XamlAstTextNode textNode))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var text = textNode.Text;
|
||||
var types = context.GetRobustTypes();
|
||||
|
||||
if (type.Equals(types.Vector2))
|
||||
{
|
||||
var foo = MathParsing.Single2.Parse(text);
|
||||
|
||||
if (!foo.Success)
|
||||
throw new XamlLoadException($"Unable to parse \"{text}\" as a Vector2", node);
|
||||
|
||||
var (x, y) = foo.Value;
|
||||
|
||||
result = new RXamlSingleVecLikeConstAstNode(
|
||||
node,
|
||||
types.Vector2, types.Vector2ConstructorFull,
|
||||
types.Single, new[] {x, y});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.Equals(types.Thickness))
|
||||
{
|
||||
var foo = MathParsing.Thickness.Parse(text);
|
||||
|
||||
if (!foo.Success)
|
||||
throw new XamlLoadException($"Unable to parse \"{text}\" as a Thickness", node);
|
||||
|
||||
var val = foo.Value;
|
||||
float[] full;
|
||||
if (val.Length == 1)
|
||||
{
|
||||
var u = val[0];
|
||||
full = new[] {u, u, u, u};
|
||||
}
|
||||
else if (val.Length == 2)
|
||||
{
|
||||
var h = val[0];
|
||||
var v = val[1];
|
||||
full = new[] {h, v, h, v};
|
||||
}
|
||||
else // 4
|
||||
{
|
||||
full = val;
|
||||
}
|
||||
|
||||
result = new RXamlSingleVecLikeConstAstNode(
|
||||
node,
|
||||
types.Thickness, types.ThicknessConstructorFull,
|
||||
types.Single, full);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.Equals(types.Thickness))
|
||||
{
|
||||
var foo = MathParsing.Thickness.Parse(text);
|
||||
|
||||
if (!foo.Success)
|
||||
throw new XamlLoadException($"Unable to parse \"{text}\" as a Thickness", node);
|
||||
|
||||
var val = foo.Value;
|
||||
float[] full;
|
||||
if (val.Length == 1)
|
||||
{
|
||||
var u = val[0];
|
||||
full = new[] {u, u, u, u};
|
||||
}
|
||||
else if (val.Length == 2)
|
||||
{
|
||||
var h = val[0];
|
||||
var v = val[1];
|
||||
full = new[] {h, v, h, v};
|
||||
}
|
||||
else // 4
|
||||
{
|
||||
full = val;
|
||||
}
|
||||
|
||||
result = new RXamlSingleVecLikeConstAstNode(
|
||||
node,
|
||||
types.Thickness, types.ThicknessConstructorFull,
|
||||
types.Single, full);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.Equals(types.Color))
|
||||
{
|
||||
// TODO: Interpret these colors at XAML compile time instead of at runtime.
|
||||
result = new RXamlColorAstNode(node, types, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
21
Robust.Client.NameGenerator/NameReferenceSyntaxReceiver.cs
Normal file
21
Robust.Client.NameGenerator/NameReferenceSyntaxReceiver.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
287
Robust.Client.NameGenerator/RoslynTypeSystem.cs
Normal file
287
Robust.Client.NameGenerator/RoslynTypeSystem.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
262
Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs
Normal file
262
Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
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)
|
||||
{
|
||||
XmlnsAttributes = {typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute")}
|
||||
}),
|
||||
new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>(), false);
|
||||
compiler.Transformers.Add(new TypeReferenceResolver());
|
||||
compiler.Transform(parsed);
|
||||
var initialRoot = (XamlAstObjectNode) parsed.Root;
|
||||
var names = NameVisitor.GetNames(initialRoot);
|
||||
var fieldAccess = classSymbol.IsSealed ? "private" : "protected";
|
||||
//var names = NameVisitor.GetNames((XamlAstObjectNode)XDocumentXamlParser.Parse(xamlFile).Root);
|
||||
var namedControls = names.Select(info => " " +
|
||||
$"{fieldAccess} 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]));
|
||||
continue;
|
||||
}
|
||||
|
||||
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)))));
|
||||
continue;
|
||||
}
|
||||
|
||||
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]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.GameObjects.Components.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Client.Animations
|
||||
/// <seealso cref="AnimationPlayerComponent"/>
|
||||
public sealed class Animation
|
||||
{
|
||||
public readonly List<AnimationTrack> AnimationTracks = new();
|
||||
public List<AnimationTrack> AnimationTracks { get; private set; } = new();
|
||||
|
||||
public TimeSpan Length { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Shared.Animations;
|
||||
|
||||
namespace Content.Client.Animations
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
public class AnimationTrackControlProperty : AnimationTrackProperty
|
||||
{
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -17,7 +15,7 @@ namespace Robust.Client.Animations
|
||||
/// <summary>
|
||||
/// A list of key frames for when to fire flicks.
|
||||
/// </summary>
|
||||
public readonly List<KeyFrame> KeyFrames = new();
|
||||
public List<KeyFrame> KeyFrames { get; private set; } = new();
|
||||
|
||||
public override (int KeyFrameIndex, float FramePlayingTime) InitPlayback()
|
||||
{
|
||||
@@ -39,8 +37,7 @@ namespace Robust.Client.Animations
|
||||
|
||||
var keyFrame = KeyFrames[keyFrameIndex];
|
||||
|
||||
EntitySystem.Get<AudioSystem>()
|
||||
.Play(keyFrame.Resource, entity, keyFrame.AudioParamsFunc.Invoke());
|
||||
SoundSystem.Play(Filter.Local(), keyFrame.Resource, entity, keyFrame.AudioParamsFunc.Invoke());
|
||||
}
|
||||
|
||||
return (keyFrameIndex, playingTime);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Client.Animations
|
||||
/// </summary>
|
||||
public abstract class AnimationTrackProperty : AnimationTrack
|
||||
{
|
||||
public readonly List<KeyFrame> KeyFrames = new();
|
||||
public List<KeyFrame> KeyFrames { get; protected set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// How to interpolate values when between two keyframes.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
@@ -15,7 +15,7 @@ namespace Robust.Client.Animations
|
||||
/// <summary>
|
||||
/// A list of key frames for when to fire flicks.
|
||||
/// </summary>
|
||||
public readonly List<KeyFrame> KeyFrames = new();
|
||||
public List<KeyFrame> KeyFrames { get; private set; } = new();
|
||||
|
||||
// TODO: Should this layer key be per keyframe maybe?
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Clyde;
|
||||
|
||||
namespace Robust.Client.Audio
|
||||
{
|
||||
|
||||
@@ -4,16 +4,15 @@ using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.Interfaces.Physics;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Utility;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
@@ -64,14 +63,18 @@ namespace Robust.Client.Audio.Midi
|
||||
bool IsAvailable { get; }
|
||||
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
void Shutdown();
|
||||
}
|
||||
|
||||
internal class MidiManager : IDisposable, IMidiManager
|
||||
internal class MidiManager : IMidiManager
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
|
||||
|
||||
public bool IsAvailable
|
||||
{
|
||||
get
|
||||
@@ -104,7 +107,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();
|
||||
|
||||
@@ -155,6 +158,7 @@ namespace Robust.Client.Audio.Midi
|
||||
_midiThread = new Thread(ThreadUpdate);
|
||||
_midiThread.Start();
|
||||
|
||||
_broadPhaseSystem = EntitySystem.Get<SharedBroadPhaseSystem>();
|
||||
FluidsynthInitialized = true;
|
||||
}
|
||||
|
||||
@@ -207,13 +211,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.
|
||||
@@ -302,7 +303,7 @@ namespace Robust.Client.Audio.Midi
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length > 0)
|
||||
{
|
||||
occlusion = IoCManager.Resolve<IPhysicsManager>().IntersectRayPenetration(
|
||||
occlusion = _broadPhaseSystem.IntersectRayPenetration(
|
||||
pos.MapId,
|
||||
new CollisionRay(
|
||||
pos.Position,
|
||||
@@ -319,6 +320,11 @@ namespace Robust.Client.Audio.Midi
|
||||
continue;
|
||||
}
|
||||
|
||||
if (renderer.TrackingEntity != null)
|
||||
{
|
||||
renderer.Source.SetVelocity(renderer.TrackingEntity.GlobalLinearVelocity());
|
||||
}
|
||||
|
||||
if (float.IsNaN(pos.Position.X) || float.IsNaN(pos.Position.Y))
|
||||
{
|
||||
// just duck out instead of move to NaN
|
||||
@@ -356,7 +362,7 @@ namespace Robust.Client.Audio.Midi
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public void Shutdown()
|
||||
{
|
||||
_alive = false;
|
||||
_midiThread?.Join();
|
||||
@@ -382,10 +388,18 @@ namespace Robust.Client.Audio.Midi
|
||||
|
||||
public override IntPtr Open(string filename)
|
||||
{
|
||||
Stream? stream;
|
||||
if (filename.StartsWith("/Resources/"))
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
if (!IoCManager.Resolve<IResourceCache>().TryContentFileRead(filename.Substring(10), out stream))
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
Stream? stream;
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var resourcePath = new ResourcePath(filename);
|
||||
|
||||
if (resourcePath.IsRooted && resourceCache.ContentFileExists(filename))
|
||||
{
|
||||
if (!resourceCache.TryContentFileRead(filename, out stream))
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
else if (File.Exists(filename))
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -72,9 +70,9 @@ namespace Robust.Client.Audio.Midi
|
||||
int PlayerTotalTick { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tick of the MIDI player.
|
||||
/// Gets or sets (seeks) the current tick of the MIDI player.
|
||||
/// </summary>
|
||||
int PlayerTick { get; }
|
||||
int PlayerTick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tick of the sequencer.
|
||||
@@ -235,7 +233,16 @@ namespace Robust.Client.Audio.Midi
|
||||
public bool DisableProgramChangeEvent { get; set; } = true;
|
||||
|
||||
public int PlayerTotalTick => _player?.GetTotalTicks ?? 0;
|
||||
public int PlayerTick => _player?.CurrentTick ?? 0;
|
||||
public int PlayerTick
|
||||
{
|
||||
get => _player?.CurrentTick ?? 0;
|
||||
set
|
||||
{
|
||||
lock (_playerStateLock)
|
||||
_player?.Seek(Math.Max(Math.Min(value, PlayerTotalTick), 0));
|
||||
}
|
||||
}
|
||||
|
||||
public uint SequencerTick => _sequencer?.Tick ?? 0;
|
||||
public double SequencerTimeScale => _sequencer?.TimeScale ?? 0;
|
||||
|
||||
@@ -310,9 +317,8 @@ namespace Robust.Client.Audio.Midi
|
||||
|
||||
lock (_playerStateLock)
|
||||
{
|
||||
if (_player == null)
|
||||
_player = new NFluidsynth.Player(_synth);
|
||||
_player.Stop();
|
||||
_player?.Dispose();
|
||||
_player = new NFluidsynth.Player(_synth);
|
||||
_player.AddMem(buffer);
|
||||
_player.SetPlaybackCallback(MidiPlayerEventHandler);
|
||||
_player.Play();
|
||||
@@ -351,10 +357,7 @@ namespace Robust.Client.Audio.Midi
|
||||
|
||||
public void StopAllNotes()
|
||||
{
|
||||
for (var i = 0; i < 16; i++)
|
||||
{
|
||||
_synth.AllNotesOff(i);
|
||||
}
|
||||
_synth.AllNotesOff(-1);
|
||||
}
|
||||
|
||||
public void LoadSoundfont(string filename, bool resetPresets = false)
|
||||
@@ -475,12 +478,6 @@ namespace Robust.Client.Audio.Midi
|
||||
lock(_playerStateLock)
|
||||
switch (midiEvent.Type)
|
||||
{
|
||||
// Sometimes MIDI files spam these for no good reason and I can't find any info on what they are.
|
||||
case 1:
|
||||
case 5:
|
||||
case 81:
|
||||
break;
|
||||
|
||||
// Note On 0x80
|
||||
case 144:
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
|
||||
@@ -503,8 +500,10 @@ namespace Robust.Client.Audio.Midi
|
||||
|
||||
// Program Change - 0xC0
|
||||
case 192:
|
||||
if(!DisableProgramChangeEvent)
|
||||
if (!DisableProgramChangeEvent)
|
||||
_synth.ProgramChange(midiEvent.Channel, midiEvent.Program);
|
||||
else
|
||||
return;
|
||||
break;
|
||||
|
||||
// Channel Pressure - 0xD0
|
||||
@@ -517,13 +516,17 @@ namespace Robust.Client.Audio.Midi
|
||||
_synth.PitchBend(midiEvent.Channel, midiEvent.Pitch);
|
||||
break;
|
||||
|
||||
// Sometimes MIDI files spam these for no good reason and I can't find any info on what they are.
|
||||
case 1:
|
||||
case 5:
|
||||
case 81:
|
||||
// System Messages - 0xF0
|
||||
case 240:
|
||||
break;
|
||||
return;
|
||||
|
||||
default:
|
||||
_midiSawmill.Warning("Unhandled midi event of type {0}", midiEvent.Type, midiEvent);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (FluidSynthInteropException)
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Net;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.Debugging;
|
||||
using Robust.Client.Interfaces.GameObjects;
|
||||
using Robust.Client.Interfaces.GameStates;
|
||||
using Robust.Client.Interfaces.Utility;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client
|
||||
@@ -25,7 +23,7 @@ namespace Robust.Client
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _net = default!;
|
||||
[Dependency] private readonly IPlayerManager _playMan = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IClientEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IDiscordRichPresence _discord = default!;
|
||||
@@ -50,18 +48,28 @@ namespace Robust.Client
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_net.RegisterNetMessage<MsgServerInfo>(MsgServerInfo.NAME, HandleServerInfo);
|
||||
_net.RegisterNetMessage<MsgSetTickRate>(MsgSetTickRate.NAME, HandleSetTickRate);
|
||||
_net.RegisterNetMessage<MsgServerInfoReq>(MsgServerInfoReq.NAME);
|
||||
_net.Connected += OnConnected;
|
||||
_net.ConnectFailed += OnConnectFailed;
|
||||
_net.Disconnect += OnNetDisconnect;
|
||||
|
||||
_configManager.OnValueChanged(CVars.NetTickrate, TickRateChanged, invokeImmediately: true);
|
||||
|
||||
_playMan.Initialize();
|
||||
_debugDrawMan.Initialize();
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void TickRateChanged(int tickrate)
|
||||
{
|
||||
if (GameInfo != null)
|
||||
{
|
||||
GameInfo.TickRate = (byte) tickrate;
|
||||
}
|
||||
|
||||
_timing.TickRate = (byte) tickrate;
|
||||
Logger.InfoS("client", $"Tickrate changed to: {tickrate} on tick {_timing.CurTick}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ConnectToServer(DnsEndPoint endPoint)
|
||||
{
|
||||
@@ -89,6 +97,25 @@ namespace Robust.Client
|
||||
_net.ClientDisconnect(reason);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void StartSinglePlayer()
|
||||
{
|
||||
DebugTools.Assert(RunLevel < ClientRunLevel.Connecting);
|
||||
DebugTools.Assert(!_net.IsConnected);
|
||||
_playMan.Startup();
|
||||
_playMan.LocalPlayer!.Name = PlayerNameOverride ?? _configManager.GetCVar(CVars.PlayerName);
|
||||
OnRunLevelChanged(ClientRunLevel.SinglePlayerGame);
|
||||
GameStartedSetup();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void StopSinglePlayer()
|
||||
{
|
||||
DebugTools.Assert(RunLevel == ClientRunLevel.SinglePlayerGame);
|
||||
DebugTools.Assert(!_net.IsConnected);
|
||||
GameStoppedReset();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<RunLevelChangedEventArgs>? RunLevelChanged;
|
||||
|
||||
@@ -98,9 +125,39 @@ namespace Robust.Client
|
||||
|
||||
private void OnConnected(object? sender, NetChannelArgs args)
|
||||
{
|
||||
// request base info about the server
|
||||
var msgInfo = _net.CreateNetMessage<MsgServerInfoReq>();
|
||||
_net.ClientSendMessage(msgInfo);
|
||||
_configManager.SyncWithServer();
|
||||
_configManager.ReceivedInitialNwVars += OnReceivedClientData;
|
||||
}
|
||||
|
||||
private void OnReceivedClientData(object? sender, EventArgs e)
|
||||
{
|
||||
_configManager.ReceivedInitialNwVars -= OnReceivedClientData;
|
||||
|
||||
var info = GameInfo;
|
||||
|
||||
var serverName = _configManager.GetCVar<string>("game.hostname");
|
||||
if (info == null)
|
||||
{
|
||||
GameInfo = info = new ServerInfo(serverName);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.ServerName = serverName;
|
||||
}
|
||||
|
||||
var maxPlayers = _configManager.GetCVar<int>("game.maxplayers");
|
||||
info.ServerMaxPlayers = maxPlayers;
|
||||
|
||||
var userName = _net.ServerChannel!.UserName;
|
||||
var userId = _net.ServerChannel.UserId;
|
||||
_discord.Update(info.ServerName, userName, info.ServerMaxPlayers.ToString());
|
||||
// start up player management
|
||||
_playMan.Startup();
|
||||
|
||||
_playMan.LocalPlayer!.UserId = userId;
|
||||
_playMan.LocalPlayer.Name = userName;
|
||||
|
||||
_playMan.LocalPlayer.StatusChanged += OnLocalStatusChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,12 +170,18 @@ namespace Robust.Client
|
||||
DebugTools.Assert(RunLevel < ClientRunLevel.Connected);
|
||||
OnRunLevelChanged(ClientRunLevel.Connected);
|
||||
|
||||
GameStartedSetup();
|
||||
|
||||
PlayerJoinedServer?.Invoke(this, new PlayerEventArgs(session));
|
||||
}
|
||||
|
||||
private void GameStartedSetup()
|
||||
{
|
||||
_entityManager.Startup();
|
||||
_mapManager.Startup();
|
||||
|
||||
_timing.ResetSimTime();
|
||||
_timing.Paused = false;
|
||||
PlayerJoinedServer?.Invoke(this, new PlayerEventArgs(session));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -135,6 +198,7 @@ namespace Robust.Client
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
_configManager.ClearReceivedInitialNwVars();
|
||||
OnRunLevelChanged(ClientRunLevel.Initialize);
|
||||
}
|
||||
|
||||
@@ -151,51 +215,21 @@ namespace Robust.Client
|
||||
PlayerLeaveServer?.Invoke(this, new PlayerEventArgs(_playMan.LocalPlayer?.Session));
|
||||
|
||||
LastDisconnectReason = args.Reason;
|
||||
GameStoppedReset();
|
||||
}
|
||||
|
||||
private void GameStoppedReset()
|
||||
{
|
||||
IoCManager.Resolve<INetConfigurationManager>().FlushMessages();
|
||||
_gameStates.Reset();
|
||||
_playMan.Shutdown();
|
||||
IoCManager.Resolve<IEntityLookup>().Shutdown();
|
||||
_entityManager.Shutdown();
|
||||
_mapManager.Shutdown();
|
||||
_discord.ClearPresence();
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void HandleServerInfo(MsgServerInfo msg)
|
||||
{
|
||||
var info = GameInfo;
|
||||
|
||||
if (info == null)
|
||||
{
|
||||
GameInfo = info = new ServerInfo(msg.ServerName);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.ServerName = msg.ServerName;
|
||||
}
|
||||
|
||||
info.ServerMaxPlayers = msg.ServerMaxPlayers;
|
||||
info.TickRate = msg.TickRate;
|
||||
_timing.TickRate = msg.TickRate;
|
||||
Logger.InfoS("client", $"Tickrate changed to: {msg.TickRate}");
|
||||
|
||||
var userName = msg.MsgChannel.UserName;
|
||||
var userId = msg.MsgChannel.UserId;
|
||||
_discord.Update(info.ServerName, userName, info.ServerMaxPlayers.ToString());
|
||||
// start up player management
|
||||
_playMan.Startup(_net.ServerChannel!);
|
||||
|
||||
_playMan.LocalPlayer!.UserId = userId;
|
||||
_playMan.LocalPlayer.Name = userName;
|
||||
|
||||
_playMan.LocalPlayer.StatusChanged += OnLocalStatusChanged;
|
||||
}
|
||||
|
||||
private void HandleSetTickRate(MsgSetTickRate message)
|
||||
{
|
||||
_timing.TickRate = message.NewTickRate;
|
||||
Logger.InfoS("client", $"Tickrate changed to: {message.NewTickRate} on tick {_timing.CurTick}");
|
||||
}
|
||||
|
||||
private void OnLocalStatusChanged(object? obj, StatusEventArgs eventArgs)
|
||||
{
|
||||
// player finished fully connecting to the server.
|
||||
@@ -246,6 +280,11 @@ namespace Robust.Client
|
||||
/// The client is now in the game, moving around.
|
||||
/// </summary>
|
||||
InGame,
|
||||
|
||||
/// <summary>
|
||||
/// The client is now in singleplayer mode, in-game.
|
||||
/// </summary>
|
||||
SinglePlayerGame,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,49 +1,34 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.ClientEye;
|
||||
using Robust.Client.Graphics.Clyde;
|
||||
using Robust.Client.Graphics.Lighting;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.Debugging;
|
||||
using Robust.Client.Interfaces.GameObjects;
|
||||
using Robust.Client.Interfaces.GameStates;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Lighting;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.Map;
|
||||
using Robust.Client.Interfaces.Placement;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Interfaces.Utility;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Prototypes;
|
||||
using Robust.Client.Reflection;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client
|
||||
{
|
||||
@@ -53,8 +38,14 @@ namespace Robust.Client
|
||||
{
|
||||
SharedIoC.RegisterIoC();
|
||||
|
||||
IoCManager.Register<IPrototypeManager, PrototypeManager>();
|
||||
IoCManager.Register<IGameTiming, ClientGameTiming>();
|
||||
IoCManager.Register<IClientGameTiming, ClientGameTiming>();
|
||||
IoCManager.Register<IPrototypeManager, ClientPrototypeManager>();
|
||||
IoCManager.Register<IMapManager, ClientMapManager>();
|
||||
IoCManager.Register<IMapManagerInternal, ClientMapManager>();
|
||||
IoCManager.Register<IClientMapManager, ClientMapManager>();
|
||||
IoCManager.Register<IEntityManager, ClientEntityManager>();
|
||||
IoCManager.Register<IEntityLookup, SharedEntityLookup>();
|
||||
IoCManager.Register<IComponentFactory, ClientComponentFactory>();
|
||||
IoCManager.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
IoCManager.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
@@ -67,10 +58,12 @@ namespace Robust.Client
|
||||
IoCManager.Register<IResourceCacheInternal, ResourceCache>();
|
||||
IoCManager.Register<IClientNetManager, NetManager>();
|
||||
IoCManager.Register<IClientEntityManager, ClientEntityManager>();
|
||||
IoCManager.Register<IEntityNetworkManager, ClientEntityNetworkManager>();
|
||||
IoCManager.Register<IClientEntityManagerInternal, ClientEntityManager>();
|
||||
IoCManager.Register<IEntityNetworkManager, ClientEntityManager>();
|
||||
IoCManager.Register<IClientGameStateManager, ClientGameStateManager>();
|
||||
IoCManager.Register<IBaseClient, BaseClient>();
|
||||
IoCManager.Register<IPlayerManager, PlayerManager>();
|
||||
IoCManager.Register<ISharedPlayerManager, PlayerManager>();
|
||||
IoCManager.Register<IStateManager, StateManager>();
|
||||
IoCManager.Register<IUserInterfaceManager, UserInterfaceManager>();
|
||||
IoCManager.Register<IUserInterfaceManagerInternal, UserInterfaceManager>();
|
||||
@@ -78,10 +71,10 @@ namespace Robust.Client
|
||||
IoCManager.Register<IDebugDrawingManager, DebugDrawingManager>();
|
||||
IoCManager.Register<ILightManager, LightManager>();
|
||||
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
|
||||
IoCManager.Register<IClientConsole, ClientConsole>();
|
||||
IoCManager.Register<IFontManager, FontManager>();
|
||||
IoCManager.Register<IFontManagerInternal, FontManager>();
|
||||
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
|
||||
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
|
||||
IoCManager.Register<IMidiManager, MidiManager>();
|
||||
IoCManager.Register<IAuthManager, AuthManager>();
|
||||
switch (mode)
|
||||
{
|
||||
case GameController.DisplayMode.Headless:
|
||||
@@ -106,8 +99,9 @@ namespace Robust.Client
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
IoCManager.Register<IFontManager, FontManager>();
|
||||
IoCManager.Register<IFontManagerInternal, FontManager>();
|
||||
IoCManager.Register<IEyeManager, EyeManager>();
|
||||
|
||||
IoCManager.Register<IPlacementManager, PlacementManager>();
|
||||
IoCManager.Register<IOverlayManager, OverlayManager>();
|
||||
IoCManager.Register<IOverlayManagerInternal, OverlayManager>();
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Client.Log;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public class AddStringArgs : EventArgs
|
||||
{
|
||||
public string Text { get; }
|
||||
public Color Color { get; }
|
||||
|
||||
public AddStringArgs(string text, Color color)
|
||||
{
|
||||
Text = text;
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
public class AddFormattedMessageArgs : EventArgs
|
||||
{
|
||||
public readonly FormattedMessage Message;
|
||||
|
||||
public AddFormattedMessageArgs(FormattedMessage message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ClientConsole : IClientConsole, IDebugConsole
|
||||
{
|
||||
private static readonly Color MsgColor = new Color(65, 105, 225);
|
||||
|
||||
[Dependency] private readonly IClientNetManager _network = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly ILogManager logManager = default!;
|
||||
|
||||
private readonly Dictionary<string, IConsoleCommand> _commands = new Dictionary<string, IConsoleCommand>();
|
||||
private bool _requestedCommands;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_network.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
|
||||
_network.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
|
||||
_network.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME);
|
||||
|
||||
Reset();
|
||||
logManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Reset()
|
||||
{
|
||||
_commands.Clear();
|
||||
_requestedCommands = false;
|
||||
_network.Connected += OnNetworkConnected;
|
||||
|
||||
InitializeCommands();
|
||||
SendServerCommandRequest();
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
|
||||
{
|
||||
SendServerCommandRequest();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
// We don't have anything to dispose.
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, IConsoleCommand> Commands => _commands;
|
||||
|
||||
public void AddLine(string text, Color color)
|
||||
{
|
||||
AddString?.Invoke(this, new AddStringArgs(text, color));
|
||||
}
|
||||
|
||||
public void AddLine(string text)
|
||||
{
|
||||
AddLine(text, Color.White);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ClearText?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler<AddStringArgs>? AddString;
|
||||
public event EventHandler? ClearText;
|
||||
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
|
||||
|
||||
private void HandleConCmdAck(MsgConCmdAck msg)
|
||||
{
|
||||
AddLine("< " + msg.Text, MsgColor);
|
||||
}
|
||||
|
||||
private void HandleConCmdReg(MsgConCmdReg msg)
|
||||
{
|
||||
foreach (var cmd in msg.Commands)
|
||||
{
|
||||
var commandName = cmd.Name;
|
||||
|
||||
// Do not do duplicate commands.
|
||||
if (_commands.ContainsKey(commandName))
|
||||
{
|
||||
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
|
||||
_commands[commandName] = command;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes commands (chat messages starting with /)
|
||||
/// </summary>
|
||||
/// <param name="text">input text</param>
|
||||
public void ProcessCommand(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return;
|
||||
|
||||
// echo the command locally
|
||||
AddLine("> " + text, Color.Lime);
|
||||
|
||||
//Commands are processed locally and then sent to the server to be processed there again.
|
||||
var args = new List<string>();
|
||||
|
||||
CommandParsing.ParseArguments(text, args);
|
||||
|
||||
var commandname = args[0];
|
||||
|
||||
var forward = true;
|
||||
if (_commands.ContainsKey(commandname))
|
||||
{
|
||||
var command = _commands[commandname];
|
||||
args.RemoveAt(0);
|
||||
forward = command.Execute(this, args.ToArray());
|
||||
}
|
||||
else if (!_network.IsConnected)
|
||||
{
|
||||
AddLine("Unknown command: " + commandname, Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
if (forward)
|
||||
SendServerConsoleCommand(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates and registeres all local commands.
|
||||
/// </summary>
|
||||
private void InitializeCommands()
|
||||
{
|
||||
foreach (var t in _reflectionManager.GetAllChildren<IConsoleCommand>())
|
||||
{
|
||||
var instance = (IConsoleCommand)Activator.CreateInstance(t)!;
|
||||
if (_commands.ContainsKey(instance.Command))
|
||||
throw new InvalidOperationException($"Command already registered: {instance.Command}");
|
||||
|
||||
_commands[instance.Command] = instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests remote commands from server.
|
||||
/// </summary>
|
||||
public void SendServerCommandRequest()
|
||||
{
|
||||
if (_requestedCommands)
|
||||
return;
|
||||
|
||||
if (!_network.IsConnected)
|
||||
return;
|
||||
|
||||
var msg = _network.CreateNetMessage<MsgConCmdReg>();
|
||||
_network.ClientSendMessage(msg);
|
||||
|
||||
_requestedCommands = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command directly to the server.
|
||||
/// </summary>
|
||||
private void SendServerConsoleCommand(string text)
|
||||
{
|
||||
if (_network == null || !_network.IsConnected)
|
||||
return;
|
||||
|
||||
var msg = _network.CreateNetMessage<MsgConCmd>();
|
||||
msg.Text = text;
|
||||
_network.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
public void AddFormattedLine(FormattedMessage message)
|
||||
{
|
||||
// Why the hell does this class implement IDebugConsole.
|
||||
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These dummies are made purely so list and help can list server-side commands.
|
||||
/// </summary>
|
||||
[Reflect(false)]
|
||||
internal class ServerDummyCommand : IConsoleCommand
|
||||
{
|
||||
internal ServerDummyCommand(string command, string help, string description)
|
||||
{
|
||||
Command = command;
|
||||
Help = help;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public string Command { get; }
|
||||
|
||||
public string Help { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
// Always forward to server.
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
218
Robust.Client/Console/ClientConsoleHost.cs
Normal file
218
Robust.Client/Console/ClientConsoleHost.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Log;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public class AddStringArgs : EventArgs
|
||||
{
|
||||
public string Text { get; }
|
||||
|
||||
public bool Local { get; }
|
||||
|
||||
public bool Error { get; }
|
||||
|
||||
public AddStringArgs(string text, bool local, bool error)
|
||||
{
|
||||
Text = text;
|
||||
Local = local;
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
|
||||
public class AddFormattedMessageArgs : EventArgs
|
||||
{
|
||||
public readonly FormattedMessage Message;
|
||||
|
||||
public AddFormattedMessageArgs(FormattedMessage message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IClientConsoleHost" />
|
||||
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
|
||||
{
|
||||
private bool _requestedCommands;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
|
||||
|
||||
Reset();
|
||||
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
|
||||
}
|
||||
|
||||
private void ProcessCommand(MsgConCmd message)
|
||||
{
|
||||
string? text = message.Text;
|
||||
LogManager.GetSawmill(SawmillName).Info($"SERVER:{text}");
|
||||
|
||||
ExecuteCommand(null, text);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Reset()
|
||||
{
|
||||
AvailableCommands.Clear();
|
||||
_requestedCommands = false;
|
||||
NetManager.Connected += OnNetworkConnected;
|
||||
|
||||
LoadConsoleCommands();
|
||||
SendServerCommandRequest();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<AddStringArgs>? AddString;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddFormattedLine(FormattedMessage message)
|
||||
{
|
||||
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteError(ICommonSession? session, string text)
|
||||
{
|
||||
OutputText(text, true, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExecuteCommand(ICommonSession? session, string command)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
return;
|
||||
|
||||
// echo the command locally
|
||||
WriteError(null, "> " + command);
|
||||
|
||||
//Commands are processed locally and then sent to the server to be processed there again.
|
||||
var args = new List<string>();
|
||||
|
||||
CommandParsing.ParseArguments(command, args);
|
||||
|
||||
var commandName = args[0];
|
||||
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
{
|
||||
var command1 = AvailableCommands[commandName];
|
||||
args.RemoveAt(0);
|
||||
command1.Execute(new ConsoleShell(this, null), command, args.ToArray());
|
||||
}
|
||||
else
|
||||
WriteError(null, "Unknown command: " + commandName);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void RemoteExecuteCommand(ICommonSession? session, string command)
|
||||
{
|
||||
if (!NetManager.IsConnected) // we don't care about session on client
|
||||
return;
|
||||
|
||||
var msg = NetManager.CreateNetMessage<MsgConCmd>();
|
||||
msg.Text = command;
|
||||
NetManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine(ICommonSession? session, string text)
|
||||
{
|
||||
OutputText(text, true, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
// We don't have anything to dispose.
|
||||
}
|
||||
|
||||
private void OutputText(string text, bool local, bool error)
|
||||
{
|
||||
AddString?.Invoke(this, new AddStringArgs(text, local, error));
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
|
||||
{
|
||||
SendServerCommandRequest();
|
||||
}
|
||||
|
||||
private void HandleConCmdAck(MsgConCmdAck msg)
|
||||
{
|
||||
OutputText("< " + msg.Text, false, msg.Error);
|
||||
}
|
||||
|
||||
private void HandleConCmdReg(MsgConCmdReg msg)
|
||||
{
|
||||
foreach (var cmd in msg.Commands)
|
||||
{
|
||||
string? commandName = cmd.Name;
|
||||
|
||||
// Do not do duplicate commands.
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
{
|
||||
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
|
||||
AvailableCommands[commandName] = command;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests remote commands from server.
|
||||
/// </summary>
|
||||
private void SendServerCommandRequest()
|
||||
{
|
||||
if (_requestedCommands)
|
||||
return;
|
||||
|
||||
if (!NetManager.IsConnected)
|
||||
return;
|
||||
|
||||
var msg = NetManager.CreateNetMessage<MsgConCmdReg>();
|
||||
NetManager.ClientSendMessage(msg);
|
||||
|
||||
_requestedCommands = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These dummies are made purely so list and help can list server-side commands.
|
||||
/// </summary>
|
||||
[Reflect(false)]
|
||||
internal class ServerDummyCommand : IConsoleCommand
|
||||
{
|
||||
internal ServerDummyCommand(string command, string help, string description)
|
||||
{
|
||||
Command = command;
|
||||
Help = help;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public string Command { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
public string Help { get; }
|
||||
|
||||
// Always forward to server.
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
shell.RemoteExecuteCommand(argStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Server.Console.Commands
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class AddCompCommand : IClientCommand
|
||||
internal sealed class AddCompCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "addcomp";
|
||||
public string Description => "Adds a component to an entity";
|
||||
public string Help => "addcomp <uid> <componentName>";
|
||||
public string Command => "addcompc";
|
||||
public string Description => "Adds a component to an entity on the client";
|
||||
public string Help => "addcompc <uid> <componentName>";
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.SendText(player, "Wrong number of arguments");
|
||||
shell.WriteLine("Wrong number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,16 +37,18 @@ namespace Robust.Server.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RemoveCompCommand : IClientCommand
|
||||
[UsedImplicitly]
|
||||
internal sealed class RemoveCompCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "rmcomp";
|
||||
public string Command => "rmcompc";
|
||||
public string Description => "Removes a component from an entity.";
|
||||
public string Help => "rmcomp <uid> <componentName>";
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
public string Help => "rmcompc <uid> <componentName>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.SendText(player, "Wrong number of arguments");
|
||||
shell.WriteLine("Wrong number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
@@ -13,18 +13,17 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Spawns a client-side entity with specific type at your feet.";
|
||||
public string Help => "cspawn <entity type>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
|
||||
if (player?.ControlledEntity == null)
|
||||
{
|
||||
console.AddLine("You don't have an attached entity.");
|
||||
return false;
|
||||
shell.WriteLine("You don't have an attached entity.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
entityManager.SpawnEntity(args[0], player.ControlledEntity.Transform.Coordinates);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class CVarCommand : SharedCVarCommand, IConsoleCommand
|
||||
{
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1 || args.Length > 2)
|
||||
{
|
||||
console.AddLine("Must provide exactly one or two arguments.", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("Must provide exactly one or two arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
@@ -26,21 +24,21 @@ namespace Robust.Client.Console.Commands
|
||||
if (name == "?")
|
||||
{
|
||||
var cvars = configManager.GetRegisteredCVars().OrderBy(c => c);
|
||||
console.AddLine(string.Join("\n", cvars));
|
||||
return false;
|
||||
shell.WriteLine(string.Join("\n", cvars));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configManager.IsCVarRegistered(name))
|
||||
{
|
||||
console.AddLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.", Color.Red);
|
||||
return false;
|
||||
shell.WriteError($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length == 1)
|
||||
{
|
||||
// Read CVar
|
||||
var value = configManager.GetCVar<object>(name);
|
||||
console.AddLine(value.ToString() ?? "");
|
||||
shell.WriteLine(value.ToString() ?? "");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -54,11 +52,9 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
console.AddLine($"Input value is in incorrect format for type {type}");
|
||||
shell.WriteLine($"Input value is in incorrect format for type {type}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,10 +65,9 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Saves the client configuration to the config file";
|
||||
public string Help => "saveconfig";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IConfigurationManager>().SaveToFile();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
// This file is for commands that do something to the console itself.
|
||||
// This file is for commands that do something to the console itself.
|
||||
// Not some generic console command type.
|
||||
// Couldn't think of a better name sorry.
|
||||
|
||||
using System;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
@@ -17,10 +12,9 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "Clears the debug console of all messages.";
|
||||
public string Description => "Clears the console.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
console.Clear();
|
||||
return false;
|
||||
shell.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +24,12 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "Fills the console with some nonsense for debugging.";
|
||||
public string Description => "Fill up the console for debugging.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
Color[] colors = { Color.Green, Color.Blue, Color.Red };
|
||||
var random = IoCManager.Resolve<IRobustRandom>();
|
||||
for (int x = 0; x < 50; x++)
|
||||
{
|
||||
console.AddLine("filling...", colors[random.Next(0, colors.Length)]);
|
||||
shell.WriteLine("filling...");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,37 +6,24 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Robust.Client.Input;
|
||||
using System.Threading;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Client.Interfaces.Debugging;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Lighting;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.ResourceManagement.ResourceTypes;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -49,16 +36,14 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "Dump entity list";
|
||||
public string Description => "Dumps entity list of UIDs and prototype.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
foreach (var e in entityManager.GetEntities().OrderBy(e => e.Uid))
|
||||
{
|
||||
console.AddLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.", Color.White);
|
||||
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,12 +53,12 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "Usage: getcomponentregistration <componentName>";
|
||||
public string Description => "Gets component registration information";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var componentFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
@@ -94,19 +79,17 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
message.Append($", NSE: {registration.NetworkSynchronizeExistence}, references:");
|
||||
|
||||
console.AddLine(message.ToString(), Color.White);
|
||||
shell.WriteLine(message.ToString());
|
||||
|
||||
foreach (var type in registration.References)
|
||||
{
|
||||
console.AddLine($" {type}", Color.White);
|
||||
shell.WriteLine($" {type}");
|
||||
}
|
||||
}
|
||||
catch (UnknownComponentException)
|
||||
{
|
||||
console.AddLine($"No registration found for '{args[0]}'", Color.Red);
|
||||
shell.WriteError($"No registration found for '{args[0]}'");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,14 +102,14 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public string Description => "Toggles a debug monitor in the F3 menu.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var monitor = IoCManager.Resolve<IUserInterfaceManager>().DebugMonitors;
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0])
|
||||
@@ -159,11 +142,9 @@ namespace Robust.Client.Console.Commands
|
||||
monitor.ShowInput ^= true;
|
||||
break;
|
||||
default:
|
||||
console.AddLine($"Invalid key: {args[0]}");
|
||||
shell.WriteLine($"Invalid key: {args[0]}");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +154,7 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "Throws an exception";
|
||||
public string Description => "Throws an exception";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
throw new InvalidOperationException("Fuck");
|
||||
}
|
||||
@@ -185,11 +166,10 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "";
|
||||
public string Description => "Enables debug drawing over all bounding boxes in the game, showing their size.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IDebugDrawing>();
|
||||
mgr.DebugColliders = !mgr.DebugColliders;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,11 +179,10 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "";
|
||||
public string Description => "Enables debug drawing over all entity positions in the game.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IDebugDrawing>();
|
||||
mgr.DebugPositions = !mgr.DebugPositions;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,25 +192,24 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "Usage: showrays <raylifetime>";
|
||||
public string Description => "Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[0], out var id))
|
||||
{
|
||||
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
|
||||
return false;
|
||||
shell.WriteError($"{args[0]} is not a valid integer.");
|
||||
return;
|
||||
}
|
||||
|
||||
var mgr = IoCManager.Resolve<IDebugDrawingManager>();
|
||||
mgr.DebugDrawRays = !mgr.DebugDrawRays;
|
||||
console.AddLine("Toggled showing rays to:" + mgr.DebugDrawRays.ToString(), Color.Green);
|
||||
shell.WriteError("Toggled showing rays to:" + mgr.DebugDrawRays.ToString());
|
||||
mgr.DebugRayLifetime = TimeSpan.FromSeconds((double)int.Parse(args[0], CultureInfo.InvariantCulture));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,10 +219,9 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "";
|
||||
public string Description => "Immediately disconnect from the server and go back to the main menu.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IClientNetManager>().ClientDisconnect("Disconnect command used.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,33 +234,33 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public string Description => "Displays verbose diagnostics for an entity.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!new Regex(@"^c?[0-9]+$").IsMatch(args[0])))
|
||||
{
|
||||
console.AddLine("Malformed UID", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("Malformed UID");
|
||||
return;
|
||||
}
|
||||
|
||||
var uid = EntityUid.Parse(args[0]);
|
||||
var entmgr = IoCManager.Resolve<IEntityManager>();
|
||||
if (!entmgr.TryGetEntity(uid, out var entity))
|
||||
{
|
||||
console.AddLine("That entity does not exist. Sorry lad.", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("That entity does not exist. Sorry lad.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.AddLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
|
||||
console.AddLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
|
||||
shell.WriteLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
|
||||
shell.WriteLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
|
||||
foreach (var component in entity.GetAllComponents())
|
||||
{
|
||||
console.AddLine(component.ToString() ?? "");
|
||||
shell.WriteLine(component.ToString() ?? "");
|
||||
if (component is IComponentDebug debug)
|
||||
{
|
||||
foreach (var line in debug.GetDebugString().Split('\n'))
|
||||
@@ -293,12 +270,10 @@ namespace Robust.Client.Console.Commands
|
||||
continue;
|
||||
}
|
||||
|
||||
console.AddLine("\t" + line);
|
||||
shell.WriteLine("\t" + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,12 +283,12 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "sggcell <gridID> <vector2i> [offset]\nThat vector2i param is in the form x<int>,y<int>.";
|
||||
public string Description => "Lists entities on a snap grid cell.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2 && args.Length != 3)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
string gridId = args[0];
|
||||
@@ -322,14 +297,14 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (!int.TryParse(args[0], out var id))
|
||||
{
|
||||
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
|
||||
return false;
|
||||
shell.WriteError($"{args[0]} is not a valid integer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!new Regex(@"^-?[0-9]+,-?[0-9]+$").IsMatch(indices))
|
||||
{
|
||||
console.AddLine("mapIndicies must be of form x<int>,y<int>", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("mapIndicies must be of form x<int>,y<int>");
|
||||
return;
|
||||
}
|
||||
|
||||
SnapGridOffset selectedOffset;
|
||||
@@ -339,8 +314,8 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
console.AddLine("given offset type is not defined", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("given offset type is not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
@@ -354,16 +329,13 @@ namespace Robust.Client.Console.Commands
|
||||
int.Parse(indices.Split(',')[1], CultureInfo.InvariantCulture)),
|
||||
selectedOffset))
|
||||
{
|
||||
console.AddLine(entity.Owner.Uid.ToString());
|
||||
shell.WriteLine(entity.Owner.Uid.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
console.AddLine("grid does not exist", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("grid does not exist");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,19 +345,17 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Changes the name used when attempting to connect to the server.";
|
||||
public string Help => Command + " <name>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
var client = IoCManager.Resolve<IBaseClient>();
|
||||
client.PlayerNameOverride = args[0];
|
||||
|
||||
console.AddLine($"Overriding player name to \"{args[0]}\".", Color.White);
|
||||
|
||||
return false;
|
||||
shell.WriteLine($"Overriding player name to \"{args[0]}\".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,12 +365,12 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Pre-caches a resource.";
|
||||
public string Help => "ldrsc <path> <type>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var reflection = IoCManager.Resolve<IReflectionManager>();
|
||||
@@ -412,8 +382,8 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
catch(ArgumentException)
|
||||
{
|
||||
console.AddLine("Unable to find type", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("Unable to find type");
|
||||
return;
|
||||
}
|
||||
|
||||
var getResourceMethod =
|
||||
@@ -423,7 +393,6 @@ namespace Robust.Client.Console.Commands
|
||||
DebugTools.Assert(getResourceMethod != null);
|
||||
var generic = getResourceMethod!.MakeGenericMethod(type);
|
||||
generic.Invoke(resourceCache, new object[] { args[0], true });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,12 +402,12 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Reloads a resource.";
|
||||
public string Help => "rldrsc <path> <type>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var reflection = IoCManager.Resolve<IReflectionManager>();
|
||||
@@ -450,15 +419,14 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
catch(ArgumentException)
|
||||
{
|
||||
console.AddLine("Unable to find type", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("Unable to find type");
|
||||
return;
|
||||
}
|
||||
|
||||
var getResourceMethod = resourceCache.GetType().GetMethod("ReloadResource", new[] { typeof(string) });
|
||||
DebugTools.Assert(getResourceMethod != null);
|
||||
var generic = getResourceMethod!.MakeGenericMethod(type);
|
||||
generic.Invoke(resourceCache, new object[] { args[0] });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,18 +436,18 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Gets the tile count of a grid";
|
||||
public string Help => "Usage: gridtc <gridId>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[0], out var id))
|
||||
{
|
||||
console.AddLine($"{args[0]} is not a valid integer.");
|
||||
return false;
|
||||
shell.WriteLine($"{args[0]} is not a valid integer.");
|
||||
return;
|
||||
}
|
||||
|
||||
var gridId = new GridId(int.Parse(args[0]));
|
||||
@@ -487,13 +455,11 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (mapManager.TryGetGrid(gridId, out var grid))
|
||||
{
|
||||
console.AddLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
|
||||
return false;
|
||||
shell.WriteLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
console.AddLine($"No grid exists with id {id}",Color.Red);
|
||||
return false;
|
||||
shell.WriteError($"No grid exists with id {id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,7 +470,7 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Dump GUI tree to /guidump.txt in user data.";
|
||||
public string Help => "guidump";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var root = IoCManager.Resolve<IUserInterfaceManager>().RootControl;
|
||||
var res = IoCManager.Resolve<IResourceManager>();
|
||||
@@ -515,7 +481,7 @@ namespace Robust.Client.Console.Commands
|
||||
_writeNode(root, 0, writer);
|
||||
}
|
||||
|
||||
return false;
|
||||
shell.WriteLine("Saved guidump");
|
||||
}
|
||||
|
||||
private static void _writeNode(Control control, int indents, TextWriter writer)
|
||||
@@ -575,9 +541,9 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Open a dummy UI testing window";
|
||||
public string Help => "uitest";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var window = new SS14Window { CustomMinimumSize = (500, 400)};
|
||||
var window = new SS14Window { MinSize = (500, 400)};
|
||||
var tabContainer = new TabContainer();
|
||||
window.Contents.AddChild(tabContainer);
|
||||
var scroll = new ScrollContainer();
|
||||
@@ -597,7 +563,7 @@ namespace Robust.Client.Console.Commands
|
||||
optionButton.OnItemSelected += eventArgs => optionButton.SelectId(eventArgs.Id);
|
||||
vBox.AddChild(optionButton);
|
||||
|
||||
var tree = new Tree { SizeFlagsVertical = Control.SizeFlags.FillExpand };
|
||||
var tree = new Tree { VerticalExpand = true };
|
||||
var root = tree.CreateItem();
|
||||
root.Text = "Honk!";
|
||||
var child = tree.CreateItem();
|
||||
@@ -634,7 +600,7 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
grid.AddChild(new Button
|
||||
{
|
||||
CustomMinimumSize = (50, 50),
|
||||
MinSize = (50, 50),
|
||||
Text = $"{x}, {y}"
|
||||
});
|
||||
}
|
||||
@@ -666,9 +632,30 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
});
|
||||
|
||||
window.OpenCentered();
|
||||
tabContainer.AddChild(new HSplitContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Red},
|
||||
Children =
|
||||
{
|
||||
new Label{ Text = "FOOBARBAZ"},
|
||||
}
|
||||
},
|
||||
new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Blue},
|
||||
Children =
|
||||
{
|
||||
new Label{ Text = "FOOBARBAZ"},
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
window.OpenCentered();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,11 +665,10 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Sets the system clipboard";
|
||||
public string Help => "setclipboard <text>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IClipboardManager>();
|
||||
mgr.SetText(args[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,11 +678,10 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Gets the system clipboard";
|
||||
public string Help => "getclipboard";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IClipboardManager>();
|
||||
console.AddLine(mgr.GetText());
|
||||
return false;
|
||||
shell.WriteLine(mgr.GetText());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,11 +691,11 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Toggles light rendering.";
|
||||
public string Help => "togglelight";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
mgr.Enabled = !mgr.Enabled;
|
||||
return false;
|
||||
if (!mgr.LockConsoleAccess)
|
||||
mgr.Enabled = !mgr.Enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,12 +705,11 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Toggles fov for client.";
|
||||
public string Help => "togglefov";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IEyeManager>();
|
||||
if (mgr.CurrentEye != null)
|
||||
mgr.CurrentEye.DrawFov = !mgr.CurrentEye.DrawFov;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,11 +719,11 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Toggles hard fov for client (for debugging space-station-14#2353).";
|
||||
public string Help => "togglehardfov";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
mgr.DrawHardFov = !mgr.DrawHardFov;
|
||||
return false;
|
||||
if (!mgr.LockConsoleAccess)
|
||||
mgr.DrawHardFov = !mgr.DrawHardFov;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -749,11 +733,24 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Toggles shadow rendering.";
|
||||
public string Help => "toggleshadows";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
mgr.DrawShadows = !mgr.DrawShadows;
|
||||
return false;
|
||||
if (!mgr.LockConsoleAccess)
|
||||
mgr.DrawShadows = !mgr.DrawShadows;
|
||||
}
|
||||
}
|
||||
internal class ToggleLightBuf : IConsoleCommand
|
||||
{
|
||||
public string Command => "togglelightbuf";
|
||||
public string Description => "Toggles lighting rendering. This includes shadows but not FOV.";
|
||||
public string Help => "togglelightbuf";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
if (!mgr.LockConsoleAccess)
|
||||
mgr.DrawLighting = !mgr.DrawLighting;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,7 +760,7 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Run the GC.";
|
||||
public string Help => "gc [generation]";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
@@ -774,10 +771,21 @@ namespace Robust.Client.Console.Commands
|
||||
if (int.TryParse(args[0], out int result))
|
||||
GC.Collect(result);
|
||||
else
|
||||
console.AddLine("Failed to parse argument.",Color.Red);
|
||||
shell.WriteError("Failed to parse argument.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
internal class GcFullCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "gcf";
|
||||
public string Description => "Run the GC, fully, compacting LOH and everything.";
|
||||
public string Help => "gcf";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
GC.Collect(2, GCCollectionMode.Forced, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,16 +798,16 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public string Help => "gc_mode\nSee current GC Latencymode\ngc_mode [type]\n Change GC Latency mode to [type]";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var prevMode = GCSettings.LatencyMode;
|
||||
if (args.Length == 0)
|
||||
{
|
||||
console.AddLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
|
||||
console.AddLine("possible modes:");
|
||||
shell.WriteLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
|
||||
shell.WriteLine("possible modes:");
|
||||
foreach (int mode in (int[]) Enum.GetValues(typeof(GCLatencyMode)))
|
||||
{
|
||||
console.AddLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
|
||||
shell.WriteLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -811,16 +819,14 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
else if (!Enum.TryParse(args[0], true, out mode))
|
||||
{
|
||||
console.AddLine($"unknown gc latency mode: {args[0]}");
|
||||
return false;
|
||||
shell.WriteLine($"unknown gc latency mode: {args[0]}");
|
||||
return;
|
||||
}
|
||||
|
||||
console.AddLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
|
||||
shell.WriteLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
|
||||
GCSettings.LatencyMode = mode;
|
||||
console.AddLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
|
||||
shell.WriteLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -834,15 +840,13 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public string Help => "szr_stats";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
|
||||
console.AddLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
|
||||
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
|
||||
console.AddLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
|
||||
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
|
||||
|
||||
return false;
|
||||
shell.WriteLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
|
||||
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
|
||||
shell.WriteLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
|
||||
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -853,7 +857,7 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Gets info about a chunk under your mouse cursor.";
|
||||
public string Help => Command;
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
var inputMan = IoCManager.Resolve<IInputManager>();
|
||||
@@ -863,8 +867,8 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (!mapMan.TryFindGridAt(mousePos, out var grid))
|
||||
{
|
||||
console.AddLine("No grid under your mouse cursor.");
|
||||
return false;
|
||||
shell.WriteLine("No grid under your mouse cursor.");
|
||||
return;
|
||||
}
|
||||
|
||||
var internalGrid = (IMapGridInternal)grid;
|
||||
@@ -872,8 +876,7 @@ namespace Robust.Client.Console.Commands
|
||||
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
|
||||
var chunk = internalGrid.GetChunk(chunkIndex);
|
||||
|
||||
console.AddLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
|
||||
return false;
|
||||
shell.WriteLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,7 +893,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public static ConcurrentDictionary<string, bool>? _reloadShadersQueued = new();
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IResourceCacheInternal resC;
|
||||
if (args.Length == 1)
|
||||
@@ -899,8 +902,8 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
if (_watchers != null)
|
||||
{
|
||||
console.AddLine("Already watching.");
|
||||
return false;
|
||||
shell.WriteLine("Already watching.");
|
||||
return;
|
||||
}
|
||||
resC = IoCManager.Resolve<IResourceCacheInternal>();
|
||||
|
||||
@@ -969,11 +972,11 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
IoCManager.Resolve<IResourceCache>()
|
||||
.ReloadResource<ShaderSourceResource>(resPath);
|
||||
console.AddLine($"Reloaded shader: {resPath}");
|
||||
shell.WriteLine($"Reloaded shader: {resPath}");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
console.AddLine($"Failed to reload shader: {resPath}");
|
||||
shell.WriteLine($"Failed to reload shader: {resPath}");
|
||||
}
|
||||
|
||||
_reloadShadersQueued.TryRemove(ev.FullPath, out var _);
|
||||
@@ -993,17 +996,17 @@ namespace Robust.Client.Console.Commands
|
||||
++created;
|
||||
}
|
||||
|
||||
console.AddLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
|
||||
shell.WriteLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
|
||||
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "-watch")
|
||||
{
|
||||
if (_watchers == null)
|
||||
{
|
||||
console.AddLine("No shader directory watchers active.");
|
||||
return false;
|
||||
shell.WriteLine("No shader directory watchers active.");
|
||||
return;
|
||||
}
|
||||
|
||||
var disposed = 0;
|
||||
@@ -1015,19 +1018,19 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
_watchers = null;
|
||||
|
||||
console.AddLine($"Disposed of {disposed} shader directory watchers.");
|
||||
shell.WriteLine($"Disposed of {disposed} shader directory watchers.");
|
||||
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Length > 1)
|
||||
{
|
||||
console.AddLine("Not implemented.");
|
||||
return false;
|
||||
shell.WriteLine("Not implemented.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.AddLine("Reloading content shader resources...");
|
||||
shell.WriteLine("Reloading content shader resources...");
|
||||
|
||||
resC = IoCManager.Resolve<IResourceCacheInternal>();
|
||||
|
||||
@@ -1039,13 +1042,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
console.AddLine($"Failed to reload shader: {path}");
|
||||
shell.WriteLine($"Failed to reload shader: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
console.AddLine("Done.");
|
||||
|
||||
return false;
|
||||
shell.WriteLine("Done.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1056,14 +1057,14 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Toggle fov and light debug layers";
|
||||
public string Help => "cldbglyr <layer>: Toggle <layer>\ncldbglyr: Turn all Layers off";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClydeInternal>();
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
clyde.DebugLayers = ClydeDebugLayers.None;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
clyde.DebugLayers = args[0] switch
|
||||
@@ -1072,8 +1073,6 @@ namespace Robust.Client.Console.Commands
|
||||
"light" => ClydeDebugLayers.Light,
|
||||
_ => ClydeDebugLayers.None
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,12 +1082,12 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Keys key info for a key";
|
||||
public string Help => "keyinfo <Key>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
console.AddLine(Help);
|
||||
return false;
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var clyde = IoCManager.Resolve<IClydeInternal>();
|
||||
@@ -1101,15 +1100,13 @@ namespace Robust.Client.Console.Commands
|
||||
var scanCode = clyde.GetKeyScanCode(key);
|
||||
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
|
||||
|
||||
console.AddLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
|
||||
shell.WriteLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
|
||||
}
|
||||
else if (int.TryParse(args[0], out var scanCode))
|
||||
{
|
||||
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
|
||||
console.AddLine($"name via scan code: '{nameScanCode}'");
|
||||
shell.WriteLine($"name via scan code: '{nameScanCode}'");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
@@ -12,23 +11,21 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Dumps a type's members in a format suitable for the sandbox configuration file.";
|
||||
public string Help => "Usage: dmetamem <type>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = Type.GetType(args[0]);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
console.AddLine("That type does not exist", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("That type does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var sig in AssemblyTypeChecker.DumpMetaMembers(type))
|
||||
{
|
||||
System.Console.WriteLine(@$"- ""{sig}""");
|
||||
console.AddLine(sig);
|
||||
shell.WriteLine(sig);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Linq;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
@@ -12,37 +11,36 @@ namespace Robust.Client.Console.Commands
|
||||
public string Help => "When no arguments are provided, displays a generic help text. When an argument is passed, display the help text for the command with that name.";
|
||||
public string Description => "Display help text.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
console.AddLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
|
||||
shell.WriteLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
string commandname = args[0];
|
||||
if (!console.Commands.ContainsKey(commandname))
|
||||
if (!shell.ConsoleHost.RegisteredCommands.ContainsKey(commandname))
|
||||
{
|
||||
if (!IoCManager.Resolve<IClientNetManager>().IsConnected)
|
||||
{
|
||||
// No server so nothing to respond with unknown command.
|
||||
console.AddLine("Unknown command: " + commandname, Color.Red);
|
||||
return false;
|
||||
shell.WriteError("Unknown command: " + commandname);
|
||||
return;
|
||||
}
|
||||
// TODO: Maybe have a server side help?
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
IConsoleCommand command = console.Commands[commandname];
|
||||
console.AddLine(string.Format("{0} - {1}", command.Command, command.Description));
|
||||
console.AddLine(command.Help);
|
||||
IConsoleCommand command = shell.ConsoleHost.RegisteredCommands[commandname];
|
||||
shell.WriteLine(string.Format("{0} - {1}", command.Command, command.Description));
|
||||
shell.WriteLine(command.Help);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.AddLine("Invalid amount of arguments.", Color.Red);
|
||||
shell.WriteError("Invalid amount of arguments.");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +53,7 @@ namespace Robust.Client.Console.Commands
|
||||
"only commands that contain the given string in their name will be listed.";
|
||||
public string Description => "List all commands, optionally with a filter.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var filter = "";
|
||||
if (args.Length == 1)
|
||||
@@ -64,14 +62,12 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
|
||||
var conGroup = IoCManager.Resolve<IClientConGroupController>();
|
||||
foreach (var command in console.Commands.Values
|
||||
foreach (var command in shell.ConsoleHost.RegisteredCommands.Values
|
||||
.Where(p => p.Command.Contains(filter) && conGroup.CanCommand(p.Command))
|
||||
.OrderBy(c => c.Command))
|
||||
{
|
||||
console.AddLine(command.Command + ": " + command.Description);
|
||||
shell.WriteLine(command.Command + ": " + command.Description);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
Robust.Client/Console/Commands/LauncherAuthCommand.cs
Normal file
67
Robust.Client/Console/Commands/LauncherAuthCommand.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#if !FULL_RELEASE
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class LauncherAuthCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "launchauth";
|
||||
public string Description => "Load authentication tokens from launcher data to aid in testing of live servers";
|
||||
public string Help => "launchauth [account name]";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var wantName = args.Length > 0 ? args[0] : null;
|
||||
|
||||
var basePath = Path.GetDirectoryName(UserDataDir.GetUserDataDir())!;
|
||||
var cfgPath = Path.Combine(basePath, "launcher", "launcher_config.json");
|
||||
|
||||
var data = JsonSerializer.Deserialize<LauncherConfig>(File.ReadAllText(cfgPath))!;
|
||||
|
||||
var login = wantName != null
|
||||
? data.Logins.FirstOrDefault(p => p.Username == wantName)
|
||||
: data.Logins.FirstOrDefault();
|
||||
|
||||
if (login == null)
|
||||
{
|
||||
shell.WriteLine("Unable to find a matching login");
|
||||
return;
|
||||
}
|
||||
|
||||
var token = login.Token.Token;
|
||||
var userId = login.UserId;
|
||||
|
||||
var cfg = IoCManager.Resolve<IAuthManager>();
|
||||
cfg.Token = token;
|
||||
cfg.UserId = new NetUserId(Guid.Parse(userId));
|
||||
}
|
||||
|
||||
private sealed class LauncherConfig
|
||||
{
|
||||
[JsonInclude] [JsonPropertyName("logins")]
|
||||
public LauncherLogin[] Logins = default!;
|
||||
}
|
||||
|
||||
private sealed class LauncherLogin
|
||||
{
|
||||
[JsonInclude] public string Username = default!;
|
||||
[JsonInclude] public string UserId = default!;
|
||||
[JsonInclude] public LauncherToken Token = default!;
|
||||
}
|
||||
|
||||
private sealed class LauncherToken
|
||||
{
|
||||
[JsonInclude] public string Token = default!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
@@ -13,18 +12,16 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Lists loaded assemblies by load context.";
|
||||
public string Help => Command;
|
||||
|
||||
public bool Execute(IDebugConsole console, string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
foreach (var context in AssemblyLoadContext.All)
|
||||
{
|
||||
console.AddLine($"{context.Name}:");
|
||||
shell.WriteLine($"{context.Name}:");
|
||||
foreach (var assembly in context.Assemblies.OrderBy(a => a.FullName))
|
||||
{
|
||||
console.AddLine($" {assembly.FullName}");
|
||||
shell.WriteLine($" {assembly.FullName}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Log;
|
||||
using System;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
@@ -13,12 +12,12 @@ namespace Robust.Client.Console.Commands
|
||||
+ "\n sawmill: A label prefixing log messages. This is the one you're setting the level for."
|
||||
+ "\n level: The log level. Must match one of the values of the LogLevel enum.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args[0];
|
||||
@@ -32,13 +31,12 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
if (!Enum.TryParse<LogLevel>(levelname, out var result))
|
||||
{
|
||||
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
|
||||
return false;
|
||||
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
|
||||
return;
|
||||
}
|
||||
level = result;
|
||||
}
|
||||
Logger.GetSawmill(name).Level = level;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,12 +49,12 @@ namespace Robust.Client.Console.Commands
|
||||
+ "\n level: The log level. Must match one of the values of the LogLevel enum."
|
||||
+ "\n message: The message to be logged. Wrap this in double quotes if you want to use spaces.";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 3)
|
||||
{
|
||||
console.AddLine("Invalid argument amount. Expected 3 arguments.", Color.Red);
|
||||
return false;
|
||||
shell.WriteError("Invalid argument amount. Expected 3 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args[0];
|
||||
@@ -64,13 +62,12 @@ namespace Robust.Client.Console.Commands
|
||||
var message = args[2]; // yes this doesn't support spaces idgaf.
|
||||
if (!Enum.TryParse<LogLevel>(levelname, out var result))
|
||||
{
|
||||
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
|
||||
return false;
|
||||
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
|
||||
return;
|
||||
}
|
||||
var level = result;
|
||||
|
||||
Logger.LogS(level, name, message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
Robust.Client/Console/Commands/MonitorCommands.cs
Normal file
44
Robust.Client/Console/Commands/MonitorCommands.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class LsMonitorCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "lsmonitor";
|
||||
public string Description => "";
|
||||
public string Help => "";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
foreach (var monitor in clyde.EnumerateMonitors())
|
||||
{
|
||||
shell.WriteLine(
|
||||
$"[{monitor.Id}] {monitor.Name}: {monitor.Size.X}x{monitor.Size.Y}@{monitor.RefreshRate}Hz");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class SetMonitorCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "setmonitor";
|
||||
public string Description => "";
|
||||
public string Help => "Usage: setmonitor <id>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
var id = int.Parse(args[0]);
|
||||
var monitor = clyde.EnumerateMonitors().Single(m => m.Id == id);
|
||||
clyde.SetWindowMonitor(monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Robust.Client/Console/Commands/PhysicsOverlayCommands.cs
Normal file
41
Robust.Client/Console/Commands/PhysicsOverlayCommands.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class PhysicsOverlayCommands : IConsoleCommand
|
||||
{
|
||||
public string Command => "physics";
|
||||
public string Description => $"{Command} <contactnormals / contactpoints / shapes>";
|
||||
public string Help => $"{Command} <overlay>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine($"Invalid number of args supplied");
|
||||
return;
|
||||
}
|
||||
|
||||
var system = EntitySystem.Get<DebugPhysicsSystem>();
|
||||
|
||||
switch (args[0])
|
||||
{
|
||||
case "contactnormals":
|
||||
system.Flags ^= PhysicsDebugFlags.ContactNormals;
|
||||
break;
|
||||
case "contactpoints":
|
||||
system.Flags ^= PhysicsDebugFlags.ContactPoints;
|
||||
break;
|
||||
case "shapes":
|
||||
system.Flags ^= PhysicsDebugFlags.Shapes;
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine($"{args[0]} is not a recognised overlay");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using System;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
@@ -9,10 +9,9 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Kills the game client instantly.";
|
||||
public string Help => "Kills the game client instantly, leaving no traces. No telling the server goodbye";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
Robust.Client/Console/Commands/ReloadLocalizationsCommand.cs
Normal file
20
Robust.Client/Console/Commands/ReloadLocalizationsCommand.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class ReloadLocalizationsCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "rldloc";
|
||||
public string Description => "Reloads localization (client & server)";
|
||||
public string Help => "Usage: rldloc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<ILocalizationManager>().ReloadLocalizations();
|
||||
|
||||
shell.RemoteExecuteCommand("sudo rldloc");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
#if CLIENT_SCRIPTING
|
||||
internal sealed class ScriptConsoleCommand : IConsoleCommand
|
||||
internal sealed class ScriptCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "csi";
|
||||
public string Description => "Opens a C# interactive console.";
|
||||
public string Help => "csi";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
new ScriptConsoleClient().OpenCentered();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,33 +23,29 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Opens a variable watch window.";
|
||||
public string Help => "watch";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
new WatchWindow().OpenCentered();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal sealed class ServerScriptConsoleCommand : IConsoleCommand
|
||||
internal sealed class ServerScriptCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "scsi";
|
||||
public string Description => "Opens a C# interactive console on the server.";
|
||||
public string Help => "scsi";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IScriptClient>();
|
||||
if (!mgr.CanScript)
|
||||
{
|
||||
console.AddLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
|
||||
return false;
|
||||
shell.WriteError(Loc.GetString("You do not have server side scripting permission."));
|
||||
return;
|
||||
}
|
||||
|
||||
mgr.StartSession();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
@@ -11,7 +10,7 @@ namespace Robust.Client.Console.Commands
|
||||
public string Description => "Sends garbage to the server.";
|
||||
public string Help => "The server will reply with 'no u'";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
// MsgStringTableEntries is registered as NetMessageAccept.Client so the server will immediately deny it.
|
||||
// And kick us.
|
||||
@@ -19,8 +18,6 @@ namespace Robust.Client.Console.Commands
|
||||
var msg = net.CreateNetMessage<MsgStringTableEntries>();
|
||||
msg.Entries = new MsgStringTableEntries.Entry[0];
|
||||
net.ClientSendMessage(msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
Robust.Client/Console/Commands/SetInputContextCommand.cs
Normal file
34
Robust.Client/Console/Commands/SetInputContextCommand.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class SetInputContextCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "setinputcontext";
|
||||
public string Description => "Sets the active input context.";
|
||||
public string Help => "setinputcontext <context>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine("Invalid number of arguments!");
|
||||
return;
|
||||
}
|
||||
|
||||
var inputMan = IoCManager.Resolve<IInputManager>();
|
||||
|
||||
if (!inputMan.Contexts.Exists(args[0]))
|
||||
{
|
||||
shell.WriteLine("Context not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
inputMan.Contexts.SetActiveContext(args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Robust.Client/Console/Commands/VelocitiesCommand.cs
Normal file
17
Robust.Client/Console/Commands/VelocitiesCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public class VelocitiesCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showvelocities";
|
||||
public string Description => "Displays your angular and linear velocities";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<VelocityDebugSystem>().Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public interface IClientConGroupController : IClientConGroupImplementation
|
||||
{
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public interface IClientConsole : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the console into a useable state.
|
||||
/// </summary>
|
||||
void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the console to a post-initialized state.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
|
||||
event EventHandler<AddStringArgs> AddString;
|
||||
event EventHandler<AddFormattedMessageArgs> AddFormatted;
|
||||
event EventHandler ClearText;
|
||||
|
||||
IReadOnlyDictionary<string, IConsoleCommand> Commands { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parses console commands (verbs).
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
void ProcessCommand(string text);
|
||||
|
||||
void SendServerCommandRequest();
|
||||
}
|
||||
}
|
||||
24
Robust.Client/Console/IClientConsoleHost.cs
Normal file
24
Robust.Client/Console/IClientConsoleHost.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public interface IClientConsoleHost : IConsoleHost, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the console into a useable state.
|
||||
/// </summary>
|
||||
void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the console to a post-initialized state.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
event EventHandler<AddStringArgs> AddString;
|
||||
event EventHandler<AddFormattedMessageArgs> AddFormatted;
|
||||
|
||||
void AddFormattedLine(FormattedMessage message);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#if CLIENT_SCRIPTING
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.CodeAnalysis;
|
||||
@@ -10,10 +13,10 @@ using Microsoft.CodeAnalysis.Scripting;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Scripting;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -36,6 +39,8 @@ namespace Robust.Client.Console
|
||||
private readonly ScriptGlobals _globals;
|
||||
private ScriptState? _state;
|
||||
|
||||
private (string[] imports, string code)? _autoImportRepeatBuffer;
|
||||
|
||||
public ScriptConsoleClient()
|
||||
{
|
||||
Title = Loc.GetString("Robust C# Interactive (CLIENT)");
|
||||
@@ -54,38 +59,56 @@ namespace Robust.Client.Console
|
||||
var code = InputBar.Text;
|
||||
InputBar.Clear();
|
||||
|
||||
// Remove > or . at the end of the output panel.
|
||||
OutputPanel.RemoveEntry(^1);
|
||||
|
||||
_inputBuffer.AppendLine(code);
|
||||
_linesEntered += 1;
|
||||
|
||||
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(_inputBuffer.ToString()), ScriptInstanceShared.ParseOptions);
|
||||
|
||||
if (!SyntaxFactory.IsCompleteSubmission(tree))
|
||||
if (_autoImportRepeatBuffer.HasValue && code == "y")
|
||||
{
|
||||
if (_linesEntered == 1)
|
||||
var (imports, repeatCode) = _autoImportRepeatBuffer.Value;
|
||||
var sb = new StringBuilder();
|
||||
foreach (var import in imports)
|
||||
{
|
||||
OutputPanel.AddText($"> {code}");
|
||||
sb.AppendFormat("using {0};\n", import);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputPanel.AddText($". {code}");
|
||||
}
|
||||
OutputPanel.AddText(".");
|
||||
return;
|
||||
|
||||
sb.Append(repeatCode);
|
||||
|
||||
code = sb.ToString();
|
||||
}
|
||||
|
||||
code = _inputBuffer.ToString().Trim();
|
||||
|
||||
// Remove echo of partial submission from the output panel.
|
||||
for (var i = 1; i < _linesEntered; i++)
|
||||
else
|
||||
{
|
||||
// Remove > or . at the end of the output panel.
|
||||
OutputPanel.RemoveEntry(^1);
|
||||
}
|
||||
|
||||
_inputBuffer.Clear();
|
||||
_linesEntered = 0;
|
||||
_inputBuffer.AppendLine(code);
|
||||
_linesEntered += 1;
|
||||
|
||||
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(_inputBuffer.ToString()),
|
||||
ScriptInstanceShared.ParseOptions);
|
||||
|
||||
if (!SyntaxFactory.IsCompleteSubmission(tree))
|
||||
{
|
||||
if (_linesEntered == 1)
|
||||
{
|
||||
OutputPanel.AddText($"> {code}");
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputPanel.AddText($". {code}");
|
||||
}
|
||||
|
||||
OutputPanel.AddText(".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = _inputBuffer.ToString().Trim();
|
||||
|
||||
// Remove echo of partial submission from the output panel.
|
||||
for (var i = 1; i < _linesEntered; i++)
|
||||
{
|
||||
OutputPanel.RemoveEntry(^1);
|
||||
}
|
||||
|
||||
_inputBuffer.Clear();
|
||||
_linesEntered = 0;
|
||||
}
|
||||
|
||||
Script newScript;
|
||||
|
||||
@@ -135,6 +158,8 @@ namespace Robust.Client.Console
|
||||
|
||||
OutputPanel.AddMessage(msg);
|
||||
OutputPanel.AddText(">");
|
||||
|
||||
PromptAutoImports(e.Diagnostics, code);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,13 +173,23 @@ namespace Robust.Client.Console
|
||||
else if (ScriptInstanceShared.HasReturnValue(newScript))
|
||||
{
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(CSharpObjectFormatter.Instance.FormatObject(_state.ReturnValue));
|
||||
msg.AddText(ScriptInstanceShared.SafeFormat(_state.ReturnValue));
|
||||
OutputPanel.AddMessage(msg);
|
||||
}
|
||||
|
||||
OutputPanel.AddText(">");
|
||||
}
|
||||
|
||||
private void PromptAutoImports(IEnumerable<Diagnostic> diags, string code)
|
||||
{
|
||||
if (!ScriptInstanceShared.CalcAutoImports(_reflectionManager, diags, out var found))
|
||||
return;
|
||||
|
||||
OutputPanel.AddText($"Auto-import {string.Join(", ", found)} (enter 'y')?");
|
||||
|
||||
_autoImportRepeatBuffer = (found.ToArray(), code);
|
||||
}
|
||||
|
||||
private sealed class ScriptGlobalsImpl : ScriptGlobals
|
||||
{
|
||||
private readonly ScriptConsoleClient _owner;
|
||||
@@ -180,7 +215,7 @@ namespace Robust.Client.Console
|
||||
|
||||
public override void show(object obj)
|
||||
{
|
||||
write(CSharpObjectFormatter.Instance.FormatObject(obj));
|
||||
write(ScriptInstanceShared.SafeFormat(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ using Microsoft.CodeAnalysis.Scripting;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Scripting;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace Robust.Client.Console
|
||||
{
|
||||
private readonly IReflectionManager _reflectionManager;
|
||||
|
||||
protected override Vector2? CustomSize => (300, 300);
|
||||
|
||||
private readonly VBoxContainer _watchesVBox;
|
||||
private readonly LineEdit _addWatchEdit;
|
||||
private readonly Button _addWatchButton;
|
||||
@@ -37,12 +35,12 @@ namespace Robust.Client.Console
|
||||
|
||||
var mainVBox = new VBoxContainer
|
||||
{
|
||||
CustomMinimumSize = (500, 300),
|
||||
MinSize = (500, 300),
|
||||
Children =
|
||||
{
|
||||
(_watchesVBox = new VBoxContainer
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand
|
||||
VerticalExpand = true
|
||||
}),
|
||||
new HBoxContainer
|
||||
{
|
||||
@@ -50,7 +48,7 @@ namespace Robust.Client.Console
|
||||
{
|
||||
(_addWatchEdit = new HistoryLineEdit
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
HorizontalExpand = true,
|
||||
PlaceHolder = Loc.GetString("Add watch (C# interactive)")
|
||||
}),
|
||||
(_addWatchButton = new Button
|
||||
@@ -66,6 +64,8 @@ namespace Robust.Client.Console
|
||||
_addWatchEdit.OnTextEntered += _ => AddWatch();
|
||||
|
||||
Contents.AddChild(mainVBox);
|
||||
|
||||
SetSize = (300, 300);
|
||||
}
|
||||
|
||||
private void AddWatch()
|
||||
@@ -113,7 +113,7 @@ namespace Robust.Client.Console
|
||||
{
|
||||
(_outputLabel = new Label
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
HorizontalExpand = true,
|
||||
ClipText = true
|
||||
}),
|
||||
(delButton = new Button
|
||||
@@ -176,7 +176,7 @@ namespace Robust.Client.Console
|
||||
{
|
||||
Text = message,
|
||||
ClipText = true,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||
HorizontalExpand = true
|
||||
},
|
||||
(delButton = new Button {Text = Loc.GetString("Remove")})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Graphics.Shaders;
|
||||
using Robust.Client.Interfaces.Debugging;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Physics.Dynamics.Joints;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
@@ -23,10 +21,10 @@ namespace Robust.Client.Debugging
|
||||
public class DebugDrawing : IDebugDrawing
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
private bool _debugColliders;
|
||||
@@ -45,14 +43,14 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugColliders = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<PhysicsOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new PhysicsOverlay(_componentManager, _eyeManager,
|
||||
_prototypeManager, _inputManager));
|
||||
_overlayManager.AddOverlay(new PhysicsOverlay(_eyeManager,
|
||||
_prototypeManager, _inputManager, _mapManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(PhysicsOverlay));
|
||||
_overlayManager.RemoveOverlay<PhysicsOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,21 +68,21 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugPositions = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_entityManager, _eyeManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(EntityPositionOverlay));
|
||||
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PhysicsOverlay : Overlay
|
||||
{
|
||||
private readonly IComponentManager _componentManager;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly IInputManager _inputManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
|
||||
@@ -94,12 +92,12 @@ namespace Robust.Client.Debugging
|
||||
private Vector2 _hoverStartScreen = Vector2.Zero;
|
||||
private List<IPhysBody> _hoverBodies = new();
|
||||
|
||||
public PhysicsOverlay(IComponentManager compMan, IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager)
|
||||
: base(nameof(PhysicsOverlay))
|
||||
|
||||
public PhysicsOverlay(IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager, IMapManager mapManager)
|
||||
{
|
||||
_componentManager = compMan;
|
||||
_eyeManager = eyeMan;
|
||||
_inputManager = inputManager;
|
||||
_mapManager = mapManager;
|
||||
|
||||
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
||||
var cache = IoCManager.Resolve<IResourceCache>();
|
||||
@@ -135,13 +133,13 @@ namespace Robust.Client.Debugging
|
||||
row++;
|
||||
}
|
||||
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {body.Entity}");
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Ent: {body.Owner}");
|
||||
row++;
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Layer: {Convert.ToString(body.CollisionLayer, 2)}");
|
||||
row++;
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Mask: {Convert.ToString(body.CollisionMask, 2)}");
|
||||
row++;
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Enabled: {body.CanCollide}, Hard: {body.Hard}, Anchored: {((IPhysicsComponent)body).Anchored}");
|
||||
DrawString(screenHandle, _font, drawPos + new Vector2(0, row * lineHeight), $"Enabled: {body.CanCollide}, Hard: {body.Hard}, Anchored: {(body).BodyType == BodyType.Static}");
|
||||
row++;
|
||||
}
|
||||
|
||||
@@ -158,27 +156,35 @@ namespace Robust.Client.Debugging
|
||||
_hoverStartScreen = mouseScreenPos;
|
||||
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
foreach (var boundingBox in _componentManager.EntityQuery<IPhysicsComponent>())
|
||||
|
||||
if (viewport.IsEmpty()) return;
|
||||
|
||||
var mapId = _eyeManager.CurrentMap;
|
||||
var sleepThreshold = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.TimeToSleep);
|
||||
var colorEdge = Color.Red.WithAlpha(0.33f);
|
||||
var drawnJoints = new HashSet<Joint>();
|
||||
|
||||
foreach (var physBody in EntitySystem.Get<SharedBroadPhaseSystem>().GetCollidingEntities(mapId, viewport))
|
||||
{
|
||||
var physBody = (IPhysBody) boundingBox;
|
||||
|
||||
// all entities have a TransformComponent
|
||||
var transform = physBody.Entity.Transform;
|
||||
var transform = physBody.Owner.Transform;
|
||||
|
||||
// if not on the same map, continue
|
||||
if (transform.MapID != _eyeManager.CurrentMap || !transform.IsMapTransform)
|
||||
continue;
|
||||
var worldBox = physBody.GetWorldAABB(_mapManager);
|
||||
if (worldBox.IsEmpty()) continue;
|
||||
|
||||
var worldBox = physBody.WorldAABB;
|
||||
var colorEdge = Color.Red.WithAlpha(0.33f);
|
||||
|
||||
// if not on screen, or too small, continue
|
||||
if (!worldBox.Intersects(in viewport) || worldBox.IsEmpty())
|
||||
continue;
|
||||
|
||||
foreach (var shape in physBody.PhysicsShapes)
|
||||
foreach (var fixture in physBody.Fixtures)
|
||||
{
|
||||
shape.DebugDraw(drawing, transform.WorldMatrix, in viewport, physBody.SleepAccumulator / (float) physBody.SleepThreshold);
|
||||
var shape = fixture.Shape;
|
||||
var sleepPercent = physBody.Awake ? physBody.SleepTime / sleepThreshold : 1.0f;
|
||||
shape.DebugDraw(drawing, transform.WorldMatrix, in viewport, sleepPercent);
|
||||
}
|
||||
|
||||
foreach (var joint in physBody.Joints)
|
||||
{
|
||||
if (drawnJoints.Contains(joint)) continue;
|
||||
drawnJoints.Add(joint);
|
||||
|
||||
joint.DebugDraw(drawing, in viewport);
|
||||
}
|
||||
|
||||
if (worldBox.Contains(mouseWorldPos))
|
||||
@@ -195,9 +201,9 @@ namespace Robust.Client.Debugging
|
||||
{
|
||||
var baseLine = new Vector2(pos.X, font.GetAscent(1) + pos.Y);
|
||||
|
||||
foreach (var chr in str)
|
||||
foreach (var rune in str.EnumerateRunes())
|
||||
{
|
||||
var advance = font.DrawChar(handle, chr, baseLine, 1, Color.White);
|
||||
var advance = font.DrawChar(handle, rune, baseLine, 1, Color.White);
|
||||
baseLine += new Vector2(advance, 0);
|
||||
}
|
||||
}
|
||||
@@ -241,6 +247,16 @@ namespace Robust.Client.Debugging
|
||||
_handle.DrawCircle(origin, radius, color);
|
||||
}
|
||||
|
||||
public override void DrawPolygonShape(Vector2[] vertices, in Color color)
|
||||
{
|
||||
_handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, vertices, color);
|
||||
}
|
||||
|
||||
public override void DrawLine(Vector2 start, Vector2 end, in Color color)
|
||||
{
|
||||
_handle.DrawLine(start, end, color);
|
||||
}
|
||||
|
||||
public override void SetTransform(in Matrix3 transform)
|
||||
{
|
||||
_handle.SetTransform(transform);
|
||||
@@ -257,7 +273,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager) : base(nameof(EntityPositionOverlay))
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
using Robust.Client.Interfaces.Debugging;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
@@ -42,13 +39,13 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugDrawRays = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<DebugDrawRayOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new DebugDrawRayOverlay(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(DebugDrawRayOverlay));
|
||||
_overlayManager.RemoveOverlay<DebugDrawRayOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +82,7 @@ namespace Robust.Client.Debugging
|
||||
private readonly DebugDrawingManager _owner;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public DebugDrawRayOverlay(DebugDrawingManager owner) : base(nameof(DebugDrawRayOverlay))
|
||||
public DebugDrawRayOverlay(DebugDrawingManager owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
164
Robust.Client/Debugging/DebugPhysicsSystem.cs
Normal file
164
Robust.Client/Debugging/DebugPhysicsSystem.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Farseer Physics Engine:
|
||||
* Copyright (c) 2012 Ian Qvist
|
||||
*
|
||||
* Original source Box2D:
|
||||
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* Heavily inspired by Farseer */
|
||||
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Dynamics.Contacts;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
internal sealed class DebugPhysicsSystem : SharedDebugPhysicsSystem
|
||||
{
|
||||
/*
|
||||
* Used for debugging shapes, controllers, joints, contacts
|
||||
*/
|
||||
|
||||
private const int MaxContactPoints = 2048;
|
||||
internal int PointCount;
|
||||
|
||||
internal ContactPoint[] _points = new ContactPoint[MaxContactPoints];
|
||||
|
||||
public PhysicsDebugFlags Flags
|
||||
{
|
||||
get => _flags;
|
||||
set
|
||||
{
|
||||
if (value == _flags) return;
|
||||
|
||||
if (_flags == PhysicsDebugFlags.None)
|
||||
IoCManager.Resolve<IOverlayManager>().AddOverlay(new PhysicsDebugOverlay(this));
|
||||
|
||||
if (value == PhysicsDebugFlags.None)
|
||||
IoCManager.Resolve<IOverlayManager>().RemoveOverlay(typeof(PhysicsDebugOverlay));
|
||||
|
||||
_flags = value;
|
||||
}
|
||||
}
|
||||
|
||||
private PhysicsDebugFlags _flags;
|
||||
|
||||
public override void HandlePreSolve(Contact contact, in Manifold oldManifold)
|
||||
{
|
||||
if ((Flags & PhysicsDebugFlags.ContactPoints) != 0)
|
||||
{
|
||||
Manifold manifold = contact.Manifold;
|
||||
|
||||
if (manifold.PointCount == 0)
|
||||
return;
|
||||
|
||||
Fixture fixtureA = contact.FixtureA!;
|
||||
|
||||
PointState[] state1, state2;
|
||||
CollisionManager.GetPointStates(out state1, out state2, oldManifold, manifold);
|
||||
|
||||
Span<Vector2> points = stackalloc Vector2[2];
|
||||
Vector2 normal;
|
||||
contact.GetWorldManifold(out normal, points);
|
||||
|
||||
for (int i = 0; i < manifold.PointCount && PointCount < MaxContactPoints; ++i)
|
||||
{
|
||||
if (fixtureA == null)
|
||||
_points[i] = new ContactPoint();
|
||||
|
||||
ContactPoint cp = _points[PointCount];
|
||||
cp.Position = points[i];
|
||||
cp.Normal = normal;
|
||||
cp.State = state2[i];
|
||||
_points[PointCount] = cp;
|
||||
++PointCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ContactPoint
|
||||
{
|
||||
public Vector2 Normal;
|
||||
public Vector2 Position;
|
||||
public PointState State;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum PhysicsDebugFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
ContactPoints = 1 << 0,
|
||||
ContactNormals = 1 << 1,
|
||||
Shapes = 1 << 2,
|
||||
}
|
||||
|
||||
internal sealed class PhysicsDebugOverlay : Overlay
|
||||
{
|
||||
private DebugPhysicsSystem _physics = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public PhysicsDebugOverlay(DebugPhysicsSystem system)
|
||||
{
|
||||
_physics = system;
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
|
||||
{
|
||||
if (_physics.Flags == PhysicsDebugFlags.None) return;
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) handle;
|
||||
|
||||
if ((_physics.Flags & PhysicsDebugFlags.Shapes) != 0)
|
||||
{
|
||||
// Port DebugDrawing over.
|
||||
}
|
||||
|
||||
if ((_physics.Flags & PhysicsDebugFlags.ContactPoints) != 0)
|
||||
{
|
||||
const float axisScale = 0.3f;
|
||||
|
||||
for (int i = 0; i < _physics.PointCount; ++i)
|
||||
{
|
||||
DebugPhysicsSystem.ContactPoint point = _physics._points[i];
|
||||
|
||||
if (point.State == PointState.Add)
|
||||
worldHandle.DrawCircle(point.Position, 0.5f, new Color(255, 77, 243, 77));
|
||||
else if (point.State == PointState.Persist)
|
||||
worldHandle.DrawCircle(point.Position, 0.5f, new Color(255, 77, 77, 77));
|
||||
|
||||
if ((_physics.Flags & PhysicsDebugFlags.ContactNormals) != 0)
|
||||
{
|
||||
Vector2 p1 = point.Position;
|
||||
Vector2 p2 = p1 + point.Normal * axisScale;
|
||||
worldHandle.DrawLine(p1, p2, new Color(255, 102, 230, 102));
|
||||
}
|
||||
}
|
||||
|
||||
_physics.PointCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Robust.Client.Interfaces.Debugging
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of visual debug overlays for the client game.
|
||||
@@ -1,11 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Interfaces.Debugging
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
public interface IDebugDrawingManager
|
||||
{
|
||||
@@ -1,20 +1,19 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Management;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Audio.Midi;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.GameObjects;
|
||||
using Robust.Client.Interfaces.GameStates;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.Placement;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Interfaces.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.LoaderApi;
|
||||
@@ -22,17 +21,14 @@ using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Interfaces.Timers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -50,7 +46,7 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IClientConsole _console = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly ITimerManager _timerManager = default!;
|
||||
[Dependency] private readonly IClientEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlacementManager _placementManager = default!;
|
||||
@@ -67,6 +63,8 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IRobustMappedStringSerializer _stringSerializer = default!;
|
||||
[Dependency] private readonly IAuthManager _authManager = default!;
|
||||
[Dependency] private readonly IMidiManager _midiManager = default!;
|
||||
|
||||
private CommandLineArgs? _commandLineArgs;
|
||||
private bool _disableAssemblyLoadContext;
|
||||
@@ -83,6 +81,75 @@ namespace Robust.Client
|
||||
}
|
||||
|
||||
public bool Startup(Func<ILogHandler>? logHandlerFactory = null)
|
||||
{
|
||||
if (!StartupSystemSplash(logHandlerFactory))
|
||||
return false;
|
||||
|
||||
// Disable load context usage on content start.
|
||||
// This prevents Content.Client being loaded twice and things like csi blowing up because of it.
|
||||
_modLoader.SetUseLoadContext(!_disableAssemblyLoadContext);
|
||||
_modLoader.SetEnableSandboxing(true);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), "Content."))
|
||||
{
|
||||
Logger.Fatal("Errors while loading content assemblies.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var loadedModule in _modLoader.LoadedModules)
|
||||
{
|
||||
_configurationManager.LoadCVarsFromAssembly(loadedModule);
|
||||
}
|
||||
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
|
||||
// Call Init in game assemblies.
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PreInit);
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.Init);
|
||||
|
||||
_resourceCache.PreloadTextures();
|
||||
_userInterfaceManager.Initialize();
|
||||
_networkManager.Initialize(false);
|
||||
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
|
||||
_serializer.Initialize();
|
||||
_inputManager.Initialize();
|
||||
_console.Initialize();
|
||||
_prototypeManager.Initialize();
|
||||
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
|
||||
_prototypeManager.Resync();
|
||||
_mapManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
IoCManager.Resolve<IEntityLookup>().Initialize();
|
||||
_gameStateManager.Initialize();
|
||||
_placementManager.Initialize();
|
||||
_viewVariablesManager.Initialize();
|
||||
_scriptClient.Initialize();
|
||||
|
||||
_client.Initialize();
|
||||
_discord.Initialize();
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);
|
||||
|
||||
if (_commandLineArgs?.Username != null)
|
||||
{
|
||||
_client.PlayerNameOverride = _commandLineArgs.Username;
|
||||
}
|
||||
|
||||
_authManager.LoadFromEnv();
|
||||
|
||||
GC.Collect();
|
||||
|
||||
_clyde.Ready();
|
||||
|
||||
if ((_commandLineArgs?.Connect == true || _commandLineArgs?.Launcher == true)
|
||||
&& LaunchState.ConnectEndpoint != null)
|
||||
{
|
||||
_client.ConnectToServer(LaunchState.ConnectEndpoint);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool StartupSystemSplash(Func<ILogHandler>? logHandlerFactory)
|
||||
{
|
||||
ReadInitialLaunchState();
|
||||
|
||||
@@ -122,6 +189,8 @@ namespace Robust.Client
|
||||
_configurationManager.OverrideConVars(_commandLineArgs.CVars);
|
||||
}
|
||||
|
||||
ProfileOptSetup.Setup(_configurationManager);
|
||||
|
||||
_resourceCache.Initialize(LoadConfigAndUserData ? userDataDir : null);
|
||||
|
||||
ProgramShared.DoMounts(_resourceCache, _commandLineArgs?.MountOptions, "Content.Client", _loaderArgs != null);
|
||||
@@ -132,6 +201,13 @@ namespace Robust.Client
|
||||
_modLoader.VerifierExtraLoadHandler = VerifierExtraLoadHandler;
|
||||
}
|
||||
|
||||
_clyde.TextEntered += TextEntered;
|
||||
_clyde.MouseMove += MouseMove;
|
||||
_clyde.KeyUp += KeyUp;
|
||||
_clyde.KeyDown += KeyDown;
|
||||
_clyde.MouseWheel += MouseWheel;
|
||||
_clyde.CloseWindow += Shutdown;
|
||||
|
||||
// Bring display up as soon as resources are mounted.
|
||||
if (!_clyde.Initialize())
|
||||
{
|
||||
@@ -140,59 +216,7 @@ namespace Robust.Client
|
||||
|
||||
_clyde.SetWindowTitle("Space Station 14");
|
||||
|
||||
_fontManager.Initialize();
|
||||
|
||||
// Disable load context usage on content start.
|
||||
// This prevents Content.Client being loaded twice and things like csi blowing up because of it.
|
||||
_modLoader.SetUseLoadContext(!_disableAssemblyLoadContext);
|
||||
_modLoader.SetEnableSandboxing(true);
|
||||
|
||||
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), "Content."))
|
||||
{
|
||||
Logger.Fatal("Errors while loading content assemblies.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var loadedModule in _modLoader.LoadedModules)
|
||||
{
|
||||
_configurationManager.LoadCVarsFromAssembly(loadedModule);
|
||||
}
|
||||
|
||||
// Call Init in game assemblies.
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PreInit);
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.Init);
|
||||
|
||||
_userInterfaceManager.Initialize();
|
||||
_networkManager.Initialize(false);
|
||||
_serializer.Initialize();
|
||||
_inputManager.Initialize();
|
||||
_console.Initialize();
|
||||
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
|
||||
_prototypeManager.Resync();
|
||||
_mapManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
_gameStateManager.Initialize();
|
||||
_placementManager.Initialize();
|
||||
_viewVariablesManager.Initialize();
|
||||
_scriptClient.Initialize();
|
||||
|
||||
_client.Initialize();
|
||||
_discord.Initialize();
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);
|
||||
|
||||
if (_commandLineArgs?.Username != null)
|
||||
{
|
||||
_client.PlayerNameOverride = _commandLineArgs.Username;
|
||||
}
|
||||
|
||||
_clyde.Ready();
|
||||
|
||||
if ((_commandLineArgs?.Connect == true || _commandLineArgs?.Launcher == true)
|
||||
&& LaunchState.ConnectEndpoint != null)
|
||||
{
|
||||
_client.ConnectToServer(LaunchState.ConnectEndpoint);
|
||||
}
|
||||
|
||||
_fontManager.SetFontDpi((uint) _configurationManager.GetCVar(CVars.DisplayFontDpi));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -269,17 +293,13 @@ namespace Robust.Client
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.PreEngine, frameEventArgs);
|
||||
_timerManager.UpdateTimers(frameEventArgs);
|
||||
_taskManager.ProcessPendingTasks();
|
||||
_userInterfaceManager.Update(frameEventArgs);
|
||||
|
||||
// GameStateManager is in full control of the simulation update.
|
||||
if (_client.RunLevel >= ClientRunLevel.Connected)
|
||||
{
|
||||
_componentManager.CullRemovedComponents();
|
||||
_gameStateManager.ApplyGameState();
|
||||
_entityManager.Update(frameEventArgs.DeltaSeconds);
|
||||
_playerManager.Update(frameEventArgs.DeltaSeconds);
|
||||
}
|
||||
|
||||
_stateManager.Update(frameEventArgs);
|
||||
_modLoader.BroadcastUpdate(ModUpdateLevel.PostEngine, frameEventArgs);
|
||||
}
|
||||
|
||||
@@ -320,6 +340,7 @@ namespace Robust.Client
|
||||
logManager.GetSawmill("discord").Level = LogLevel.Warning;
|
||||
logManager.GetSawmill("net.predict").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("szr").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("loc").Level = LogLevel.Error;
|
||||
|
||||
#if DEBUG_ONLY_FCE_INFO
|
||||
#if DEBUG_ONLY_FCE_LOG
|
||||
@@ -378,6 +399,9 @@ namespace Robust.Client
|
||||
|
||||
private void Cleanup()
|
||||
{
|
||||
_networkManager.Shutdown("Client shutting down");
|
||||
_midiManager.Shutdown();
|
||||
IoCManager.Resolve<IEntityLookup>().Shutdown();
|
||||
_entityManager.Shutdown();
|
||||
_clyde.Shutdown();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Input;
|
||||
|
||||
namespace Robust.Client
|
||||
{
|
||||
|
||||
@@ -1,67 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Robust.Client.Graphics.Clyde;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.ClientEye;
|
||||
using Robust.Client.Graphics.Lighting;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.Debugging;
|
||||
using Robust.Client.Interfaces.GameObjects;
|
||||
using Robust.Client.Interfaces.GameStates;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Lighting;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.Map;
|
||||
using Robust.Client.Interfaces.Placement;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Interfaces.Utility;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Reflection;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Interfaces.Physics;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Interfaces.Timers;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
namespace Robust.Client
|
||||
{
|
||||
@@ -75,7 +17,6 @@ namespace Robust.Client
|
||||
RegisterReflection();
|
||||
}
|
||||
|
||||
|
||||
internal static void RegisterReflection()
|
||||
{
|
||||
// Gets a handle to the shared and the current (client) dll.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Robust.Client;
|
||||
using Robust.Client;
|
||||
using Robust.LoaderApi;
|
||||
|
||||
[assembly: LoaderEntryPoint(typeof(GameController.LoaderEntryPoint))]
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Client
|
||||
{
|
||||
public void Main(IMainArgs args)
|
||||
{
|
||||
GameController.Start(args.Args, contentStart: false, args);
|
||||
Start(args.Args, contentStart: false, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using Robust.Client.Interfaces;
|
||||
using System;
|
||||
using Robust.LoaderApi;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
3
Robust.Client/GameObjects/!NO_FOLDER_NAMESPACES
Normal file
3
Robust.Client/GameObjects/!NO_FOLDER_NAMESPACES
Normal file
@@ -0,0 +1,3 @@
|
||||
Everything in this folder and subfolders needs to be in:
|
||||
|
||||
Robust.Client.GameObjects
|
||||
@@ -1,18 +1,5 @@
|
||||
using Robust.Client.GameObjects.Components;
|
||||
using Robust.Client.GameObjects.Components.Animations;
|
||||
using Robust.Client.GameObjects.Components.Containers;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Components.Appearance;
|
||||
using Robust.Shared.GameObjects.Components.Eye;
|
||||
using Robust.Shared.GameObjects.Components.Map;
|
||||
using Robust.Shared.GameObjects.Components.Renderable;
|
||||
using Robust.Shared.GameObjects.Components.Timers;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -37,9 +24,13 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
Register<PhysicsComponent>();
|
||||
RegisterReference<PhysicsComponent, IPhysBody>();
|
||||
RegisterReference<PhysicsComponent, IPhysicsComponent>();
|
||||
|
||||
Register<CollisionWakeComponent>();
|
||||
|
||||
Register<ContainerManagerComponent>();
|
||||
RegisterReference<ContainerManagerComponent, IContainerManager>();
|
||||
|
||||
RegisterIgnore("KeyBindingInput");
|
||||
Register<PointLightComponent>();
|
||||
|
||||
Register<InputComponent>();
|
||||
|
||||
@@ -55,25 +46,19 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
Register<AppearanceComponent>();
|
||||
RegisterReference<AppearanceComponent, SharedAppearanceComponent>();
|
||||
|
||||
|
||||
Register<AppearanceTestComponent>();
|
||||
Register<SnapGridComponent>();
|
||||
|
||||
Register<ClientUserInterfaceComponent>();
|
||||
RegisterReference<ClientUserInterfaceComponent, SharedUserInterfaceComponent>();
|
||||
|
||||
RegisterIgnore("IgnorePause");
|
||||
|
||||
Register<AnimationPlayerComponent>();
|
||||
|
||||
Register<ContainerManagerComponent>();
|
||||
RegisterReference<ContainerManagerComponent, IContainerManager>();
|
||||
|
||||
Register<TimerComponent>();
|
||||
|
||||
#if DEBUG
|
||||
Register<DebugExceptionOnAddComponent>();
|
||||
Register<DebugExceptionExposeDataComponent>();
|
||||
Register<DebugExceptionInitializeComponent>();
|
||||
Register<DebugExceptionStartupComponent>();
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Client.Interfaces.GameObjects;
|
||||
using JetBrains.Annotations;
|
||||
using Prometheus;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -16,303 +19,152 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Manager for entities -- controls things like template loading and instantiation
|
||||
/// </summary>
|
||||
public sealed class ClientEntityManager : EntityManager, IClientEntityManager
|
||||
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _compFactory = default!;
|
||||
#if EXCEPTION_TOLERANCE
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
[Dependency] private readonly IClientNetManager _networkManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private int _nextClientEntityUid = EntityUid.ClientUid + 1;
|
||||
protected override int NextEntityUid { get; set; } = EntityUid.ClientUid + 1;
|
||||
|
||||
public override void Startup()
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Startup();
|
||||
SetupNetworking();
|
||||
ReceivedComponentMessage += (_, compMsg) => DispatchComponentMessage(compMsg);
|
||||
ReceivedSystemMessage += (_, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
|
||||
|
||||
if (Started)
|
||||
{
|
||||
throw new InvalidOperationException("Startup() called multiple times");
|
||||
}
|
||||
|
||||
EntitySystemManager.Initialize();
|
||||
Started = true;
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public List<EntityUid> ApplyEntityStates(EntityState[]? curEntStates, IEnumerable<EntityUid>? deletions,
|
||||
EntityState[]? nextEntStates)
|
||||
IEntity IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid? uid)
|
||||
{
|
||||
var toApply = new Dictionary<IEntity, (EntityState?, EntityState?)>();
|
||||
var toInitialize = new List<Entity>();
|
||||
var created = new List<EntityUid>();
|
||||
deletions ??= new EntityUid[0];
|
||||
return base.CreateEntity(prototypeName, uid);
|
||||
}
|
||||
|
||||
if (curEntStates != null && curEntStates.Length != 0)
|
||||
void IClientEntityManagerInternal.InitializeEntity(IEntity entity)
|
||||
{
|
||||
EntityManager.InitializeEntity((Entity)entity);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.StartEntity(IEntity entity)
|
||||
{
|
||||
base.StartEntity((Entity)entity);
|
||||
}
|
||||
|
||||
#region IEntityNetworkManager impl
|
||||
|
||||
public override IEntityNetworkManager EntityNetManager => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<NetworkComponentMessage>? ReceivedComponentMessage;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<object>? ReceivedSystemMessage;
|
||||
|
||||
private readonly PriorityQueue<(uint seq, MsgEntity msg)> _queue = new(new MessageTickComparer());
|
||||
private uint _incomingMsgSequence = 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetupNetworking()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
}
|
||||
|
||||
public override void TickUpdate(float frameTime, Histogram? histogram)
|
||||
{
|
||||
using (histogram?.WithLabels("EntityNet").NewTimer())
|
||||
{
|
||||
foreach (var es in curEntStates)
|
||||
while (_queue.Count != 0 && _queue.Peek().msg.SourceTick <= _gameStateManager.CurServerTick)
|
||||
{
|
||||
//Known entities
|
||||
if (Entities.TryGetValue(es.Uid, out var entity))
|
||||
{
|
||||
toApply.Add(entity, (es, null));
|
||||
}
|
||||
else //Unknown entities
|
||||
{
|
||||
var metaState = (MetaDataComponentState?) es.ComponentStates
|
||||
?.FirstOrDefault(c => c.NetID == NetIDs.META_DATA);
|
||||
if (metaState == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Server sent new entity state for {es.Uid} without metadata component!");
|
||||
}
|
||||
var newEntity = CreateEntity(metaState.PrototypeId, es.Uid);
|
||||
toApply.Add(newEntity, (es, null));
|
||||
toInitialize.Add(newEntity);
|
||||
created.Add(newEntity.Uid);
|
||||
}
|
||||
var (_, msg) = _queue.Take();
|
||||
// Logger.DebugS("net.ent", "Dispatching: {0}: {1}", seq, msg);
|
||||
DispatchMsgEntity(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextEntStates != null && nextEntStates.Length != 0)
|
||||
{
|
||||
foreach (var es in nextEntStates)
|
||||
{
|
||||
if (Entities.TryGetValue(es.Uid, out var entity))
|
||||
{
|
||||
if (toApply.TryGetValue(entity, out var state))
|
||||
{
|
||||
toApply[entity] = (state.Item1, es);
|
||||
}
|
||||
else
|
||||
{
|
||||
toApply[entity] = (null, es);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure this is done after all entities have been instantiated.
|
||||
foreach (var kvStates in toApply)
|
||||
{
|
||||
var ent = kvStates.Key;
|
||||
var entity = (Entity) ent;
|
||||
HandleEntityState(entity.EntityManager.ComponentManager, entity, kvStates.Value.Item1,
|
||||
kvStates.Value.Item2);
|
||||
}
|
||||
|
||||
foreach (var kvp in toApply)
|
||||
{
|
||||
UpdateEntityTree(kvp.Key);
|
||||
}
|
||||
|
||||
foreach (var id in deletions)
|
||||
{
|
||||
DeleteEntity(id);
|
||||
}
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
HashSet<Entity> brokenEnts = new HashSet<Entity>();
|
||||
#endif
|
||||
|
||||
foreach (var entity in toInitialize)
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
#endif
|
||||
InitializeEntity(entity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("state", $"Server entity threw in Init: uid={entity.Uid}, proto={entity.Prototype}\n{e}");
|
||||
brokenEnts.Add(entity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
foreach (var entity in toInitialize)
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
if(brokenEnts.Contains(entity))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
#endif
|
||||
StartEntity(entity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("state", $"Server entity threw in Start: uid={entity.Uid}, proto={entity.Prototype}\n{e}");
|
||||
brokenEnts.Add(entity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
foreach (var entity in toInitialize)
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
if(brokenEnts.Contains(entity))
|
||||
continue;
|
||||
#endif
|
||||
UpdateEntityTree(entity);
|
||||
}
|
||||
#if EXCEPTION_TOLERANCE
|
||||
foreach (var entity in brokenEnts)
|
||||
{
|
||||
entity.Delete();
|
||||
}
|
||||
#endif
|
||||
|
||||
return created;
|
||||
base.TickUpdate(frameTime, histogram);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEntity CreateEntityUninitialized(string? prototypeName)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message)
|
||||
{
|
||||
return CreateEntity(prototypeName);
|
||||
SendSystemNetworkMessage(message, default(uint));
|
||||
}
|
||||
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, uint sequence)
|
||||
{
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.SystemMessage;
|
||||
msg.SystemMessage = message;
|
||||
msg.SourceTick = _gameTiming.CurTick;
|
||||
msg.Sequence = sequence;
|
||||
|
||||
_networkManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEntity CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, INetChannel channel)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, GenerateEntityUid());
|
||||
|
||||
if (TryGetEntity(coordinates.EntityId, out var entity))
|
||||
{
|
||||
newEntity.Transform.AttachParent(entity);
|
||||
newEntity.Transform.Coordinates = coordinates;
|
||||
}
|
||||
|
||||
return newEntity;
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEntity CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates)
|
||||
public void SendComponentNetworkMessage(INetChannel? channel, IEntity entity, IComponent component, ComponentMessage message)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, GenerateEntityUid());
|
||||
newEntity.Transform.AttachParent(_mapManager.GetMapEntity(coordinates.MapId));
|
||||
newEntity.Transform.WorldPosition = coordinates.Position;
|
||||
return newEntity;
|
||||
if (!component.NetID.HasValue)
|
||||
throw new ArgumentException($"Component {component.Name} does not have a NetID.", nameof(component));
|
||||
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.ComponentMessage;
|
||||
msg.EntityUid = entity.Uid;
|
||||
msg.NetId = component.NetID.Value;
|
||||
msg.ComponentMessage = message;
|
||||
msg.SourceTick = _gameTiming.CurTick;
|
||||
|
||||
_networkManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEntity SpawnEntity(string? protoName, EntityCoordinates coordinates)
|
||||
private void HandleEntityNetworkMessage(MsgEntity message)
|
||||
{
|
||||
var newEnt = CreateEntityUninitialized(protoName, coordinates);
|
||||
InitializeAndStartEntity((Entity) newEnt);
|
||||
UpdateEntityTree(newEnt);
|
||||
return newEnt;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEntity SpawnEntity(string? protoName, MapCoordinates coordinates)
|
||||
{
|
||||
var entity = CreateEntityUninitialized(protoName, coordinates);
|
||||
InitializeAndStartEntity((Entity) entity);
|
||||
UpdateEntityTree(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEntity SpawnEntityNoMapInit(string? protoName, EntityCoordinates coordinates)
|
||||
{
|
||||
return SpawnEntity(protoName, coordinates);
|
||||
}
|
||||
|
||||
protected override EntityUid GenerateEntityUid()
|
||||
{
|
||||
return new(_nextClientEntityUid++);
|
||||
}
|
||||
|
||||
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityState? curState,
|
||||
EntityState? nextState)
|
||||
{
|
||||
var compStateWork = new Dictionary<uint, (ComponentState? curState, ComponentState? nextState)>();
|
||||
var entityUid = entity.Uid;
|
||||
|
||||
if (curState?.ComponentChanges != null)
|
||||
if (message.SourceTick <= _gameStateManager.CurServerTick)
|
||||
{
|
||||
foreach (var compChange in curState.ComponentChanges)
|
||||
{
|
||||
if (compChange.Deleted)
|
||||
{
|
||||
if (compMan.TryGetComponent(entityUid, compChange.NetID, out var comp))
|
||||
{
|
||||
compMan.RemoveComponent(entityUid, comp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (compMan.HasComponent(entityUid, compChange.NetID))
|
||||
continue;
|
||||
|
||||
var newComp = (Component) _compFactory.GetComponent(compChange.ComponentName!);
|
||||
newComp.Owner = entity;
|
||||
compMan.AddComponent(entity, newComp, true);
|
||||
}
|
||||
}
|
||||
DispatchMsgEntity(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (curState?.ComponentStates != null)
|
||||
{
|
||||
foreach (var compState in curState.ComponentStates)
|
||||
{
|
||||
compStateWork[compState.NetID] = (compState, null);
|
||||
}
|
||||
}
|
||||
// MsgEntity is sent with ReliableOrdered so Lidgren guarantees ordering of incoming messages.
|
||||
// We still need to store a sequence input number to ensure ordering remains consistent in
|
||||
// the priority queue.
|
||||
_queue.Add((++_incomingMsgSequence, message));
|
||||
}
|
||||
|
||||
if (nextState?.ComponentStates != null)
|
||||
private void DispatchMsgEntity(MsgEntity message)
|
||||
{
|
||||
switch (message.Type)
|
||||
{
|
||||
foreach (var compState in nextState.ComponentStates)
|
||||
{
|
||||
if (compStateWork.TryGetValue(compState.NetID, out var state))
|
||||
{
|
||||
compStateWork[compState.NetID] = (state.curState, compState);
|
||||
}
|
||||
else
|
||||
{
|
||||
compStateWork[compState.NetID] = (null, compState);
|
||||
}
|
||||
}
|
||||
}
|
||||
case EntityMessageType.ComponentMessage:
|
||||
ReceivedComponentMessage?.Invoke(this, new NetworkComponentMessage(message));
|
||||
return;
|
||||
|
||||
foreach (var (netId, (cur, next)) in compStateWork)
|
||||
{
|
||||
if (compMan.TryGetComponent(entityUid, netId, out var component))
|
||||
{
|
||||
try
|
||||
{
|
||||
component.HandleComponentState(cur, next);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var wrapper = new ComponentStateApplyException(
|
||||
$"Failed to apply comp state: entity={component.Owner}, comp={component.Name}", e);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
_runtimeLog.LogException(wrapper, "Component state apply");
|
||||
#else
|
||||
throw wrapper;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The component can be null here due to interp.
|
||||
// Because the NEXT state will have a new component, but this one doesn't yet.
|
||||
// That's fine though.
|
||||
if (cur == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var eUid = entityUid;
|
||||
var eRegisteredNetUidName = _compFactory.GetRegistration(netId).Name;
|
||||
DebugTools.Assert(
|
||||
$"Component does not exist for state: entUid={eUid}, expectedNetId={netId}, expectedName={eRegisteredNetUidName}");
|
||||
}
|
||||
case EntityMessageType.SystemMessage:
|
||||
ReceivedSystemMessage?.Invoke(this, message.SystemMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MessageTickComparer : IComparer<(uint seq, MsgEntity msg)>
|
||||
{
|
||||
public int Compare((uint seq, MsgEntity msg) x, (uint seq, MsgEntity msg) y)
|
||||
{
|
||||
var cmp = y.msg.SourceTick.CompareTo(x.msg.SourceTick);
|
||||
if (cmp != 0)
|
||||
{
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return y.seq.CompareTo(x.seq);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Interfaces.GameStates;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// The client implementation of the Entity Network Manager.
|
||||
/// </summary>
|
||||
public class ClientEntityNetworkManager : IEntityNetworkManager
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _networkManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<NetworkComponentMessage>? ReceivedComponentMessage;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<object>? ReceivedSystemMessage;
|
||||
|
||||
private readonly PriorityQueue<(uint seq, MsgEntity msg)> _queue = new(new MessageTickComparer());
|
||||
private uint _incomingMsgSequence = 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetupNetworking()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
while (_queue.Count != 0 && _queue.Peek().msg.SourceTick <= _gameStateManager.CurServerTick)
|
||||
{
|
||||
var (_, msg) = _queue.Take();
|
||||
// Logger.DebugS("net.ent", "Dispatching: {0}: {1}", seq, msg);
|
||||
DispatchMsgEntity(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message)
|
||||
{
|
||||
SendSystemNetworkMessage(message, default(uint));
|
||||
}
|
||||
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message, uint sequence)
|
||||
{
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.SystemMessage;
|
||||
msg.SystemMessage = message;
|
||||
msg.SourceTick = _gameTiming.CurTick;
|
||||
msg.Sequence = sequence;
|
||||
|
||||
_networkManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message, INetChannel channel)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendComponentNetworkMessage(INetChannel? channel, IEntity entity, IComponent component, ComponentMessage message)
|
||||
{
|
||||
if (!component.NetID.HasValue)
|
||||
throw new ArgumentException($"Component {component.Name} does not have a NetID.", nameof(component));
|
||||
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.ComponentMessage;
|
||||
msg.EntityUid = entity.Uid;
|
||||
msg.NetId = component.NetID.Value;
|
||||
msg.ComponentMessage = message;
|
||||
msg.SourceTick = _gameTiming.CurTick;
|
||||
|
||||
_networkManager.ClientSendMessage(msg);
|
||||
}
|
||||
|
||||
private void HandleEntityNetworkMessage(MsgEntity message)
|
||||
{
|
||||
if (message.SourceTick <= _gameStateManager.CurServerTick)
|
||||
{
|
||||
DispatchMsgEntity(message);
|
||||
return;
|
||||
}
|
||||
|
||||
// MsgEntity is sent with ReliableOrdered so Lidgren guarantees ordering of incoming messages.
|
||||
// We still need to store a sequence input number to ensure ordering remains consistent in
|
||||
// the priority queue.
|
||||
_queue.Add((++_incomingMsgSequence, message));
|
||||
}
|
||||
|
||||
private void DispatchMsgEntity(MsgEntity message)
|
||||
{
|
||||
switch (message.Type)
|
||||
{
|
||||
case EntityMessageType.ComponentMessage:
|
||||
ReceivedComponentMessage?.Invoke(this, new NetworkComponentMessage(message));
|
||||
return;
|
||||
|
||||
case EntityMessageType.SystemMessage:
|
||||
ReceivedSystemMessage?.Invoke(this, message.SystemMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MessageTickComparer : IComparer<(uint seq, MsgEntity msg)>
|
||||
{
|
||||
public int Compare((uint seq, MsgEntity msg) x, (uint seq, MsgEntity msg) y)
|
||||
{
|
||||
var cmp = y.msg.SourceTick.CompareTo(x.msg.SourceTick);
|
||||
if (cmp != 0)
|
||||
{
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return y.seq.CompareTo(x.seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Robust.Client.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Robust.Client.Animations.AnimationPlaybackShared;
|
||||
|
||||
namespace Robust.Client.GameObjects.Components.Animations
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Plays back <see cref="Animation"/>s on entities.
|
||||
@@ -48,11 +48,21 @@ namespace Robust.Client.GameObjects.Components.Animations
|
||||
return;
|
||||
}
|
||||
|
||||
List<string>? toRemove = null;
|
||||
// TODO: Get rid of this ToArray() allocation.
|
||||
foreach (var (key, playback) in _playingAnimations.ToArray())
|
||||
{
|
||||
var keep = UpdatePlayback(Owner, playback, frameTime);
|
||||
if (!keep)
|
||||
{
|
||||
toRemove ??= new List<string>();
|
||||
toRemove.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove != null)
|
||||
{
|
||||
foreach (var key in toRemove)
|
||||
{
|
||||
_playingAnimations.Remove(key);
|
||||
AnimationCompleted?.Invoke(key);
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Appearance;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
@@ -19,13 +14,11 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[ViewVariables]
|
||||
private Dictionary<object, object> data = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("visuals")]
|
||||
internal List<AppearanceVisualizer> Visualizers = new();
|
||||
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
|
||||
private static bool _didRegisterSerializer;
|
||||
|
||||
[ViewVariables]
|
||||
private bool _appearanceDirty;
|
||||
|
||||
@@ -54,17 +47,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,12 +65,14 @@ namespace Robust.Client.GameObjects
|
||||
return true;
|
||||
}
|
||||
|
||||
data = default;
|
||||
data = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetData(object key, object value)
|
||||
{
|
||||
if (data.TryGetValue(key, out var existing) && existing.Equals(value)) return;
|
||||
|
||||
data[key] = value;
|
||||
|
||||
MarkDirty();
|
||||
@@ -85,10 +80,9 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if (curState == null)
|
||||
if (curState is not AppearanceComponentState actualState)
|
||||
return;
|
||||
|
||||
var actualState = (AppearanceComponentState) curState;
|
||||
data = actualState.Data;
|
||||
MarkDirty();
|
||||
}
|
||||
@@ -100,8 +94,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
EntitySystem.Get<AppearanceSystem>()
|
||||
.EnqueueAppearanceUpdate(this);
|
||||
EntitySystem.Get<AppearanceSystem>().EnqueueUpdate(this);
|
||||
_appearanceDirty = true;
|
||||
}
|
||||
|
||||
@@ -110,18 +103,6 @@ namespace Robust.Client.GameObjects
|
||||
_appearanceDirty = false;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
if (!_didRegisterSerializer)
|
||||
{
|
||||
YamlObjectSerializer.RegisterTypeSerializer(typeof(AppearanceVisualizer),
|
||||
new VisualizerTypeSerializer(_reflectionManager));
|
||||
_didRegisterSerializer = true;
|
||||
}
|
||||
|
||||
serializer.DataField(ref Visualizers, "visuals", new List<AppearanceVisualizer>());
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -133,108 +114,15 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
class VisualizerTypeSerializer : YamlObjectSerializer.TypeSerializer
|
||||
{
|
||||
private readonly IReflectionManager _reflectionManager;
|
||||
|
||||
public VisualizerTypeSerializer(IReflectionManager reflectionManager)
|
||||
{
|
||||
_reflectionManager = reflectionManager;
|
||||
}
|
||||
|
||||
public override object NodeToType(Type type, YamlNode node, YamlObjectSerializer serializer)
|
||||
{
|
||||
var mapping = (YamlMappingNode) node;
|
||||
var nodeType = mapping.GetNode("type");
|
||||
switch (nodeType.AsString())
|
||||
{
|
||||
case SpriteLayerToggle.NAME:
|
||||
var keyString = mapping.GetNode("key").AsString();
|
||||
object key;
|
||||
if (_reflectionManager.TryParseEnumReference(keyString, out var @enum))
|
||||
{
|
||||
key = @enum;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = keyString;
|
||||
}
|
||||
|
||||
var layer = mapping.GetNode("layer").AsInt();
|
||||
return new SpriteLayerToggle(key, layer);
|
||||
|
||||
default:
|
||||
var visType = _reflectionManager.LooseGetType(nodeType.AsString());
|
||||
if (!typeof(AppearanceVisualizer).IsAssignableFrom(visType))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var vis = (AppearanceVisualizer) Activator.CreateInstance(visType)!;
|
||||
vis.LoadData(mapping);
|
||||
return vis;
|
||||
}
|
||||
}
|
||||
|
||||
public override YamlNode TypeToNode(object obj, YamlObjectSerializer serializer)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case SpriteLayerToggle spriteLayerToggle:
|
||||
YamlScalarNode key;
|
||||
if (spriteLayerToggle.Key is Enum)
|
||||
{
|
||||
var name = spriteLayerToggle.Key.GetType().FullName;
|
||||
key = new YamlScalarNode($"{name}.{spriteLayerToggle.Key}");
|
||||
}
|
||||
else
|
||||
{
|
||||
key = new YamlScalarNode(spriteLayerToggle.Key.ToString());
|
||||
}
|
||||
|
||||
return new YamlMappingNode
|
||||
{
|
||||
{new YamlScalarNode("type"), new YamlScalarNode(SpriteLayerToggle.NAME)},
|
||||
{new YamlScalarNode("key"), key},
|
||||
{new YamlScalarNode("layer"), new YamlScalarNode(spriteLayerToggle.SpriteLayer.ToString())},
|
||||
};
|
||||
default:
|
||||
// TODO: A proper way to do serialization here.
|
||||
// I can't use the ExposeData system here since that's specific to entity serializers.
|
||||
return new YamlMappingNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteLayerToggle : AppearanceVisualizer
|
||||
{
|
||||
public const string NAME = "sprite_layer_toggle";
|
||||
|
||||
public readonly object Key;
|
||||
public readonly int SpriteLayer;
|
||||
|
||||
public SpriteLayerToggle(object key, int spriteLayer)
|
||||
{
|
||||
Key = key;
|
||||
SpriteLayer = spriteLayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the visualization of data inside of an appearance component.
|
||||
/// Implementations of this class are NOT bound to a specific entity, they are flyweighted across multiple.
|
||||
/// </summary>
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract class AppearanceVisualizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Load data from the prototype declaring this visualizer, to configure settings and such.
|
||||
/// </summary>
|
||||
public virtual void LoadData(YamlMappingNode node)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an entity to be managed by this appearance controller.
|
||||
/// DO NOT assume this is your only entity. Visualizers are shared.
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects.Components.Containers
|
||||
{
|
||||
public sealed partial class ContainerManagerComponent
|
||||
{
|
||||
[DebuggerDisplay("ClientContainer {Owner.Uid}/{ID}")]
|
||||
private sealed class ClientContainer : IContainer
|
||||
{
|
||||
public List<IEntity> Entities { get; } = new List<IEntity>();
|
||||
|
||||
public ClientContainer(string id, ContainerManagerComponent manager)
|
||||
{
|
||||
ID = id;
|
||||
Manager = manager;
|
||||
}
|
||||
|
||||
[ViewVariables] public IContainerManager Manager { get; }
|
||||
[ViewVariables] public string ID { get; }
|
||||
[ViewVariables] public IEntity Owner => Manager.Owner;
|
||||
[ViewVariables] public bool Deleted { get; private set; }
|
||||
[ViewVariables] public IReadOnlyList<IEntity> ContainedEntities => Entities;
|
||||
[ViewVariables]
|
||||
public bool ShowContents { get; set; }
|
||||
[ViewVariables]
|
||||
public bool OccludesLight { get; set; }
|
||||
|
||||
public bool CanInsert(IEntity toinsert)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Insert(IEntity toinsert)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanRemove(IEntity toremove)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Remove(IEntity toremove)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ForceRemove(IEntity toRemove)
|
||||
{
|
||||
throw new NotSupportedException("Cannot directly modify containers on the client");
|
||||
}
|
||||
|
||||
public bool Contains(IEntity contained)
|
||||
{
|
||||
return Entities.Contains(contained);
|
||||
}
|
||||
|
||||
public void DoInsert(IEntity entity)
|
||||
{
|
||||
Entities.Add(entity);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(entity));
|
||||
}
|
||||
|
||||
public void DoRemove(IEntity entity)
|
||||
{
|
||||
Entities.Remove(entity);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(entity));
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
Deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void InternalContainerShutdown(IContainer container)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Containers;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.GameObjects.Components.Containers
|
||||
{
|
||||
public sealed partial class ContainerManagerComponent : SharedContainerManagerComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<string, ClientContainer> _containers = new();
|
||||
|
||||
public override T MakeContainer<T>(string id)
|
||||
{
|
||||
throw new NotSupportedException("Cannot modify containers on the client.");
|
||||
}
|
||||
|
||||
public override bool Remove(IEntity entity)
|
||||
{
|
||||
// TODO: This will probably need relaxing if we want to predict things like inventories.
|
||||
throw new NotSupportedException("Cannot modify containers on the client.");
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContainer> GetAllContainersImpl()
|
||||
{
|
||||
return _containers.Values.Where(c => !c.Deleted);
|
||||
}
|
||||
|
||||
public override IContainer GetContainer(string id)
|
||||
{
|
||||
return _containers[id];
|
||||
}
|
||||
|
||||
public override bool HasContainer(string id)
|
||||
{
|
||||
return _containers.ContainsKey(id);
|
||||
}
|
||||
|
||||
public override bool TryGetContainer(string id, [NotNullWhen(true)] out IContainer? container)
|
||||
{
|
||||
var ret = _containers.TryGetValue(id, out var cont);
|
||||
container = cont!;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryGetContainer(IEntity entity, [NotNullWhen(true)] out IContainer? container)
|
||||
{
|
||||
foreach (var contain in _containers.Values)
|
||||
{
|
||||
if (!contain.Deleted && contain.Contains(entity))
|
||||
{
|
||||
container = contain;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
container = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ContainsEntity(IEntity entity)
|
||||
{
|
||||
foreach (var container in _containers.Values)
|
||||
{
|
||||
if (!container.Deleted && container.Contains(entity))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void ForceRemove(IEntity entity)
|
||||
{
|
||||
throw new NotSupportedException("Cannot modify containers on the client.");
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
if(!(curState is ContainerManagerComponentState cast))
|
||||
return;
|
||||
|
||||
// Delete now-gone containers.
|
||||
List<string>? toDelete = null;
|
||||
foreach (var (id, container) in _containers)
|
||||
{
|
||||
if (!cast.Containers.ContainsKey(id))
|
||||
{
|
||||
container.Shutdown();
|
||||
toDelete ??= new List<string>();
|
||||
toDelete.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete != null)
|
||||
{
|
||||
foreach (var dead in toDelete)
|
||||
{
|
||||
_containers.Remove(dead);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new containers and update existing contents.
|
||||
foreach (var (id, data) in cast.Containers)
|
||||
{
|
||||
|
||||
if (!_containers.TryGetValue(id, out var container))
|
||||
{
|
||||
container = new ClientContainer(id, this);
|
||||
_containers.Add(id, container);
|
||||
}
|
||||
|
||||
// sync show flag
|
||||
container.ShowContents = data.ShowContents;
|
||||
container.OccludesLight = data.OccludesLight;
|
||||
|
||||
// Remove gone entities.
|
||||
List<IEntity>? toRemove = null;
|
||||
foreach (var entity in container.Entities)
|
||||
{
|
||||
if (!data.ContainedEntities.Contains(entity.Uid))
|
||||
{
|
||||
toRemove ??= new List<IEntity>();
|
||||
toRemove.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove != null)
|
||||
{
|
||||
foreach (var goner in toRemove)
|
||||
{
|
||||
container.DoRemove(goner);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new entities.
|
||||
foreach (var uid in data.ContainedEntities)
|
||||
{
|
||||
var entity = Owner.EntityManager.GetEntity(uid);
|
||||
|
||||
if (!container.Entities.Contains(entity))
|
||||
{
|
||||
container.DoInsert(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
// On shutdown we won't get to process remove events in the containers so this has to be manually done.
|
||||
foreach (var container in _containers.Values)
|
||||
{
|
||||
foreach (var containerEntity in container.Entities)
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
|
||||
new UpdateContainerOcclusionMessage(containerEntity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user