mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
852 Commits
reactjs-su
...
v0.65.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7120000ef5 | ||
|
|
7c3fd3eaa9 | ||
|
|
8bd47cd7f8 | ||
|
|
1068458beb | ||
|
|
54db524e53 | ||
|
|
9265af75bf | ||
|
|
037bff9099 | ||
|
|
6de1b41d9c | ||
|
|
0ab9f34046 | ||
|
|
2746dccfc6 | ||
|
|
eae54d0327 | ||
|
|
c1195d1f1d | ||
|
|
5a02c5c03f | ||
|
|
d9c59164f2 | ||
|
|
72a952fbdd | ||
|
|
45e58c1ed8 | ||
|
|
127d1d7eaf | ||
|
|
18c9c6bf0a | ||
|
|
89ec06468f | ||
|
|
9920e409ca | ||
|
|
f1681b2128 | ||
|
|
490a4efff8 | ||
|
|
a416eedff0 | ||
|
|
2fe0c94d84 | ||
|
|
2f22de4eff | ||
|
|
2017d943fb | ||
|
|
e34935c9e2 | ||
|
|
db95c6284b | ||
|
|
6255ee6e96 | ||
|
|
d1b16d9a52 | ||
|
|
708b3b2acf | ||
|
|
2e471366b7 | ||
|
|
ca9ce7c7ed | ||
|
|
81cd43f988 | ||
|
|
cf5c72e7ea | ||
|
|
2629fd3efb | ||
|
|
338fcd5fcb | ||
|
|
b5395c0bc0 | ||
|
|
97bdc1edee | ||
|
|
8a0464ed35 | ||
|
|
31d68dcd49 | ||
|
|
3b337bf88d | ||
|
|
f3e0706488 | ||
|
|
3378f1ff98 | ||
|
|
f160508da3 | ||
|
|
1ba5c9120f | ||
|
|
701606c75a | ||
|
|
efa0fa3f1b | ||
|
|
63b9b56006 | ||
|
|
4b082baf37 | ||
|
|
698d65c5af | ||
|
|
c9dd27658e | ||
|
|
266223c3fd | ||
|
|
521e4746c6 | ||
|
|
72efbe543c | ||
|
|
b69ccc2241 | ||
|
|
656992ae5f | ||
|
|
947f04ebb9 | ||
|
|
603b88c77a | ||
|
|
108e270f9e | ||
|
|
d3edd10714 | ||
|
|
125d138e16 | ||
|
|
f5c210b990 | ||
|
|
4521dc37f5 | ||
|
|
adcdb9f736 | ||
|
|
2116230f58 | ||
|
|
dd0d186314 | ||
|
|
329e2b0cdc | ||
|
|
a562675553 | ||
|
|
89a1d32e1d | ||
|
|
05fb110f40 | ||
|
|
c6896e9bd9 | ||
|
|
2678fad629 | ||
|
|
7d0e9f9635 | ||
|
|
7a5df9a718 | ||
|
|
856fbd0480 | ||
|
|
828ac48f7c | ||
|
|
d0e11a755c | ||
|
|
b637967163 | ||
|
|
50dce4d56b | ||
|
|
e9af594448 | ||
|
|
b5662007d5 | ||
|
|
03acded657 | ||
|
|
6b0e2deede | ||
|
|
ffe1689be6 | ||
|
|
2f3f7976f1 | ||
|
|
df7933b890 | ||
|
|
5778ca87e7 | ||
|
|
af8a21df53 | ||
|
|
cd02dddd5d | ||
|
|
e44805cd20 | ||
|
|
635387a23e | ||
|
|
2b594f6356 | ||
|
|
cec14ae861 | ||
|
|
a12a973a26 | ||
|
|
45a315bb69 | ||
|
|
1e843bf3a2 | ||
|
|
26c85725a5 | ||
|
|
467d391ec8 | ||
|
|
601feb7cc0 | ||
|
|
cd1466cdc0 | ||
|
|
94aeb309b2 | ||
|
|
3fba59eb6c | ||
|
|
508754f4bc | ||
|
|
3f4ae0f02a | ||
|
|
b0daf64d94 | ||
|
|
c4223f015d | ||
|
|
33ebb99b9c | ||
|
|
dad176aca1 | ||
|
|
cac1f3e018 | ||
|
|
49dfb2d2f1 | ||
|
|
4e0821c6c1 | ||
|
|
39800d57d1 | ||
|
|
52b9729bb8 | ||
|
|
5a49ce1812 | ||
|
|
c3d27fb338 | ||
|
|
07378f4503 | ||
|
|
ae2f3fe70c | ||
|
|
6edd5d8a63 | ||
|
|
08c6e52806 | ||
|
|
f13a066bdd | ||
|
|
c478ef3a4b | ||
|
|
1d3bf8d072 | ||
|
|
12f40d951d | ||
|
|
c98e409ad8 | ||
|
|
ef6efd3c68 | ||
|
|
c4b0534240 | ||
|
|
ff0dbecbee | ||
|
|
b0d59e5e3f | ||
|
|
e3d29ae28f | ||
|
|
9aebaa3749 | ||
|
|
2bae6abbc0 | ||
|
|
880160e7af | ||
|
|
801b513cde | ||
|
|
bd284cc945 | ||
|
|
c481b50a8b | ||
|
|
c889c2b2aa | ||
|
|
60c150269e | ||
|
|
8dffeef261 | ||
|
|
6943693d3f | ||
|
|
964ab60020 | ||
|
|
8fad4d291f | ||
|
|
d3adcb83db | ||
|
|
3c046b5f6f | ||
|
|
dd567a4cc8 | ||
|
|
fe78883a93 | ||
|
|
1d0c9e9512 | ||
|
|
1a61f1925c | ||
|
|
18567c7051 | ||
|
|
4932487859 | ||
|
|
506f0b0a1e | ||
|
|
ae2e6bca82 | ||
|
|
0b19d04c1c | ||
|
|
8344e8ba10 | ||
|
|
5dd1b58357 | ||
|
|
537e1ab4f9 | ||
|
|
7247171b4b | ||
|
|
155a35ab06 | ||
|
|
3c50db8b5e | ||
|
|
78ba02432e | ||
|
|
32f8b6f4b8 | ||
|
|
97d52393ed | ||
|
|
576abe7adb | ||
|
|
d404494018 | ||
|
|
80e390a74b | ||
|
|
cf912d8c07 | ||
|
|
ae8a7481bc | ||
|
|
dbcb9bde3d | ||
|
|
6dc06e7690 | ||
|
|
f2cbfbf705 | ||
|
|
ca79d1fc13 | ||
|
|
2bcfca3d78 | ||
|
|
74c86bf08c | ||
|
|
b8397a1a27 | ||
|
|
4c2da94a58 | ||
|
|
42c6b9976a | ||
|
|
cd40916df0 | ||
|
|
95525a549a | ||
|
|
07c5c0ca0e | ||
|
|
c8e36eadf7 | ||
|
|
b3df54d2bb | ||
|
|
007bfe4447 | ||
|
|
ea1691c7f5 | ||
|
|
f82e3e8432 | ||
|
|
59d8cb37f0 | ||
|
|
cf52985eaf | ||
|
|
aa412c2b47 | ||
|
|
917280b4d7 | ||
|
|
51c6ff934e | ||
|
|
e534fcd800 | ||
|
|
6105be4b14 | ||
|
|
205147c0f0 | ||
|
|
f6d722f7c3 | ||
|
|
d1c1bffc4f | ||
|
|
54316b7255 | ||
|
|
2d7cd49dbc | ||
|
|
5832cb3866 | ||
|
|
818ea1a629 | ||
|
|
58c0f7796b | ||
|
|
55217c982d | ||
|
|
036951ae62 | ||
|
|
ebaa8d9ac3 | ||
|
|
e222f82059 | ||
|
|
d867661c24 | ||
|
|
ac7c44ab2e | ||
|
|
b861182846 | ||
|
|
1f3e42b013 | ||
|
|
7b0c4dd7fb | ||
|
|
2898e1413b | ||
|
|
ba3525c0c0 | ||
|
|
5ff11980b7 | ||
|
|
29274986f8 | ||
|
|
57d862f9fc | ||
|
|
31da2557d5 | ||
|
|
9752996497 | ||
|
|
7c7c8d23fb | ||
|
|
e1dc83a573 | ||
|
|
520461ff95 | ||
|
|
7c2a010c71 | ||
|
|
c17faedccf | ||
|
|
36495fe81b | ||
|
|
9e3851b847 | ||
|
|
582e705088 | ||
|
|
cf893c3c32 | ||
|
|
3a5cc20a88 | ||
|
|
cb209df0e5 | ||
|
|
eb64f55ce5 | ||
|
|
00ab9c6474 | ||
|
|
26eaa38e89 | ||
|
|
42bc16d4c9 | ||
|
|
498ce86176 | ||
|
|
3104678140 | ||
|
|
c63984eaca | ||
|
|
078b81d495 | ||
|
|
4c341f0289 | ||
|
|
bb4461587e | ||
|
|
fe45af151a | ||
|
|
ae2b0e9d8f | ||
|
|
ddf4828bf9 | ||
|
|
b1d0dbf03c | ||
|
|
a91b134c5d | ||
|
|
660b0b05d3 | ||
|
|
e14493246e | ||
|
|
d93a35b620 | ||
|
|
2e12a712fa | ||
|
|
29a84a6621 | ||
|
|
51f00d4b3b | ||
|
|
a67b22cce8 | ||
|
|
cfbfdbf420 | ||
|
|
a76d053818 | ||
|
|
1587ee3c93 | ||
|
|
662808d0d3 | ||
|
|
9d88857b55 | ||
|
|
3c92794aed | ||
|
|
527879c048 | ||
|
|
d2494bcdc7 | ||
|
|
486aa1423b | ||
|
|
20659789b0 | ||
|
|
d87d6f7f8c | ||
|
|
fa5fdcb5a5 | ||
|
|
e6af084c22 | ||
|
|
de01b9d8cb | ||
|
|
c9aa2c61bd | ||
|
|
8200f1d942 | ||
|
|
1af0ebca6c | ||
|
|
fe5957bb91 | ||
|
|
be08f1787f | ||
|
|
ea35e6c03f | ||
|
|
a6c6b22393 | ||
|
|
5e6e5cb5ea | ||
|
|
64d2a3ca28 | ||
|
|
bee4e499a8 | ||
|
|
55da2dc5e5 | ||
|
|
177eca0f90 | ||
|
|
ae229a03d8 | ||
|
|
2dc8093660 | ||
|
|
071acbc818 | ||
|
|
3f5982cac0 | ||
|
|
6f387402fe | ||
|
|
363e28561c | ||
|
|
4d63b54259 | ||
|
|
7fa05c7db3 | ||
|
|
c466f5f17f | ||
|
|
b6f302b71e | ||
|
|
7d9db6f7b2 | ||
|
|
8bedba2da7 | ||
|
|
837dde78c9 | ||
|
|
a01629969d | ||
|
|
15e13c7f92 | ||
|
|
3dd80b90eb | ||
|
|
cb8e295ad9 | ||
|
|
909163a923 | ||
|
|
6aa2408364 | ||
|
|
afc1eed7ae | ||
|
|
85c8dc05fe | ||
|
|
710371d7d1 | ||
|
|
9f651646d7 | ||
|
|
d2860c80a9 | ||
|
|
e1b9ae22b6 | ||
|
|
5d64f35c96 | ||
|
|
5cfea0cd97 | ||
|
|
a165556bf5 | ||
|
|
dee2881203 | ||
|
|
da0891a5f4 | ||
|
|
1ed9796700 | ||
|
|
578a967a31 | ||
|
|
14c0c58e87 | ||
|
|
766d909a08 | ||
|
|
9c50a217da | ||
|
|
4f9db0cdb7 | ||
|
|
3c19937750 | ||
|
|
c509764014 | ||
|
|
75e06e4060 | ||
|
|
1b909f71a1 | ||
|
|
1a8764f54b | ||
|
|
9152c97de7 | ||
|
|
45cb04f928 | ||
|
|
a700750d9e | ||
|
|
07d327eb8b | ||
|
|
2275ec9573 | ||
|
|
5848b449f6 | ||
|
|
67aa32e694 | ||
|
|
dbd2961b9f | ||
|
|
b77b49c667 | ||
|
|
0eabe62bdb | ||
|
|
31e2ea2770 | ||
|
|
7a636b3b87 | ||
|
|
98ce017b4a | ||
|
|
26b04f0d66 | ||
|
|
f4f2dea688 | ||
|
|
e9a0f9a4c1 | ||
|
|
de438ae94c | ||
|
|
9ec77f20ee | ||
|
|
da01040b52 | ||
|
|
dce2a5ddb2 | ||
|
|
5c99fbabf2 | ||
|
|
9d0846c0e9 | ||
|
|
035ecfb098 | ||
|
|
3693f5aee7 | ||
|
|
b859815b07 | ||
|
|
3701ca83e4 | ||
|
|
1473f1d34c | ||
|
|
889c140fb9 | ||
|
|
c8259915f8 | ||
|
|
a2a25fb296 | ||
|
|
938a9929ea | ||
|
|
7726075b9b | ||
|
|
03b3d1bbe7 | ||
|
|
17ec51b74c | ||
|
|
e92998d1ec | ||
|
|
6691512136 | ||
|
|
a80f4ad76c | ||
|
|
2c6f4cd80c | ||
|
|
51a4c6dcf2 | ||
|
|
1f402e581a | ||
|
|
17ea92bfda | ||
|
|
6a8266af7e | ||
|
|
f6b7606648 | ||
|
|
9cd8adae93 | ||
|
|
c5ba8b75c8 | ||
|
|
3d73cc7289 | ||
|
|
11aa062ee0 | ||
|
|
b4358a9e33 | ||
|
|
0cce4714a1 | ||
|
|
9c4e6a6595 | ||
|
|
1ddd541fe9 | ||
|
|
e45aa3f2fe | ||
|
|
99efdb6061 | ||
|
|
32f0ffdc79 | ||
|
|
cf166483c9 | ||
|
|
49631867f4 | ||
|
|
9f56eaec9a | ||
|
|
04f2b732a5 | ||
|
|
b8cfabc339 | ||
|
|
1cdd39202f | ||
|
|
3290720b4c | ||
|
|
49badb06cb | ||
|
|
2c6941e73b | ||
|
|
5e5883cb88 | ||
|
|
02c504445e | ||
|
|
4d5075a792 | ||
|
|
25212cbb4d | ||
|
|
c56cc24804 | ||
|
|
cf9250f25d | ||
|
|
a105e02131 | ||
|
|
cebdca87df | ||
|
|
bbabee3949 | ||
|
|
f16d41704b | ||
|
|
6f557ac821 | ||
|
|
ba70a7c95e | ||
|
|
f3796ee69d | ||
|
|
b2a71e5ef8 | ||
|
|
c253130c4a | ||
|
|
f03826b5ec | ||
|
|
69cf93a425 | ||
|
|
a4ea7fed6a | ||
|
|
7046152ec8 | ||
|
|
f6f1f76980 | ||
|
|
a98a214fa3 | ||
|
|
faadb70de7 | ||
|
|
7a6d6c83b1 | ||
|
|
9e32eb46e5 | ||
|
|
bea91d119e | ||
|
|
9ec725d848 | ||
|
|
790d523ebd | ||
|
|
147ca286ea | ||
|
|
bc25f5d727 | ||
|
|
c65740eba6 | ||
|
|
59e6a797f1 | ||
|
|
663d901e6b | ||
|
|
585126bf52 | ||
|
|
e27ed228ad | ||
|
|
ef35187624 | ||
|
|
1cf31fbea6 | ||
|
|
c7a3c43a4f | ||
|
|
01b55494e8 | ||
|
|
be9ab89b29 | ||
|
|
d4c8631587 | ||
|
|
3d43fc510e | ||
|
|
931c38a97d | ||
|
|
53c496fe27 | ||
|
|
c696ac1c73 | ||
|
|
57e5ceef1a | ||
|
|
b5279f3f89 | ||
|
|
fc424309c1 | ||
|
|
19aac6c461 | ||
|
|
2decf31a78 | ||
|
|
f33ba17148 | ||
|
|
752d4dfe8b | ||
|
|
92d40cded3 | ||
|
|
11f8bf794a | ||
|
|
0604464dcc | ||
|
|
9f25c14707 | ||
|
|
2c50e4f219 | ||
|
|
e97d8b9b14 | ||
|
|
f129bd45c5 | ||
|
|
f812b307b5 | ||
|
|
436cb98d53 | ||
|
|
1c1ced0f8a | ||
|
|
97015569f6 | ||
|
|
ee2d266d6b | ||
|
|
786ecdb8b1 | ||
|
|
488260cc64 | ||
|
|
6ee96873a4 | ||
|
|
2220f3e346 | ||
|
|
137723357c | ||
|
|
db3f3e5b32 | ||
|
|
fc10adfb03 | ||
|
|
812809bbfd | ||
|
|
ec70abe928 | ||
|
|
0ec6995542 | ||
|
|
1c28782d5d | ||
|
|
45472fa342 | ||
|
|
18a5335373 | ||
|
|
86590ceb1f | ||
|
|
e518abd2be | ||
|
|
13ae59dbf6 | ||
|
|
0ded41ebe1 | ||
|
|
3eb295e3ef | ||
|
|
1782792d30 | ||
|
|
d5c0776278 | ||
|
|
0822b41155 | ||
|
|
7a81c7f64a | ||
|
|
a925b7f84d | ||
|
|
557de01532 | ||
|
|
a38cbc7188 | ||
|
|
5146b1b3c6 | ||
|
|
0e97e3000f | ||
|
|
0c4a05dcbf | ||
|
|
565fa9f046 | ||
|
|
2783e89d1f | ||
|
|
d9d08dcbe3 | ||
|
|
6abeedae97 | ||
|
|
7bc1eaabfd | ||
|
|
526997639b | ||
|
|
9dc030c764 | ||
|
|
35308975db | ||
|
|
7649c120e9 | ||
|
|
610f6d88af | ||
|
|
0fb5b59eba | ||
|
|
77b1fd933c | ||
|
|
4ec019078d | ||
|
|
fe022d4100 | ||
|
|
1d4eec4855 | ||
|
|
84fceb587b | ||
|
|
a4ad98519e | ||
|
|
8f55a21cc4 | ||
|
|
4e40184c4a | ||
|
|
8bea8237f8 | ||
|
|
00c4d68db8 | ||
|
|
93e26fd88a | ||
|
|
312b879b06 | ||
|
|
852d1874db | ||
|
|
9e14a7e0d3 | ||
|
|
5700969945 | ||
|
|
6feb8b69ee | ||
|
|
1ab14fa6b7 | ||
|
|
445b13791e | ||
|
|
5605149a63 | ||
|
|
162b069de3 | ||
|
|
1c65686ee0 | ||
|
|
278755d938 | ||
|
|
64b356a9bf | ||
|
|
9681dc705e | ||
|
|
fa2b0233c3 | ||
|
|
392e2fefca | ||
|
|
47c56ffcf4 | ||
|
|
dd602fd6d0 | ||
|
|
2059d37c8f | ||
|
|
6b73b765b2 | ||
|
|
fb32b55584 | ||
|
|
045aebfc9e | ||
|
|
449418b1d4 | ||
|
|
06cf2208f4 | ||
|
|
ffbfdfe0b2 | ||
|
|
d7caa93cf5 | ||
|
|
0665781dfe | ||
|
|
a91413f1a6 | ||
|
|
6b9eeac228 | ||
|
|
c86e4e1ab9 | ||
|
|
269d229c2d | ||
|
|
7e86065c67 | ||
|
|
dcb0c2eb92 | ||
|
|
fd667a67e7 | ||
|
|
bf966ee49b | ||
|
|
2f75b74f1e | ||
|
|
a05b864952 | ||
|
|
2d96e0cda1 | ||
|
|
cd1d6ee598 | ||
|
|
09d7ad9d62 | ||
|
|
0c9c1366b0 | ||
|
|
8470cf43b1 | ||
|
|
62a24a320c | ||
|
|
578066511f | ||
|
|
2821492885 | ||
|
|
3806481f81 | ||
|
|
e610b8a286 | ||
|
|
d283043a56 | ||
|
|
4171b3ec4e | ||
|
|
f90872b085 | ||
|
|
077faebfc5 | ||
|
|
526b37804e | ||
|
|
b8cb9e6b1d | ||
|
|
0dfd71c990 | ||
|
|
18232e946b | ||
|
|
c06f3f003c | ||
|
|
72cf2f6334 | ||
|
|
e0e47ad545 | ||
|
|
70157265b8 | ||
|
|
1e5068f057 | ||
|
|
2bdea420ae | ||
|
|
be906faf42 | ||
|
|
a03eecaa36 | ||
|
|
bc12a0af87 | ||
|
|
d347641d00 | ||
|
|
221dbf945e | ||
|
|
24c900aeae | ||
|
|
76aaca6260 | ||
|
|
d3519d799a | ||
|
|
1ea18b9448 | ||
|
|
1e7956eaa8 | ||
|
|
7655aec413 | ||
|
|
a36a10c814 | ||
|
|
26cdae6877 | ||
|
|
c96eae620c | ||
|
|
9591d16aed | ||
|
|
05bb361eb2 | ||
|
|
776669b789 | ||
|
|
8d5fa58e1a | ||
|
|
dda544223f | ||
|
|
34106195b0 | ||
|
|
5bf3067342 | ||
|
|
ebeb504347 | ||
|
|
0b824f2170 | ||
|
|
0d04d8a82a | ||
|
|
b360c4439b | ||
|
|
d2cb4d102a | ||
|
|
b44524cd10 | ||
|
|
7f0feef97c | ||
|
|
82a67ecd99 | ||
|
|
42ad734a46 | ||
|
|
ac949a4bee | ||
|
|
60ca5beffa | ||
|
|
a6ab044582 | ||
|
|
d46fb519d5 | ||
|
|
832672149b | ||
|
|
3941930c2f | ||
|
|
4ab43cc923 | ||
|
|
cb0cb37e3c | ||
|
|
df4ebd6c8c | ||
|
|
647aa7ecf2 | ||
|
|
6aaf18cb27 | ||
|
|
08e295972c | ||
|
|
b4a7c5d47a | ||
|
|
4226976821 | ||
|
|
38bc58e77c | ||
|
|
386b0bc232 | ||
|
|
487471c2d1 | ||
|
|
e46cfe1601 | ||
|
|
fc4eb664e2 | ||
|
|
6c92d246c8 | ||
|
|
e86bd33a29 | ||
|
|
c4787b63a8 | ||
|
|
6b305349c9 | ||
|
|
3c16779d68 | ||
|
|
38fcadf649 | ||
|
|
6cc50013be | ||
|
|
bb7c9ad7fa | ||
|
|
b53d838b14 | ||
|
|
e03831586e | ||
|
|
6acb8f8b1b | ||
|
|
29bf2cbbc4 | ||
|
|
4fc238864b | ||
|
|
cb8d2727f4 | ||
|
|
9ef433d3ed | ||
|
|
02de3cd437 | ||
|
|
e7211063e7 | ||
|
|
e32ee1a58e | ||
|
|
1eb5859f01 | ||
|
|
ff7c3dc45c | ||
|
|
655b5974a3 | ||
|
|
47d7c7cf6c | ||
|
|
1c39deae6c | ||
|
|
6c4dc7fc72 | ||
|
|
3c5618364c | ||
|
|
bf1cc8fd7c | ||
|
|
72d4d50788 | ||
|
|
6a86fcdeeb | ||
|
|
dad95ad73a | ||
|
|
77f915acf5 | ||
|
|
6fc1daf978 | ||
|
|
934f589778 | ||
|
|
305ea97d5b | ||
|
|
e3b26e80bf | ||
|
|
810ef119b0 | ||
|
|
393b27679d | ||
|
|
a592134caf | ||
|
|
2389df7c6f | ||
|
|
422202348e | ||
|
|
284e08ffd9 | ||
|
|
2ce3d04bba | ||
|
|
8e67bfe990 | ||
|
|
27dc9510a3 | ||
|
|
d4708cb144 | ||
|
|
986de32043 | ||
|
|
4e90c291b3 | ||
|
|
7775767687 | ||
|
|
5624476e2b | ||
|
|
9ef80864b7 | ||
|
|
c32ef136a1 | ||
|
|
b2ea0fe1e2 | ||
|
|
be86b12b14 | ||
|
|
8b8e499615 | ||
|
|
36b0d38b70 | ||
|
|
78ef6921da | ||
|
|
f6ab67e92c | ||
|
|
44ffdcf429 | ||
|
|
53f4d3fab6 | ||
|
|
7dcf543443 | ||
|
|
4e445ee313 | ||
|
|
c6be3e145a | ||
|
|
f90526be22 | ||
|
|
df09bc2cf0 | ||
|
|
a1affcfd3f | ||
|
|
a402f3a880 | ||
|
|
b0fe9fb1a4 | ||
|
|
5057c91dcd | ||
|
|
84a80db21f | ||
|
|
8657e5516b | ||
|
|
6b24da6990 | ||
|
|
0ee87bc771 | ||
|
|
6e35c89740 | ||
|
|
9d69b330b5 | ||
|
|
22b3f3148c | ||
|
|
b73b3007f2 | ||
|
|
37dd4b0893 | ||
|
|
07c5a38582 | ||
|
|
c866c6b59b | ||
|
|
c3e97cb97e | ||
|
|
52ffb97369 | ||
|
|
bad5657725 | ||
|
|
f8dc3c8a0e | ||
|
|
77493b5d14 | ||
|
|
b1ceafbe4f | ||
|
|
41e9d9b08b | ||
|
|
dc32549c75 | ||
|
|
17dd3af3c7 | ||
|
|
32bdc19fe9 | ||
|
|
70fbefbe62 | ||
|
|
64cd6ea9be | ||
|
|
aa92c2444d | ||
|
|
9b19626f1b | ||
|
|
2bbe5193ee | ||
|
|
d11318f082 | ||
|
|
237f948d99 | ||
|
|
f3449be1e9 | ||
|
|
b95ffda711 | ||
|
|
0932fa0058 | ||
|
|
da2e46b81b | ||
|
|
d4a1fcc9c6 | ||
|
|
c7e7f4f28f | ||
|
|
87e191b8d7 | ||
|
|
05b21fcfcc | ||
|
|
48e4ae87d8 | ||
|
|
8b090034f1 | ||
|
|
41c183ac09 | ||
|
|
4380b95451 | ||
|
|
90fc486a20 | ||
|
|
f254153962 | ||
|
|
a2538d1905 | ||
|
|
5d7a2879a7 | ||
|
|
986adf6494 | ||
|
|
15932fb9aa | ||
|
|
3ae4bb03f5 | ||
|
|
7f47478997 | ||
|
|
a2923450ea | ||
|
|
5366b9a35b | ||
|
|
5ca37c68e2 | ||
|
|
22aa274d03 | ||
|
|
54bbe55bb9 | ||
|
|
9706576e66 | ||
|
|
b2ccd65da4 | ||
|
|
c587cd2e12 | ||
|
|
ba23a989a9 | ||
|
|
c6c69668c8 | ||
|
|
b0a4593f44 | ||
|
|
2d6e699e2c | ||
|
|
4307509402 | ||
|
|
ed165b1e4c | ||
|
|
3503300a23 | ||
|
|
3ab3d255ed | ||
|
|
fd625e0b30 | ||
|
|
53af37cd56 | ||
|
|
6bf4945181 | ||
|
|
868320c513 | ||
|
|
577b836420 | ||
|
|
4a36b52657 | ||
|
|
8dc944d22d | ||
|
|
0a10170155 | ||
|
|
dfa2c222d1 | ||
|
|
e288af162c | ||
|
|
6280820c86 | ||
|
|
102b31f83d | ||
|
|
37ca9ca19a | ||
|
|
0c151ad456 | ||
|
|
55e3f749b4 | ||
|
|
16902d7fbc | ||
|
|
c37f53e083 | ||
|
|
4cc499afdf | ||
|
|
48895efad7 | ||
|
|
eb46b04c0e | ||
|
|
442de12b99 | ||
|
|
712c1f3559 | ||
|
|
5a5cfa1eba | ||
|
|
7988386bc3 | ||
|
|
76bfe2905d | ||
|
|
23893bcacd | ||
|
|
c1da159e8f | ||
|
|
13889acb37 | ||
|
|
ffb3de3f2c | ||
|
|
87cd45fc64 | ||
|
|
1cf914813c | ||
|
|
86d61f8d03 | ||
|
|
8f638fbf9e | ||
|
|
95ac134a07 | ||
|
|
4e65f9b2a1 | ||
|
|
d60bbe9fe9 | ||
|
|
dec1495e1e | ||
|
|
dc72c6fe22 | ||
|
|
141b1205c6 | ||
|
|
65f4a09ad5 | ||
|
|
3d1545c0b9 | ||
|
|
ec26dd622b | ||
|
|
0ab3131964 | ||
|
|
588a9e9f63 | ||
|
|
f2fa930edd | ||
|
|
ec47229a37 | ||
|
|
bf5d1d58a8 | ||
|
|
8b4da24ee7 | ||
|
|
3fba108d70 | ||
|
|
35029f0eed | ||
|
|
b66ab9d7c6 | ||
|
|
5e0b745ba9 | ||
|
|
45d906ba7e | ||
|
|
24b124fb17 | ||
|
|
7cb0978468 | ||
|
|
44cb135a1d | ||
|
|
146b673203 | ||
|
|
b0d23c5665 | ||
|
|
68f89c8958 | ||
|
|
1327d6bf25 | ||
|
|
237e37ff30 | ||
|
|
4d707c86cb | ||
|
|
c7027c6e00 | ||
|
|
81ec61bcc8 | ||
|
|
649178fa54 | ||
|
|
9ff46b9aad | ||
|
|
cdcbb60ca7 | ||
|
|
d8cdb2b312 | ||
|
|
fa1c1b8e6e | ||
|
|
2ded835602 | ||
|
|
a43f04818d | ||
|
|
278dc60119 | ||
|
|
8a202be0cf | ||
|
|
58940a3cd7 | ||
|
|
aa9721d146 | ||
|
|
79f114ad5b | ||
|
|
197a6ddd9d | ||
|
|
4a4fb15e06 | ||
|
|
e7e83ce6e8 | ||
|
|
a82b293452 | ||
|
|
bb483a3a38 | ||
|
|
2ca3d0e395 | ||
|
|
bda79b1e82 | ||
|
|
4f4b754e2d | ||
|
|
214cabac43 | ||
|
|
7b6229c222 | ||
|
|
1a14a75897 | ||
|
|
76b75fd9b3 | ||
|
|
b969fd22f7 | ||
|
|
8d27d091af | ||
|
|
1eb7393a60 | ||
|
|
4cf88507c2 | ||
|
|
3565d8b321 | ||
|
|
7094c29b2e | ||
|
|
63004b270f | ||
|
|
6714a99b38 | ||
|
|
4c3b8df1e7 | ||
|
|
4bb695121f | ||
|
|
09fd47c421 | ||
|
|
d201d9c688 | ||
|
|
fd1e25c584 | ||
|
|
6bb66ae70e | ||
|
|
cc82d6b1d9 | ||
|
|
956be749b6 | ||
|
|
6585a00608 | ||
|
|
c0525f710f | ||
|
|
d3672807d2 | ||
|
|
60f18d5f36 | ||
|
|
e72d3de256 | ||
|
|
ba9846b9c4 | ||
|
|
09586284dc | ||
|
|
a1ee4374b2 | ||
|
|
4de6f25f11 | ||
|
|
582d8a5587 | ||
|
|
ec53b04f99 | ||
|
|
950fc94408 | ||
|
|
58d12e6e09 | ||
|
|
94323005c4 | ||
|
|
4989842057 | ||
|
|
80172636a8 | ||
|
|
8491f7be24 |
58
.github/workflows/benchmarks.yml
vendored
58
.github/workflows/benchmarks.yml
vendored
@@ -1,33 +1,41 @@
|
||||
name: Benchmarks
|
||||
#on:
|
||||
# push
|
||||
#schedule:
|
||||
# - cron: '0 5 * * *'
|
||||
#push:
|
||||
# tags:
|
||||
# - 'v*'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 5 * * *'
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
env:
|
||||
ROBUST_BENCHMARKS_ENABLE_SQL: 1
|
||||
ROBUST_BENCHMARKS_SQL_ADDRESS: ${{ secrets.BENCHMARKS_WRITE_ADDRESS }}
|
||||
ROBUST_BENCHMARKS_SQL_PORT: ${{ secrets.BENCHMARKS_WRITE_PORT }}
|
||||
ROBUST_BENCHMARKS_SQL_USER: ${{ secrets.BENCHMARKS_WRITE_USER }}
|
||||
ROBUST_BENCHMARKS_SQL_PASSWORD: ${{ secrets.BENCHMARKS_WRITE_PASSWORD }}
|
||||
ROBUST_BENCHMARKS_SQL_DATABASE: benchmarks
|
||||
concurrency: benchmarks
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
name: Run Benchmarks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Run benchmark
|
||||
run: cd Robust.Benchmarks && sudo dotnet run --filter '*' --configuration Release
|
||||
- name: Run script on centcomm
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
username: robust-benchmark-runner
|
||||
key: ${{ secrets.CENTCOMM_ROBUST_BENCHMARK_RUNNER_KEY }}
|
||||
command_timeout: 100000m
|
||||
script: |
|
||||
mkdir benchmark_run_${{ github.sha }}
|
||||
cd benchmark_run_${{ github.sha }}
|
||||
git clone https://github.com/space-wizards/RobustToolbox.git repo_dir --recursive
|
||||
cd repo_dir
|
||||
git checkout ${{ github.sha }}
|
||||
cd Robust.Benchmarks
|
||||
dotnet restore
|
||||
export ROBUST_BENCHMARKS_ENABLE_SQL=1
|
||||
export ROBUST_BENCHMARKS_SQL_ADDRESS="${{ secrets.BENCHMARKS_WRITE_ADDRESS }}"
|
||||
export ROBUST_BENCHMARKS_SQL_PORT="${{ secrets.BENCHMARKS_WRITE_PORT }}"
|
||||
export ROBUST_BENCHMARKS_SQL_USER="${{ secrets.BENCHMARKS_WRITE_USER }}"
|
||||
export ROBUST_BENCHMARKS_SQL_PASSWORD="${{ secrets.BENCHMARKS_WRITE_PASSWORD }}"
|
||||
export ROBUST_BENCHMARKS_SQL_DATABASE="benchmarks"
|
||||
export GITHUB_SHA="${{ github.sha }}"
|
||||
dotnet run --filter '*' --configuration Release
|
||||
cd ../../..
|
||||
rm -rf benchmark_run_${{ github.sha }}
|
||||
|
||||
2
.github/workflows/build-test.yml
vendored
2
.github/workflows/build-test.yml
vendored
@@ -28,6 +28,6 @@ jobs:
|
||||
- name: Build
|
||||
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
|
||||
- name: Test Engine
|
||||
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -v n
|
||||
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -- NUnit.ConsoleOut=0
|
||||
|
||||
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,9 +10,6 @@
|
||||
[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 "cefglue"]
|
||||
path = cefglue
|
||||
url = https://github.com/space-wizards/cefglue.git
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
</Project>
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: b723fc532e...13bd6f1ad2
@@ -1,7 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AssemblyName>SpaceWizards.Lidgren.Network</AssemblyName>
|
||||
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<DefaultItemExcludes>Lidgren.Network/**/*</DefaultItemExcludes>
|
||||
@@ -13,6 +15,7 @@
|
||||
<Compile Include="Lidgren.Network\Lidgren.Network\**\*.cs">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
<Compile Remove="Lidgren.Network\Lidgren.Network\obj\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -26,4 +26,7 @@
|
||||
<PropertyGroup Condition="'$(EnableClientScripting)' == 'True'">
|
||||
<DefineConstants>$(DefineConstants);CLIENT_SCRIPTING</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(UseSystemSqlite)' == 'True'">
|
||||
<DefineConstants>$(DefineConstants);USE_SYSTEM_SQLITE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<PropertyGroup><Version>0.8.86</Version></PropertyGroup>
|
||||
</Project>
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
<ActualOS>MacOS</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$([MSBuild]::IsOSPlatform('FreeBSD'))">
|
||||
<PropertyGroup>
|
||||
<ActualOS>FreeBSD</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<ActualOS>Linux</ActualOS>
|
||||
@@ -30,5 +35,6 @@
|
||||
<EnableClientScripting>True</EnableClientScripting>
|
||||
<!-- Client scripting is disabled on full release builds for security and size reasons. -->
|
||||
<EnableClientScripting Condition="'$(FullRelease)' == 'True'">False</EnableClientScripting>
|
||||
<UseSystemSqlite Condition="'$(TargetOS)' == 'FreeBSD'">True</UseSystemSqlite>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<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>false</RobustUseExternalMSBuild>
|
||||
<_RobustUseExternalMSBuild>$(RobustUseExternalMSBuild)</_RobustUseExternalMSBuild>
|
||||
<_RobustUseExternalMSBuild Condition="'$(_RobustForceInternalMSBuild)' == 'true'">false</_RobustUseExternalMSBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
Submodule ManagedHttpListener deleted from ae0539e66f
@@ -10,7 +10,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static OpenToolkit.GraphicsLibraryFramework.GLFWNative;
|
||||
using static Robust.Shared.Utility.MarshalHelper;
|
||||
|
||||
namespace OpenToolkit.GraphicsLibraryFramework
|
||||
{
|
||||
@@ -211,7 +210,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// <seealso cref="GetVersion"/>
|
||||
public static unsafe string GetVersionString()
|
||||
{
|
||||
return PtrToStringUTF8(glfwGetVersionString());
|
||||
return Marshal.PtrToStringUTF8((IntPtr) glfwGetVersionString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -272,7 +271,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
{
|
||||
byte* desc;
|
||||
var code = glfwGetError(&desc);
|
||||
description = PtrToStringUTF8(desc);
|
||||
description = Marshal.PtrToStringUTF8((IntPtr) desc);
|
||||
return code;
|
||||
}
|
||||
|
||||
@@ -590,7 +589,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe string GetMonitorName(Monitor* monitor)
|
||||
{
|
||||
return PtrToStringUTF8(glfwGetMonitorName(monitor));
|
||||
return Marshal.PtrToStringUTF8((IntPtr) glfwGetMonitorName(monitor));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -902,7 +901,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe void WindowHint(WindowHintString hint, string value)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(value);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(value);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1364,7 +1363,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe string GetKeyName(Keys key, int scanCode)
|
||||
{
|
||||
return PtrToStringUTF8(glfwGetKeyName(key, scanCode));
|
||||
return Marshal.PtrToStringUTF8((IntPtr) glfwGetKeyName(key, scanCode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2278,7 +2277,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe string GetJoystickName(int jid)
|
||||
{
|
||||
return PtrToStringUTF8(glfwGetJoystickName(jid));
|
||||
return Marshal.PtrToStringUTF8((IntPtr) glfwGetJoystickName(jid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2351,7 +2350,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe string GetJoystickGUID(int jid)
|
||||
{
|
||||
return PtrToStringUTF8(glfwGetJoystickGUID(jid));
|
||||
return Marshal.PtrToStringUTF8((IntPtr) glfwGetJoystickGUID(jid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2509,7 +2508,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe bool UpdateGamepadMappings(string newMapping)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(newMapping);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(newMapping);
|
||||
try
|
||||
{
|
||||
return glfwUpdateGamepadMappings((byte*)ptr) == GLFW_TRUE;
|
||||
@@ -2585,7 +2584,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe string GetGamepadName(int jid)
|
||||
{
|
||||
return PtrToStringUTF8(glfwGetGamepadName(jid));
|
||||
return Marshal.PtrToStringUTF8((IntPtr) glfwGetGamepadName(jid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2854,7 +2853,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe bool ExtensionSupported(string extensionName)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(extensionName);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(extensionName);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -2893,7 +2892,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// <seealso cref="ExtensionSupported" />
|
||||
public static unsafe IntPtr GetProcAddress(string procName)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(procName);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(procName);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -3085,7 +3084,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe Window* CreateWindow(int width, int height, string title, Monitor* monitor, Window* share)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(title);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(title);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -3306,7 +3305,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// <seealso cref="SetClipboardString"/>
|
||||
public static unsafe string GetClipboardString(Window* window)
|
||||
{
|
||||
return PtrToStringUTF8(glfwGetClipboardString(window));
|
||||
return Marshal.PtrToStringUTF8((IntPtr) glfwGetClipboardString(window));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -3917,7 +3916,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// <seealso cref="GetClipboardString"/>
|
||||
public static unsafe void SetClipboardString(Window* window, string data)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(data);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(data);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -4751,7 +4750,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// </remarks>
|
||||
public static unsafe void SetWindowTitle(Window* window, string title)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(title);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(title);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -5340,7 +5339,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
var array = new string[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = PtrToStringUTF8(ptr[i]);
|
||||
array[i] = Marshal.PtrToStringUTF8((IntPtr) ptr[i]);
|
||||
}
|
||||
|
||||
return array;
|
||||
@@ -5386,7 +5385,7 @@ namespace OpenToolkit.GraphicsLibraryFramework
|
||||
/// <returns>The address of the function, or <c>null</c> if an error occurred.</returns>
|
||||
public static unsafe IntPtr GetInstanceProcAddress(VkHandle instance, string procName)
|
||||
{
|
||||
var ptr = StringToCoTaskMemUTF8(procName);
|
||||
var ptr = Marshal.StringToCoTaskMemUTF8(procName);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.DefineConstants.targets" />
|
||||
@@ -14,7 +14,4 @@
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="System.Memory" Version="4.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
412
RELEASE-NOTES.md
Normal file
412
RELEASE-NOTES.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# Release notes for RobustToolbox.
|
||||
|
||||
<!--
|
||||
NOTE: automatically updated sometimes by version.py.
|
||||
Don't change the format without looking at the script!
|
||||
-->
|
||||
|
||||
<!--START TEMPLATE
|
||||
## Master
|
||||
|
||||
### Breaking changes
|
||||
|
||||
*None yet*
|
||||
|
||||
### New features
|
||||
|
||||
*None yet*
|
||||
|
||||
### Bugfixes
|
||||
|
||||
*None yet*
|
||||
|
||||
### Other
|
||||
|
||||
*None yet*
|
||||
|
||||
### Internal
|
||||
|
||||
*None yet*
|
||||
|
||||
|
||||
END TEMPLATE-->
|
||||
|
||||
## Master
|
||||
|
||||
### Breaking changes
|
||||
|
||||
*None yet*
|
||||
|
||||
### New features
|
||||
|
||||
*None yet*
|
||||
|
||||
### Bugfixes
|
||||
|
||||
*None yet*
|
||||
|
||||
### Other
|
||||
|
||||
*None yet*
|
||||
|
||||
### Internal
|
||||
|
||||
*None yet*
|
||||
|
||||
|
||||
## 0.65.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Rename transform lerping properties alongside other minor internal changes.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix physics testbeds.
|
||||
* Force grids to always be collidable for now and stop them clipping.
|
||||
|
||||
### Other
|
||||
|
||||
* Slight optimization to `OutputPanel`'s handling of internal `RichTextEntry`s.
|
||||
* Force non-collidable contacts to be destroyed. Previously these hung around until both entities became collidable again.
|
||||
|
||||
### Internal
|
||||
|
||||
* `Tools/version.py` has been updated to automatically update `RELEASE-NOTES.md`.
|
||||
* General cleanup to `Tools/version.py`.
|
||||
|
||||
## 0.64.1.0
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Word-wrapping in `OutputPanel` and `RichTextLabel` has been fixed.
|
||||
|
||||
## 0.64.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* IMapLoader has been refactored into MapLoaderSystem. The API is similar for now but is subject to change in the future.
|
||||
|
||||
## 0.63.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Thanks to new IME support with SDL2, `IClyde.TextInputStart()` and `IClyde.TextInputStop()` must now be appropriately called to start/stop receiving text input when focusing/unfocusing a UI control. This restriction is applied even on the (default) GLFW backend, to enforce consistent usage of these APIs.
|
||||
* `[GUI]TextEventArgs` have been renamed to `[GUI]TextEnteredEventArgs`, turned into records, and made to carry a `string` rather than a single text `Rune`.
|
||||
* IoC and `DependencyCollection` `Register` methods now have a `TInterface : class` constraint.
|
||||
* [ABI] `IoCManager.InitThread` now returns the `IDependencyCollection`.
|
||||
|
||||
### New features
|
||||
|
||||
* Fixes for compiling & running on .NET 7. You'll still have to edit a bunch of project files to enable this though.
|
||||
* `FormattedMessage.EnumerateRunes()`
|
||||
* `OSWindow.Shown()` virtual function for child classes to hook into.
|
||||
* `IUserInterfaceManager.DeferAction(...)` for running UI logic "not right now because that would cause an enumeration exception".
|
||||
* New `TextEdit` control for multi-line editable text, complete with word-wrapping!
|
||||
* `Rope` data structure for representing large editable text, used by the new `TextEdit`.
|
||||
* Robust now has IME support matching SDL2's API. This only works on the SDL2 backend (which is not currently enabled by default) but the API is there:
|
||||
* `IClyde.TextInputStart()`, `IClyde.TextInputStop()`, `IClyde.TextInputSetRect()` APIs to control text input behavior.
|
||||
* `TextEditing` events for reporting in-progress IME compositions.
|
||||
* `LineEdit` and `TextEdit` have functional IME support when the game is running on SDL2. If you provide a font file with the relevant glyphs, CJK text input should now be usable.
|
||||
* `Register<T>` (single type parameter) extension method for `IDependencyCollection`.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixes erroneous literal "\\n" inside the Clyde debug panel.
|
||||
* Fixed Lidgren connection status changes potentially getting mislogged.
|
||||
* Fixed missing components not being correctly saved for maps
|
||||
* Fixed map saving sometimes not including new components.
|
||||
* Fix hot reload unit tests.
|
||||
|
||||
### Other
|
||||
|
||||
* Properly re-use `HttpClient` in `NetManager` meaning we properly pool connections to the auth server, improving performance.
|
||||
* Hub advertisements have extended keep-alive pool timeout, so the connection can be kept active between advertisements.
|
||||
* All HTTP requests from the engine now have appropriate `User-Agent` header.
|
||||
* `bind` command has been made somewhat more clear thanks to a bit of help text and some basic completions.
|
||||
* `BoundKeyEventArgs` and derivatives now have a `[DebuggerDisplay]`.
|
||||
* Text cursors now have a fancy blinking animation.
|
||||
* `SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH` is set on the SDL2 windowing backend, so clicking on the game window to focus it will pass clicks through into the game itself, matching GLFW's behavior.
|
||||
* Windows clipboard history paste now works.
|
||||
* Improved multi-window UI keyboard focusing system: a single focused control is now tracked per UI root (OS window), and is saved/restored when switching between focused window. This means that you (ideally) only ever have a UI control focused on the current OS window.
|
||||
|
||||
### Internal
|
||||
|
||||
* `uitest2` is a new command that's like `uitest` but opens an OS window instead. It can also be passed an argument to open a specific tab immediately.
|
||||
* Word-wrapping logic has been split off from `RichTextEntry`, into a new helper struct `WordWrap`.
|
||||
* Some internal logic in `LineEdit` has been shared with `TextEdit` by moving it to a new `TextEditShared` file.
|
||||
* SDL2 backend now uses `[UnmanagedCallersOnly]` instead of `GetFunctionPointerForDelegate`-style P/Invoke marshalling.
|
||||
* Entity prototype reloading logic has been moved out of `PrototypeManager` and into a new `PrototypeReloadSystem`.
|
||||
|
||||
## 0.62.1.0
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed a PVS issue causing entities to be sent to clients without first sending their parents.
|
||||
* Improved client-side state handling exception tolerance.
|
||||
|
||||
### Other
|
||||
|
||||
* Removed null-space map entities.
|
||||
|
||||
### Internal
|
||||
|
||||
* Added some more anchoring tests.
|
||||
|
||||
## 0.62.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed sprites not animating when directly toggling layer visibility,
|
||||
* Fixed anchored entities not being added to the anchored lookups.
|
||||
|
||||
## 0.62.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed some obsolete map event handlers.
|
||||
|
||||
### New features
|
||||
|
||||
* Added entity query struct enumerators
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Improved error tolerance during client state application.
|
||||
* Added better error logs when a client deletes a predicted entity.
|
||||
* Fixes command permissions not getting sent to clients.
|
||||
* Fixes a broad-phase bug were entities were not properly updating their positions.
|
||||
|
||||
### Other
|
||||
|
||||
* Added the LocalizedCommands class, which automatically infer help and description loc strings from the commands name.
|
||||
|
||||
## 0.61.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* IMap and IMapGrid have been removed. Just use the associated components directly.
|
||||
|
||||
### Other
|
||||
|
||||
* AudioSystem has been refactored.
|
||||
|
||||
## 0.60.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* ISerializationHooks.BeforeSerialization() has been removed. Use custom type serializers instead.
|
||||
|
||||
### New features
|
||||
|
||||
* Added function to UserInterfaceSystem that returns list of BUIs that a client has open.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed various container related broadphase bugs which could result in entities getting stuck with a null-broadphase.
|
||||
* Fixed client fixture state handling bug that caused the client to incorrectly disable collision.
|
||||
|
||||
### Other
|
||||
|
||||
* Misc PVS optimisations
|
||||
|
||||
### Internal
|
||||
|
||||
* Removed redundant grid-init physics logic
|
||||
* Modified garbage collection for entity spawning profiling.
|
||||
|
||||
## 0.59.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Various transform related methods have been removed from MapGrids
|
||||
* TransformSystem.SetCoordinates() arguments have changed and now allow an entity to be sent to nullspace
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed an entity lookup bug that sometimes failed to return entities in StaticSundriesTrees
|
||||
|
||||
### Other
|
||||
|
||||
* The EntitySystem.Resolve<> methods have been change to protected
|
||||
|
||||
## 0.58.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed some container shutdown errors
|
||||
* Fixed LookupFlags.Static not acting as a full replacement for LookupFlags.Anchored
|
||||
|
||||
## 0.58.1.0
|
||||
|
||||
### Other
|
||||
|
||||
* Physics collision changed and body type changed events no longer get raised before initialisation
|
||||
|
||||
## 0.58.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Some TransformComponent functions have been moved to the system.
|
||||
* Container insert, remove, and shutdown function arguments and functionality has changed.
|
||||
* Physics entities without fixtures now automatically disable collision.
|
||||
|
||||
### New features
|
||||
|
||||
* Added command to profile entity spawning
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* EntityLookup/BroadphaseComponent tracking has been overhauled, which should hopefully fix various broadphase bugs.
|
||||
|
||||
### Other
|
||||
|
||||
* Component.Owner is now marked as obsolete.
|
||||
|
||||
## 0.57.0.4
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Made entity deletion more resilient against exceptions. Should fix several bugs.
|
||||
|
||||
## 0.57.0.2 and 0.57.0.3
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed more entity-lookup bugs.
|
||||
|
||||
## 0.57.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed entity lookup bug that was causing crashes.
|
||||
|
||||
### 0.57.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* EntityLookupComponent has been merged into BroadphaseComponent. The data that was previously stored in this tree is now stored across the 3 trees on BroadphaseComponent.
|
||||
|
||||
### New features
|
||||
|
||||
* EntityLookup has had its flags updated to reflect the merge of EntityLookupComponent and BroadphaseComponent, with the new flags reflecting each tree: Dynamic, Static, and Sundries. Dynamic and Static store physics bodies that are collidable and Sundries stores everything else (apart from grids).
|
||||
|
||||
### Internal
|
||||
|
||||
* EntityLookup and Broadphase have had their data de-duplicated, dropping the AABBs stored on the server by half. This also means MoveEvent updates will be much faster.
|
||||
* PVS mover updates has had their performance improved slightly.
|
||||
* Physics LinkedList nodes for contacts will no longer be re-made for every contact and will just be cleared when re-used.
|
||||
* Sprite / Light dynamictree allocations on the client have been dropped by using static lambdas.
|
||||
* The physics contact buffer for each FixtureProxy is now pooled.
|
||||
|
||||
## 0.56.1.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix PVS sometimes not sending an entity's parents.
|
||||
* Fix velocity preservation on parenting changes.
|
||||
|
||||
## 0.56.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Update pt-BR locale with more localizations
|
||||
* Separated PVS entity budget into an entity creation budget and a pvs-entry budget.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix VV type handler removal.
|
||||
* System errors during component removal should no longer result in undeletable entities.
|
||||
|
||||
### Other
|
||||
|
||||
* The ordering of component removals and shutdowns during entity deltion has changed (see #3355).
|
||||
* Improved Box2Serializer
|
||||
* Removed uses IEnumerables from EntityLookupSystem.
|
||||
* Optimized client entity spawning by 15%.
|
||||
* Modified how the rendering tree handles entity movement.
|
||||
* Improved grid enumeration allocs.
|
||||
* Fixed a bunch of build warnings (see #3329 and #3289 for details)
|
||||
|
||||
## 0.56.0.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Rename \_lib.ftl to \_engine_lib.ftl to avoid overwriting
|
||||
|
||||
## 0.56.0.1
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix instantiation of data records containing value types
|
||||
|
||||
## 0.56.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `CastShadows` moved to `SharedPointLightComponent` from clientside, now networked
|
||||
|
||||
### New features
|
||||
|
||||
* New type handler helpers added to V^3
|
||||
* Added pt-BR locale
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed audio fallback coords
|
||||
|
||||
### Other
|
||||
|
||||
* Improved PVS performance by using `for` over `forEach`
|
||||
* Improved Vec2 inverse allocations
|
||||
|
||||
## 0.55.5.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added a method to pass in physics transforms for getting nearest point.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Prevent singular sprite matrices.
|
||||
* Fix obsolete warnings in tests.
|
||||
|
||||
### Other
|
||||
|
||||
* Significantly reduce physics contact allocations.
|
||||
|
||||
## 0.55.4.1
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Removed `SI`, `SIoC`, `I`, `IoC`, `SE` and `CE` VV command prefixes.
|
||||
* `SI`, `SIoC`, `I` and `IoC` are replaced by VV paths under `/ioc/` and `/c/ioc/`.
|
||||
* `SE` and `CE` are replaced by VV paths under `/system/` and `/c/system`.
|
||||
|
||||
### New features
|
||||
|
||||
* Added CVars to control Lidgren's <abbr title="Maximum Transmission Unit">MTU</abbr> parameters:
|
||||
* `net.mtu`
|
||||
* `net.mtu_expand`
|
||||
* `net.mtu_expand_frequency`
|
||||
* `net.mtu_expand_fail_attempts`
|
||||
* Added a whole load of features to ViewVariables.
|
||||
* Added VV Paths, which allow you to refer to an object by a path, e.g. `/entity/1234/Transform/WorldPosition`
|
||||
* Added VV Domains, which allow you to add "handlers" for the top-most VV Path segment, e.g. `/entity` is a domain and so is `/player`...
|
||||
* Added VV Type Handlers, which allow you to add "custom paths" under specific types, even dynamically!
|
||||
* Added VV Path networking, which allows you to read/write/invoke paths remotely, both from server to client and from client to server.
|
||||
* Added `vvread`, `vvwrite` and `vvinvoke` commands, which allow you to read, write and invoke VV paths.
|
||||
* Added autocompletion to all VV commands.
|
||||
* Please note that the VV GUI still remains the same. It will be updated to use these new features in the future.
|
||||
|
||||
### Other
|
||||
|
||||
* Changed Lidgren to be compiled against `net6.0`. This unlocks `Half` read/write methods.
|
||||
* Lidgren has been updated to [0.2.2](https://github.com/space-wizards/SpaceWizards.Lidgren.Network/blob/v0.2.2/RELEASE-NOTES.md). Not all the changes since 0.1.0 are new here, since this is the first version where we're properly tracking this in release notes.
|
||||
* Robust.Client now uses our own [NFluidsynth](https://github.com/space-wizards/SpaceWizards.NFluidsynth) [nuget package](https://www.nuget.org/packages/SpaceWizards.NFluidsynth).
|
||||
|
||||
### Internal
|
||||
|
||||
* Renamed Lidgren's assembly to `SpaceWizards.Lidgren.Network`.
|
||||
* Rogue `obj/` folders inside Lidgren no longer break the build.
|
||||
* Renamed NFluidsynth's assembly to `SpaceWizards.NFluidsynth`
|
||||
3
Resources/EnginePrototypes/UserInterface/uiThemes.yml
Normal file
3
Resources/EnginePrototypes/UserInterface/uiThemes.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
- type: uiTheme
|
||||
id: Default
|
||||
path: /textures/interface/Default
|
||||
63
Resources/Locale/en-US/_engine_lib.ftl
Normal file
63
Resources/Locale/en-US/_engine_lib.ftl
Normal file
@@ -0,0 +1,63 @@
|
||||
# Used internally by the THE() function.
|
||||
zzzz-the = { PROPER($ent) ->
|
||||
*[false] the { $ent }
|
||||
[true] { $ent }
|
||||
}
|
||||
|
||||
# Used internally by the SUBJECT() function.
|
||||
zzzz-subject-pronoun = { GENDER($ent) ->
|
||||
[male] he
|
||||
[female] she
|
||||
[epicene] they
|
||||
*[neuter] it
|
||||
}
|
||||
|
||||
# Used internally by the OBJECT() function.
|
||||
zzzz-object-pronoun = { GENDER($ent) ->
|
||||
[male] him
|
||||
[female] her
|
||||
[epicene] them
|
||||
*[neuter] it
|
||||
}
|
||||
|
||||
# Used internally by the POSS-PRONOUN() function.
|
||||
zzzz-possessive-pronoun = { GENDER($ent) ->
|
||||
[male] his
|
||||
[female] hers
|
||||
[epicene] theirs
|
||||
*[neuter] its
|
||||
}
|
||||
|
||||
# Used internally by the POSS-ADJ() function.
|
||||
zzzz-possessive-adjective = { GENDER($ent) ->
|
||||
[male] his
|
||||
[female] her
|
||||
[epicene] their
|
||||
*[neuter] its
|
||||
}
|
||||
|
||||
# Used internally by the REFLEXIVE() function.
|
||||
zzzz-reflexive-pronoun = { GENDER($ent) ->
|
||||
[male] himself
|
||||
[female] herself
|
||||
[epicene] themselves
|
||||
*[neuter] itself
|
||||
}
|
||||
|
||||
# Used internally by the CONJUGATE-BE() function.
|
||||
zzzz-conjugate-be = { GENDER($ent) ->
|
||||
[epicene] are
|
||||
*[other] is
|
||||
}
|
||||
|
||||
# Used internally by the CONJUGATE-HAVE() function.
|
||||
zzzz-conjugate-have = { GENDER($ent) ->
|
||||
[epicene] have
|
||||
*[other] has
|
||||
}
|
||||
|
||||
# Used internally by the CONJUGATE-BASIC() function.
|
||||
zzzz-conjugate-basic = { GENDER($ent) ->
|
||||
[epicene] { $first }
|
||||
*[other] { $second }
|
||||
}
|
||||
16
Resources/Locale/en-US/client-state-commands.ftl
Normal file
16
Resources/Locale/en-US/client-state-commands.ftl
Normal file
@@ -0,0 +1,16 @@
|
||||
# Loc strings for various entity state & client-side PVS related commands
|
||||
|
||||
cmd-reset-ent-help = Usage: resetent <Entity UID>
|
||||
cmd-reset-ent-desc = Reset an entity to the most recently received server state. This will also reset entities that have been detached to null-space.
|
||||
|
||||
cmd-reset-all-ents-help = Usage: resetallents
|
||||
cmd-reset-all-ents-desc = Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
|
||||
|
||||
cmd-detach-ent-help = Usage: detachent <Entity UID>
|
||||
cmd-detach-ent-desc = Detach an entity to null-space, as if it had left PVS range.
|
||||
|
||||
cmd-local-delete-help = Usage: localdelete <Entity UID>
|
||||
cmd-local-delete-desc = Deletes an entity. Unlike the normal delete command, this is CLIENT-SIDE. Unless the entity is a client-side entity, this will likely cause errors.
|
||||
|
||||
cmd-full-state-reset-help = Usage: fullstatereset
|
||||
cmd-full-state-reset-desc = Discards any entity state information and requests a full-state from the server.
|
||||
531
Resources/Locale/en-US/commands.ftl
Normal file
531
Resources/Locale/en-US/commands.ftl
Normal file
@@ -0,0 +1,531 @@
|
||||
### Localization for engine console commands
|
||||
|
||||
## generic command errors
|
||||
|
||||
cmd-invalid-arg-number-error = Invalid number of arguments.
|
||||
|
||||
cmd-parse-failure-integer = {$arg} is not a valid integer.
|
||||
cmd-parse-failure-float = {$arg} is not a valid float.
|
||||
cmd-parse-failure-bool = {$arg} is not a valid bool.
|
||||
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
|
||||
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
|
||||
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
|
||||
|
||||
|
||||
## 'help' command
|
||||
cmd-help-desc = Display general help or help text for a specific command
|
||||
cmd-help-help = Usage: help [command name]
|
||||
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
|
||||
|
||||
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
cmd-help-unknown = Unknown command: { $command }
|
||||
cmd-help-top = { $command } - { $description }
|
||||
cmd-help-invalid-args = Invalid amount of arguments.
|
||||
cmd-help-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Gets or sets a CVar.
|
||||
cmd-cvar-help = Usage: cvar <name | ?> [value]
|
||||
If a value is passed, the value is parsed and stored as the new value of the CVar.
|
||||
If not, the current value of the CVar is displayed.
|
||||
Use 'cvar ?' to get a list of all registered CVars.
|
||||
|
||||
cmd-cvar-invalid-args = Must provide exactly one or two arguments.
|
||||
cmd-cvar-not-registered = CVar '{ $cvar }' is not registered. Use 'cvar ?' to get a list of all registered CVars.
|
||||
cmd-cvar-parse-error = Input value is in incorrect format for type { $type }
|
||||
cmd-cvar-compl-list = List available CVars
|
||||
cmd-cvar-arg-name = <name | ?>
|
||||
cmd-cvar-value-hidden = <value hidden>
|
||||
|
||||
## 'list' command
|
||||
cmd-list-desc = Lists available commands, with optional search filter
|
||||
cmd-list-help = Usage: list [filter]
|
||||
Lists all available commands. If an argument is provided, it will be used to filter commands by name.
|
||||
|
||||
cmd-list-heading = SIDE NAME DESC{"\u000A"}-------------------------{"\u000A"}
|
||||
|
||||
cmd-list-arg-filter = [filter]
|
||||
|
||||
## '>' command, aka remote exec
|
||||
cmd-remoteexec-desc = Executes server-side commands
|
||||
cmd-remoteexec-help = Usage: > <command> [arg] [arg] [arg...]
|
||||
Executes a command on the server. This is necessary if a command with the same name exists on the client, as simply running the command would run the client command first.
|
||||
|
||||
## 'gc' command
|
||||
cmd-gc-desc = Run the GC (Garbage Collector)
|
||||
cmd-gc-help = Usage: gc [generation]
|
||||
Uses GC.Collect() to execute the Garbage Collector.
|
||||
If an argument is provided, it is parsed as a GC generation number and GC.Collect(int) is used.
|
||||
Use the 'gfc' command to do an LOH-compacting full GC.
|
||||
cmd-gc-failed-parse = Failed to parse argument.
|
||||
cmd-gc-arg-generation = [generation]
|
||||
|
||||
## 'gcf' command
|
||||
cmd-gcf-desc = Run the GC, fully, compacting LOH and everything.
|
||||
cmd-gcf-help = Usage: gcf
|
||||
Does a full GC.Collect(2, GCCollectionMode.Forced, true, true) while also compacting LOH.
|
||||
This will probably lock up for hundreds of milliseconds, be warned.
|
||||
|
||||
## 'gc_mode' command
|
||||
cmd-gc_mode-desc = Change/Read the GC Latency mode
|
||||
cmd-gc_mode-help = Usage: gc_mode [type]
|
||||
If no argument is provided, returns the current GC latency mode.
|
||||
If an argument is passed, it is parsed as GCLatencyMode and set as the GC latency mode.
|
||||
|
||||
cmd-gc_mode-current = current gc latency mode: { $prevMode }
|
||||
cmd-gc_mode-possible = possible modes:
|
||||
cmd-gc_mode-option = - { $mode }
|
||||
cmd-gc_mode-unknown = unknown gc latency mode: { $arg }
|
||||
cmd-gc_mode-attempt = attempting gc latency mode change: { $prevMode } -> { $mode }
|
||||
cmd-gc_mode-result = resulting gc latency mode: { $mode }
|
||||
cmd-gc_mode-arg-type = [type]
|
||||
|
||||
## 'mem' command
|
||||
cmd-mem-desc = Prints managed memory info
|
||||
cmd-mem-help = Usage: mem
|
||||
|
||||
cmd-mem-report = Heap Size: { TOSTRING($heapSize, "N0") }
|
||||
Total Allocated: { TOSTRING($totalAllocated, "N0") }
|
||||
|
||||
## 'physics' command
|
||||
cmd-physics-overlay = {$overlay} is not a recognised overlay
|
||||
|
||||
## 'lsasm' command
|
||||
cmd-lsasm-desc = Lists loaded assemblies by load context
|
||||
cmd-lsasm-help = Usage: lsasm
|
||||
|
||||
## 'exec' command
|
||||
cmd-exec-desc = Executes a script file from the game's writeable user data
|
||||
cmd-exec-help = Usage: exec <fileName>
|
||||
Each line in the file is executed as a single command, unless it starts with a #
|
||||
|
||||
cmd-exec-arg-filename = <fileName>
|
||||
|
||||
## 'dump_net_comps' command
|
||||
cmd-dump_net_comps-desc = Prints the table of networked components.
|
||||
cmd-dump_net_comps-help = Usage: dump_net-comps
|
||||
|
||||
cmd-dump_net_comps-error-writeable = Registration still writeable, network ids have not been generated.
|
||||
cmd-dump_net_comps-header = Networked Component Registrations:
|
||||
|
||||
## 'dump_event_tables' command
|
||||
cmd-dump_event_tables-desc = Prints directed event tables for an entity.
|
||||
cmd-dump_event_tables-help = Usage: dump_event_tables <entityUid>
|
||||
|
||||
cmd-dump_event_tables-missing-arg-entity = Missing entity argument
|
||||
cmd-dump_event_tables-error-entity = Invalid entity
|
||||
cmd-dump_event_tables-arg-entity = <entityUid>
|
||||
|
||||
## 'monitor' command
|
||||
cmd-monitor-desc = Toggles a debug monitor in the F3 menu.
|
||||
cmd-monitor-help = Usage: monitor <name>
|
||||
Possible monitors are: { $monitors }
|
||||
You can also use the special values "-all" and "+all" to hide or show all monitors, respectively.
|
||||
|
||||
cmd-monitor-arg-monitor = <monitor>
|
||||
cmd-monitor-invalid-name = Invalid monitor name
|
||||
cmd-monitor-arg-count = Missing monitor argument
|
||||
cmd-monitor-minus-all-hint = Hides all monitors
|
||||
cmd-monitor-plus-all-hint = Shows all monitors
|
||||
|
||||
|
||||
## 'setambientlight' command
|
||||
cmd-set-ambient-light-desc = Allows you to set the ambient light for the specified map, in SRGB.
|
||||
cmd-set-ambient-light-help = setambientlight [mapid] [r g b a]
|
||||
cmd-set-ambient-light-parse = Unable to parse args as a byte values for a color.
|
||||
|
||||
## Mapping commands
|
||||
|
||||
cmd-savemap-desc = Serializes a map to disk. Will not save a post-init map unless forced.
|
||||
cmd-savemap-help = savemap <MapID> <Path> [force]
|
||||
cmd-savemap-not-exist = Target map does not exist.
|
||||
cmd-savemap-init-warning = Attempted to save a post-init map without forcing the save.
|
||||
cmd-savemap-attempt = Attempting to save map {$mapId} to {$path}.
|
||||
cmd-savemap-success = Map successfully saved.
|
||||
cmd-hint-savemap-id = <MapID>
|
||||
cmd-hint-savemap-path = <Path>
|
||||
cmd-hint-savemap-force = [bool]
|
||||
|
||||
cmd-loadmap-desc = Loads a map from disk into the game.
|
||||
cmd-loadmap-help = loadmap <MapID> <Path> [x] [y] [rotation] [consistentUids]
|
||||
cmd-loadmap-nullspace = You cannot load into map 0.
|
||||
cmd-loadmap-exists = Map {$mapId} already exists.
|
||||
cmd-loadmap-success = Map {$mapId} has been loaded from {$path}.
|
||||
cmd-loadmap-error = An error occurred while loading map from {$path}.
|
||||
cmd-hint-loadmap-x-position = [x-position]
|
||||
cmd-hint-loadmap-y-position = [y-position]
|
||||
cmd-hint-loadmap-rotation = [rotation]
|
||||
cmd-hint-loadmap-uids = [float]
|
||||
|
||||
cmd-hint-savebp-id = <Grid EntityID>
|
||||
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
cmd-flushcookies-desc = Flush CEF cookie storage to disk
|
||||
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
|
||||
Note that the actual operation is asynchronous.
|
||||
|
||||
cmd-ldrsc-desc = Pre-caches a resource.
|
||||
cmd-ldrsc-help = Usage: ldrsc <path> <type>
|
||||
|
||||
cmd-rldrsc-desc = Reloads a resource.
|
||||
cmd-rldrsc-help = Usage: rldrsc <path> <type>
|
||||
|
||||
cmd-gridtc-desc = Gets the tile count of a grid.
|
||||
cmd-gridtc-help = Usage: gridtc <gridId>
|
||||
|
||||
|
||||
# Client-side commands
|
||||
cmd-guidump-desc = Dump GUI tree to /guidump.txt in user data.
|
||||
cmd-guidump-help = Usage: guidump
|
||||
|
||||
cmd-uitest-desc = Open a dummy UI testing window
|
||||
cmd-uitest-help = Usage: uitest
|
||||
|
||||
## 'uitest2' command
|
||||
cmd-uitest2-desc = Opens a UI control testing OS window
|
||||
cmd-uitest2-help = Usage: uitest2 <tab>
|
||||
cmd-uitest2-arg-tab = <tab>
|
||||
cmd-uitest2-error-args = Expected at most one argument
|
||||
cmd-uitest2-error-tab = Invalid tab: '{$value}'
|
||||
cmd-uitest2-title = UITest2
|
||||
|
||||
|
||||
cmd-setclipboard-desc = Sets the system clipboard
|
||||
cmd-setclipboard-help = Usage: setclipboard <text>
|
||||
|
||||
cmd-getclipboard-desc = Gets the system clipboard
|
||||
cmd-getclipboard-help = Usage: Getclipboard
|
||||
|
||||
cmd-togglelight-desc = Toggles light rendering.
|
||||
cmd-togglelight-help = Usage: togglelight
|
||||
|
||||
cmd-togglefov-desc = Toggles fov for client.
|
||||
cmd-togglefov-help = Usage: togglefov
|
||||
|
||||
cmd-togglehardfov-desc = Toggles hard fov for client. (for debugging space-station-14#2353)
|
||||
cmd-togglehardfov-help = Usage: togglehardfov
|
||||
|
||||
cmd-toggleshadows-desc = Toggles shadow rendering.
|
||||
cmd-toggleshadows-help = Usage: toggleshadows
|
||||
|
||||
cmd-togglelightbuf-desc = Toggles lighting rendering. This includes shadows but not FOV.
|
||||
cmd-togglelightbuf-help = Usage: togglelightbuf
|
||||
|
||||
cmd-chunkinfo-desc = Gets info about a chunk under your mouse cursor.
|
||||
cmd-chunkinfo-help = Usage: chunkinfo
|
||||
|
||||
cmd-rldshader-desc = Reloads all shaders.
|
||||
cmd-rldshader-help = Usage: rldshader
|
||||
|
||||
cmd-cldbglyr-desc = Toggle fov and light debug layers.
|
||||
cmd-cldbglyr-help= Usage: cldbglyr <layer>: Toggle <layer>
|
||||
cldbglyr: Turn all Layers off
|
||||
|
||||
cmd-key-info-desc = Keys key info for a key.
|
||||
cmd-key-info-help = Usage: keyinfo <Key>
|
||||
|
||||
## 'bind' command
|
||||
cmd-bind-desc = Binds an input key combination to an input command.
|
||||
cmd-bind-help = Usage: bind { cmd-bind-arg-key } { cmd-bind-arg-mode } { cmd-bind-arg-command }
|
||||
Note that this DOES NOT automatically save bindings.
|
||||
Use the 'svbind' command to save binding configuration.
|
||||
|
||||
cmd-bind-arg-key = <KeyName>
|
||||
cmd-bind-arg-mode = <BindMode>
|
||||
cmd-bind-arg-command = <InputCommand>
|
||||
|
||||
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net-draw-interp-help = Usage: net_draw_interp
|
||||
|
||||
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net-draw-interp-help = Usage: net_draw_interp
|
||||
|
||||
cmd-net-watch-ent-desc = Dumps all network updates for an EntityId to the console.
|
||||
cmd-net-watch-ent-help = Usage: net_watchent <0|EntityUid>
|
||||
|
||||
cmd-net-refresh-desc = Requests a full server state.
|
||||
cmd-net-refresh-help = Usage: net_refresh
|
||||
|
||||
cmd-net-entity-report-desc = Toggles the net entity report panel.
|
||||
cmd-net-entity-report-help = Usage: net_entityreport
|
||||
|
||||
cmd-fill-desc = Fill up the console for debugging.
|
||||
cmd-fill-help = Fills the console with some nonsense for debugging.
|
||||
|
||||
cmd-cls-desc = Clears the console.
|
||||
cmd-cls-help = Clears the debug console of all messages.
|
||||
|
||||
cmd-sendgarbage-desc = Sends garbage to the server.
|
||||
cmd-sendgarbage-help = The server will reply with 'no u'
|
||||
|
||||
cmd-loadgrid-desc = Loads a grid from a file into an existing map.
|
||||
cmd-loadgrid-help = loadgrid <MapID> <Path> [x y] [rotation] [storeUids]
|
||||
|
||||
cmd-loc-desc = Prints the absolute location of the player's entity to console.
|
||||
cmd-loc-help = loc
|
||||
|
||||
cmd-tpgrid-desc = Teleports a grid to a new location.
|
||||
cmd-tpgrid-help = tpgrid <gridId> <X> <Y> [<MapId>]
|
||||
|
||||
cmd-rmgrid-desc = Removes a grid from a map. You cannot remove the default grid.
|
||||
cmd-rmgrid-help = rmgrid <gridId>
|
||||
|
||||
cmd-mapinit-desc = Runs map init on a map.
|
||||
cmd-mapinit-help = mapinit <mapID>
|
||||
|
||||
cmd-lsmap-desc = Lists maps.
|
||||
cmd-lsmap-help = lsmap
|
||||
|
||||
cmd-lsgrid-desc = Lists grids.
|
||||
cmd-lsgrid-help = lsgrid
|
||||
|
||||
cmd-addmap-desc = Adds a new empty map to the round. If the mapID already exists, this command does nothing.
|
||||
cmd-addmap-help = addmap <mapID> [initialize]
|
||||
|
||||
cmd-rmmap-desc = Removes a map from the world. You cannot remove nullspace.
|
||||
cmd-rmmap-help = rmmap <mapId>
|
||||
|
||||
cmd-savegrid-desc = Serializes a grid to disk.
|
||||
cmd-savegrid-help = savegrid <gridID> <Path>
|
||||
|
||||
cmd-testbed-desc = Loads a physics testbed on the specified map.
|
||||
cmd-testbed-help = testbed <mapid> <test>
|
||||
|
||||
cmd-saveconfig-desc = Saves the client configuration to the config file.
|
||||
cmd-saveconfig-help = saveconfig
|
||||
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
cmd-flushcookies-desc = Flush CEF cookie storage to disk
|
||||
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
|
||||
Note that the actual operation is asynchronous.
|
||||
|
||||
## 'addcomp' command
|
||||
cmd-addcomp-desc = Adds a component to an entity.
|
||||
cmd-addcomp-help = addcomp <uid> <componentName>
|
||||
cmd-addcompc-desc = Adds a component to an entity on the client.
|
||||
cmd-addcompc-help = addcompc <uid> <componentName>
|
||||
|
||||
## 'rmcomp' command
|
||||
cmd-rmcomp-desc = Removes a component from an entity.
|
||||
cmd-rmcomp-help = rmcomp <uid> <componentName>
|
||||
cmd-rmcompc-desc = Removes a component from an entity on the client.
|
||||
cmd-rmcompc-help = rmcomp <uid> <componentName>
|
||||
|
||||
## 'addview' command
|
||||
cmd-addview-desc = Allows you to subscribe to an entity's view for debugging purposes.
|
||||
cmd-addview-help = addview <entityUid>
|
||||
cmd-addviewc-desc = Allows you to subscribe to an entity's view for debugging purposes.
|
||||
cmd-addviewc-help = addview <entityUid>
|
||||
|
||||
## 'removeview' command
|
||||
cmd-removeview-desc = Allows you to unsubscribe to an entity's view for debugging purposes.
|
||||
cmd-removeview-help = removeview <entityUid>
|
||||
|
||||
## 'loglevel' command
|
||||
cmd-loglevel-desc = Changes the log level for a provided sawmill.
|
||||
cmd-loglevel-help = Usage: loglevel <sawmill> <level>
|
||||
sawmill: A label prefixing log messages. This is the one you're setting the level for.
|
||||
level: The log level. Must match one of the values of the LogLevel enum.
|
||||
|
||||
cmd-testlog-desc = Writes a test log to a sawmill.
|
||||
cmd-testlog-help = Usage: testlog <sawmill> <level> <message>
|
||||
sawmill: A label prefixing the logged message.
|
||||
level: The log level. Must match one of the values of the LogLevel enum.
|
||||
message: The message to be logged. Wrap this in double quotes if you want to use spaces.
|
||||
|
||||
## 'vv' command
|
||||
cmd-vv-desc = Opens View Variables.
|
||||
cmd-vv-help = Usage: vv <entity ID|IoC interface name|SIoC interface name>
|
||||
|
||||
## 'showvelocities' command
|
||||
cmd-showvelocities-desc = Displays your angular and linear velocities.
|
||||
cmd-showvelocities-help = Usage: showvelocities
|
||||
|
||||
## 'setinputcontext' command
|
||||
cmd-setinputcontext-desc = Sets the active input context.
|
||||
cmd-setinputcontext-help = Usage: setinputcontext <context>
|
||||
|
||||
## 'forall' command
|
||||
cmd-forall-desc = Runs a command over all entities with a given component.
|
||||
cmd-forall-help = Usage: forall <bql query> do <command...>
|
||||
|
||||
## 'delete' command
|
||||
cmd-delete-desc = Deletes the entity with the specified ID.
|
||||
cmd-delete-help = delete <entity UID>
|
||||
|
||||
# System commands
|
||||
cmd-showtime-desc = Shows the server time.
|
||||
cmd-showtime-help = showtime
|
||||
|
||||
cmd-restart-desc = Gracefully restarts the server (not just the round).
|
||||
cmd-restart-help = restart
|
||||
|
||||
cmd-shutdown-desc = Gracefully shuts down the server.
|
||||
cmd-shutdown-help = shutdown
|
||||
|
||||
cmd-saveconfig-desc = Saves the server configuration to the config file.
|
||||
cmd-saveconfig-help = saveconfig
|
||||
|
||||
cmd-netaudit-desc = Prints into about NetMsg security.
|
||||
cmd-netaudit-help = netaudit
|
||||
|
||||
# Player commands
|
||||
cmd-tp-desc = Teleports a player to any location in the round.
|
||||
cmd-tp-help = tp <x> <y> [<mapID>]
|
||||
|
||||
cmd-tpto-desc = Teleports the current player or the specified players/entities to the location of last player/entity specified.d.
|
||||
cmd-tpto-help = tpto <username|uid> [username|uid]...
|
||||
|
||||
cmd-listplayers-desc = Lists all players currently connected.
|
||||
cmd-listplayers-help = listplayers
|
||||
|
||||
cmd-kick-desc = Kicks a connected player out of the server, disconnecting them.
|
||||
cmd-kick-help = kick <PlayerIndex> [<Reason>]
|
||||
|
||||
# Spin command
|
||||
cmd-spin-desc = Causes an entity to spin. Default entity is the attached player's parent.
|
||||
cmd-spin-help = spin velocity [drag] [entityUid]
|
||||
|
||||
# Localization command
|
||||
cmd-rldloc-desc = Reloads localization (client & server).
|
||||
cmd-rldloc-help = Usage: rldloc
|
||||
|
||||
# Debug entity controls
|
||||
cmd-spawn-desc = Spawns an entity with specific type.
|
||||
cmd-spawn-help = spawn <prototype> OR spawn <prototype> <relative entity ID> OR spawn <prototype> <x> <y>
|
||||
cmd-cspawn-desc = Spawns a client-side entity with specific type at your feet.
|
||||
cmd-cspawn-help = cspawn <entity type>
|
||||
|
||||
cmd-scale-desc = Increases or decreases an entity's size naively.
|
||||
cmd-scale-help = scale <entityUid> <float>
|
||||
|
||||
cmd-dumpentities-desc = Dump entity list.
|
||||
cmd-dumpentities-help = Dumps entity list of UIDs and prototype.
|
||||
|
||||
cmd-getcomponentregistration-desc = Gets component registration information.
|
||||
cmd-getcomponentregistration-help = Usage: getcomponentregistration <componentName>
|
||||
|
||||
cmd-showrays-desc = Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided.
|
||||
cmd-showrays-help = Usage: showrays <raylifetime>
|
||||
|
||||
cmd-disconnect-desc = Immediately disconnect from the server and go back to the main menu.
|
||||
cmd-disconnect-help = Usage: disconnect
|
||||
|
||||
cmd-entfo-desc = Displays verbose diagnostics for an entity.
|
||||
cmd-entfo-help = Usage: entfo <entityuid>
|
||||
The entity UID can be prefixed with 'c' to convert it to a client entity UID.
|
||||
|
||||
cmd-fuck-desc = Throws an exception
|
||||
cmd-fuck-help = Throws an exception
|
||||
|
||||
cmd-showpos-desc = Enables debug drawing over all entity positions in the game.
|
||||
cmd-showpos-help = Usage: showpos
|
||||
|
||||
cmd-sggcell-desc = Lists entities on a snap grid cell.
|
||||
cmd-sggcell-help = Usage: sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
|
||||
|
||||
cmd-overrideplayername-desc = Changes the name used when attempting to connect to the server.
|
||||
cmd-overrideplayername-help = Usage: overrideplayername <name>
|
||||
|
||||
cmd-showanchored-desc = Shows anchored entities on a particular tile
|
||||
cmd-showanchored-help = Usage: showanchored
|
||||
|
||||
cmd-dmetamem-desc = Dumps a type's members in a format suitable for the sandbox configuration file.
|
||||
cmd-dmetamem-help = Usage: dmetamem <type>
|
||||
|
||||
cmd-dmetamem-desc = Displays chunk bounds for the purposes of rendering.
|
||||
cmd-dmetamem-help = Usage: showchunkbb <type>
|
||||
|
||||
cmd-launchauth-desc = Load authentication tokens from launcher data to aid in testing of live servers.
|
||||
cmd-launchauth-help = Usage: launchauth <account name>
|
||||
|
||||
cmd-lightbb-desc = Toggles whether to show light bounding boxes.
|
||||
cmd-lightbb-help = Usage: lightbb
|
||||
|
||||
cmd-monitorinfo-desc = Monitors info
|
||||
cmd-monitorinfo-help = Usage: monitorinfo <id>
|
||||
|
||||
cmd-setmonitor-desc = Set monitor
|
||||
cmd-setmonitor-help = Usage: setmonitor <id>
|
||||
|
||||
cmd-physics-desc = Shows a debug physics overlay. The arg supplied specifies the overlay.
|
||||
cmd-physics-help = Usage: physics <aabbs / com / contactnormals / contactpoints / distance / joints / shapeinfo / shapes>
|
||||
|
||||
cmd-hardquit-desc = Kills the game client instantly.
|
||||
cmd-hardquit-help = Kills the game client instantly, leaving no traces. No telling the server goodbye.
|
||||
|
||||
cmd-quit-desc = Shuts down the game client gracefully.
|
||||
cmd-quit-help = Properly shuts down the game client, notifying the connected server and such.
|
||||
|
||||
cmd-csi-desc = Opens a C# interactive console.
|
||||
cmd-csi-help = Usage: csi
|
||||
|
||||
cmd-scsi-desc = Opens a C# interactive console on the server.
|
||||
cmd-scsi-help = Usage: scsi
|
||||
|
||||
cmd-watch-desc = Opens a variable watch window.
|
||||
cmd-watch-help = Usage: watch
|
||||
|
||||
cmd-showspritebb-desc = Toggle whether sprite bounds are shown
|
||||
cmd-showspritebb-help = Usage: showspritebb
|
||||
|
||||
cmd-togglelookup-desc = Shows / hides entitylookup bounds via an overlay.
|
||||
cmd-togglelookup-help = Usage: togglelookup
|
||||
|
||||
cmd-net_entityreport-desc = Toggles the net entity report panel.
|
||||
cmd-net_entityreport-help = Usage: net_entityreport
|
||||
|
||||
cmd-net_refresh-desc = Requests a full server state.
|
||||
cmd-net_refresh-help = Usage: net_refresh
|
||||
|
||||
cmd-net_graph-desc = Toggles the net statistics pannel.
|
||||
cmd-net_graph-help = Usage: net_graph
|
||||
|
||||
cmd-net_watchent-desc = Dumps all network updates for an EntityId to the console.
|
||||
cmd-net_watchent-help = Usage: net_watchent <0|EntityUid>
|
||||
|
||||
cmd-net_draw_interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net_draw_interp-help = Usage: net_draw_interp <0|EntityUid>
|
||||
|
||||
cmd-vram-desc = Displays video memory usage statics by the game.
|
||||
cmd-vram-help = Usage: vram
|
||||
|
||||
cmd-showislands-desc = Shows the current physics bodies involved in each physics island.
|
||||
cmd-showislands-help = Usage: showislands
|
||||
|
||||
cmd-showgridnodes-desc = Shows the nodes for grid split purposes.
|
||||
cmd-showgridnodes-help = Usage: showgridnodes
|
||||
|
||||
cmd-profsnap-desc = Make a profiling snapshot.
|
||||
cmd-profsnap-help = Usage: profsnap
|
||||
|
||||
cmd-devwindow-desc = Dev Window
|
||||
cmd-devwindow-help = Usage: devwindow
|
||||
|
||||
cmd-devwindow-desc = Open file
|
||||
cmd-devwindow-help = Usage: testopenfile
|
||||
|
||||
cmd-scene-desc = Immediately changes the UI scene/state.
|
||||
cmd-scene-help = Usage: scene <className>
|
||||
|
||||
cmd-szr_stats-desc = Report serializer statistics.
|
||||
cmd-szr_stats-help = Usage: szr_stats
|
||||
|
||||
cmd-hwid-desc = Returns the current HWID (HardWare ID).
|
||||
cmd-hwid-help = Usage: hwid
|
||||
|
||||
cmd-vvread-desc = Retrieve a path's value using VV (View Variables).
|
||||
cmd-vvread-desc = Usage: vvread <path>
|
||||
|
||||
cmd-vvwrite-desc = Modify a path's value using VV (View Variables).
|
||||
cmd-vvwrite-desc = Usage: vvwrite <path>
|
||||
|
||||
cmd-vv-desc = Opens View Variables (VV).
|
||||
cmd-vv-desc = Usage: vv <path|entity ID|guihover>
|
||||
|
||||
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
|
||||
cmd-vvinvoke-desc = Usage: vvinvoke <path> [arguments...]
|
||||
10
Resources/Locale/en-US/controls.ftl
Normal file
10
Resources/Locale/en-US/controls.ftl
Normal file
@@ -0,0 +1,10 @@
|
||||
color-selector-sliders-red = R
|
||||
color-selector-sliders-green = G
|
||||
color-selector-sliders-blue = B
|
||||
color-selector-sliders-hue = H
|
||||
color-selector-sliders-saturation = S
|
||||
color-selector-sliders-value = V
|
||||
color-selector-sliders-alpha = A
|
||||
|
||||
color-selector-sliders-rgb = RGB
|
||||
color-selector-sliders-hsv = HSV
|
||||
1
Resources/Locale/en-US/midi-commands.ftl
Normal file
1
Resources/Locale/en-US/midi-commands.ftl
Normal file
@@ -0,0 +1 @@
|
||||
cmd-midipanic-desc = Turns off every note for every active MIDI renderer.
|
||||
63
Resources/Locale/pt-BR/_engine_lib.ftl
Normal file
63
Resources/Locale/pt-BR/_engine_lib.ftl
Normal file
@@ -0,0 +1,63 @@
|
||||
# Used internally by the THE() function.
|
||||
zzzz-the = { PROPER($ent) ->
|
||||
*[false] a { $ent }
|
||||
[true] { $ent }
|
||||
}
|
||||
|
||||
# Used internally by the SUBJECT() function.
|
||||
zzzz-subject-pronoun = { GENDER($ent) ->
|
||||
[male] ele
|
||||
[female] ela
|
||||
[epicene] eles
|
||||
*[neuter] ele
|
||||
}
|
||||
|
||||
# Used internally by the OBJECT() function.
|
||||
zzzz-object-pronoun = { GENDER($ent) ->
|
||||
[male] ele
|
||||
[female] ela
|
||||
[epicene] eles
|
||||
*[neuter] isso
|
||||
}
|
||||
|
||||
# Used internally by the POSS-PRONOUN() function.
|
||||
zzzz-possessive-pronoun = { GENDER($ent) ->
|
||||
[male] dele
|
||||
[female] dela
|
||||
[epicene] deles
|
||||
*[neuter] dele
|
||||
}
|
||||
|
||||
# Used internally by the POSS-ADJ() function.
|
||||
zzzz-possessive-adjective = { GENDER($ent) ->
|
||||
[male] dele
|
||||
[female] dela
|
||||
[epicene] deles
|
||||
*[neuter] dele
|
||||
}
|
||||
|
||||
# Used internally by the REFLEXIVE() function.
|
||||
zzzz-reflexive-pronoun = { GENDER($ent) ->
|
||||
[male] ele mesmo
|
||||
[female] ela mesmo
|
||||
[epicene] eles mesmos
|
||||
*[neuter] ele mesmo
|
||||
}
|
||||
|
||||
# Used internally by the CONJUGATE-BE() function.
|
||||
zzzz-conjugate-be = { GENDER($ent) ->
|
||||
[epicene] é
|
||||
*[other] é
|
||||
}
|
||||
|
||||
# Used internally by the CONJUGATE-HAVE() function.
|
||||
zzzz-conjugate-have = { GENDER($ent) ->
|
||||
[epicene] tenho
|
||||
*[other] tem
|
||||
}
|
||||
|
||||
# Used internally by the CONJUGATE-BASIC() function.
|
||||
zzzz-conjugate-basic = { GENDER($ent) ->
|
||||
[epicene] { $first }
|
||||
*[other] { $second }
|
||||
}
|
||||
16
Resources/Locale/pt-BR/client-state-commands.ftl
Normal file
16
Resources/Locale/pt-BR/client-state-commands.ftl
Normal file
@@ -0,0 +1,16 @@
|
||||
# Strings Loc para vários comandos relacionados ao estado da entidade e ao PVS do lado do cliente
|
||||
|
||||
cmd-reset-ent-help = Uso: resetent <Entity UID>
|
||||
cmd-reset-ent-desc = Redefina uma entidade para o estado do servidor recebido mais recentemente. Isso também redefinirá as entidades que foram desanexadas para espaço nulo.
|
||||
|
||||
cmd-reset-all-ents-help = Uso: resetallents
|
||||
cmd-reset-all-ents-desc = Redefine todas as entidades para o estado do servidor recebido mais recentemente. Isso afeta apenas as entidades que não foram desanexadas ao espaço nulo.
|
||||
|
||||
cmd-detach-ent-help = Uso: detachent <Entity UID>
|
||||
cmd-detach-ent-desc = Desanexar uma entidade para espaço nulo, como se tivesse saído do intervalo PVS.
|
||||
|
||||
cmd-local-delete-help = Uso: localdelete <Entity UID>
|
||||
cmd-local-delete-desc = Exclui uma entidade. Ao contrário do comando delete normal, este é CLIENT-SIDE. A menos que a entidade seja uma entidade do lado do cliente, isso provavelmente causará erros.
|
||||
|
||||
cmd-full-state-reset-help = Uso: fullstatereset
|
||||
cmd-full-state-reset-desc = Descarta qualquer informação de estado da entidade e solicita um estado completo do servidor.
|
||||
161
Resources/Locale/pt-BR/commands.ftl
Normal file
161
Resources/Locale/pt-BR/commands.ftl
Normal file
@@ -0,0 +1,161 @@
|
||||
### Localization for engine console commands
|
||||
|
||||
## generic
|
||||
|
||||
cmd-invalid-arg-number-error = Número inválido de argumentos.
|
||||
|
||||
cmd-parse-failure-integer = {$arg} não é um inteiro válido.
|
||||
cmd-parse-failure-float = {$arg} não é um float válido.
|
||||
cmd-parse-failure-bool = {$arg} não é um booleano válido.
|
||||
cmd-parse-failure-uid = {$arg} não é um UID de entidade válido.
|
||||
cmd-parse-failure-entity-exist = UID {$arg} não corresponde a uma entidade existente.
|
||||
|
||||
|
||||
## 'help' command
|
||||
cmd-help-desc = Exibir ajuda geral ou texto de ajuda para um comando específico
|
||||
cmd-help-help = Uso: help [command name]
|
||||
Quando nenhum nome de comando é fornecido, exibe o texto de ajuda geral. Se um nome de comando for fornecido, exibe o texto de ajuda para esse comando.
|
||||
|
||||
cmd-help-no-args = Para exibir a ajuda de um comando específico, escreva 'help <command>'. Para listar todos os comandos disponíveis, escreva 'list'. Para procurar comandos, use 'list <filter>'.
|
||||
cmd-help-unknown = Comando desconhecido: { $command }
|
||||
cmd-help-top = { $command } - { $description }
|
||||
cmd-help-invalid-args = Quantidade de argumentos inválida.
|
||||
cmd-help-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Obtém ou define um CVar.
|
||||
cmd-cvar-help = Uso: cvar <name | ?> [value]
|
||||
Se um valor for passado, o valor será analisado e armazenado como o novo valor do CVar.
|
||||
Caso contrário, o valor atual do CVar é exibido.
|
||||
Use 'cvar ?' para obter uma lista de todos os CVars registrados.
|
||||
|
||||
cmd-cvar-invalid-args = Deve fornecer exatamente um ou dois argumentos.
|
||||
cmd-cvar-not-registered = CVar '{ $cvar }' não está registrado. Use 'cvar ?' para obter uma lista de todos os CVars registrados.
|
||||
cmd-cvar-parse-error = O valor de entrada está no formato incorreto para o tipo { $type }
|
||||
cmd-cvar-compl-list = Listar CVars disponíveis
|
||||
cmd-cvar-arg-name = <name | ?>
|
||||
cmd-cvar-value-hidden = <value hidden>
|
||||
|
||||
## 'list' command
|
||||
cmd-list-desc = Lista os comandos disponíveis, com filtro de pesquisa opcional
|
||||
cmd-list-help = Uso: list [filter]
|
||||
Lista todos os comandos disponíveis. Se um argumento for fornecido, ele será usado para filtrar comandos por nome.
|
||||
|
||||
cmd-list-heading = NOME DESC{"\u000A"}-------------------------{"\u000A"}
|
||||
|
||||
cmd-list-arg-filter = [filter]
|
||||
|
||||
## '>' command, aka remote exec
|
||||
cmd-remoteexec-desc = Executa comandos do lado do servidor
|
||||
cmd-remoteexec-help = Uso: > <command> [arg] [arg] [arg...]
|
||||
Executa um comando no servidor. Isso é necessário se um comando com o mesmo nome existir no cliente, pois a simples execução do comando executaria o comando do cliente primeiro.
|
||||
|
||||
## 'gc' command
|
||||
cmd-gc-desc = Execute o GC (coletor de lixo)
|
||||
cmd-gc-help = Uso: gc [generation]
|
||||
Usa GC.Collect() para executar o Garbage Collector.
|
||||
Se um argumento for fornecido, ele será analisado como um número de geração do GC e GC.Collect(int) será usado.
|
||||
Use o comando 'gfc' para fazer um GC completo compactando LOH.
|
||||
cmd-gc-failed-parse = Falha ao analisar o argumento.
|
||||
cmd-gc-arg-generation = [generation]
|
||||
|
||||
## 'gcf' command
|
||||
cmd-gcf-desc = Execute o GC, totalmente, compactando LOH e tudo.
|
||||
cmd-gcf-help = Uso: gcf
|
||||
Faz um GC.Collect(2, GCCollectionMode.Forced, true, true) completo enquanto também compacta LOH.
|
||||
Isso provavelmente será bloqueado por centenas de milissegundos, esteja avisado.
|
||||
|
||||
## 'gc_mode' command
|
||||
cmd-gc_mode-desc = Alterar/ler o modo de latência do GC
|
||||
cmd-gc_mode-help = Uso: gc_mode [type]
|
||||
Se nenhum argumento for fornecido, retornará o modo de latência do GC atual.
|
||||
Se um argumento for passado, ele será analisado como GCLatencyMode e definido como o modo de latência do GC.
|
||||
|
||||
cmd-gc_mode-current = modo de latência atual do gc: { $prevMode }
|
||||
cmd-gc_mode-possible = modos possíveis:
|
||||
cmd-gc_mode-option = - { $mode }
|
||||
cmd-gc_mode-unknown = modo de latência gc desconhecido: { $arg }
|
||||
cmd-gc_mode-attempt = tentando alterar o modo de latência do gc: { $prevMode } -> { $mode }
|
||||
cmd-gc_mode-result = modo de latência gc resultante: { $mode }
|
||||
cmd-gc_mode-arg-type = [type]
|
||||
|
||||
## 'mem' command
|
||||
cmd-mem-desc = Imprime informações de memória gerenciada
|
||||
cmd-mem-help = Uso: mem
|
||||
|
||||
cmd-mem-report = Tamanho da pilha: { TOSTRING($heapSize, "N0") }
|
||||
Total alocado: { TOSTRING($totalAllocated, "N0") }
|
||||
|
||||
## 'physics' command
|
||||
cmd-physics-overlay = {$overlay} não é uma sobreposição reconhecida
|
||||
|
||||
## 'lsasm' command
|
||||
cmd-lsasm-desc = Lista assemblies carregados por contexto de carregamento
|
||||
cmd-lsasm-help = Uso: lsasm
|
||||
|
||||
## 'exec' command
|
||||
cmd-exec-desc = Executa um arquivo de script dos dados de usuário graváveis do jogo
|
||||
cmd-exec-help = Uso: exec <fileName>
|
||||
Cada linha no arquivo é executada como um único comando, a menos que comece com um #
|
||||
|
||||
cmd-exec-arg-filename = <fileName>
|
||||
|
||||
## 'dump_net_comps' command
|
||||
cmd-dump_net_comps-desc = Imprime a tabela de componentes em rede.
|
||||
cmd-dump_net_comps-help = Uso: dump_net-comps
|
||||
|
||||
cmd-dump_net_comps-error-writeable = Registro ainda gravável, IDs de rede não foram gerados.
|
||||
cmd-dump_net_comps-header = Registros de componentes em rede:
|
||||
|
||||
## 'dump_event_tables' command
|
||||
cmd-dump_event_tables-desc = Imprime tabelas de eventos direcionados para uma entidade.
|
||||
cmd-dump_event_tables-help = Uso: dump_event_tables <entityUid>
|
||||
|
||||
cmd-dump_event_tables-missing-arg-entity = Argumento de entidade ausente
|
||||
cmd-dump_event_tables-error-entity = Entidade inválida
|
||||
cmd-dump_event_tables-arg-entity = <entityUid>
|
||||
|
||||
## 'monitor' command
|
||||
cmd-monitor-desc = Alterna um monitor de depuração no menu F3.
|
||||
cmd-monitor-help = Uso: monitor <name>
|
||||
Os monitores possíveis são: { $monitors }
|
||||
Você também pode usar os valores especiais "-all" e "+all" para ocultar ou mostrar todos os monitores, respectivamente.
|
||||
|
||||
cmd-monitor-arg-monitor = <monitor>
|
||||
cmd-monitor-invalid-name = Nome do monitor inválido
|
||||
cmd-monitor-arg-count = Argumento do monitor ausente
|
||||
cmd-monitor-minus-all-hint = Esconde todos os monitores
|
||||
cmd-monitor-plus-all-hint = Mostra todos os monitores
|
||||
|
||||
|
||||
## Mapping commands
|
||||
|
||||
cmd-savemap-desc = Serializa um mapa para o disco. Não salvará um mapa pós-inicialização a menos que seja forçado.
|
||||
cmd-savemap-help = savemap <MapID> <Path> [force]
|
||||
cmd-savemap-not-exist = O mapa de destino não existe.
|
||||
cmd-savemap-init-warning = Tentativa de salvar um mapa pós-inicialização sem forçar o salvamento.
|
||||
cmd-savemap-attempt = Tentando salvar o mapa {$mapId} em {$path}.
|
||||
cmd-savemap-success = Mapa salvo com sucesso.
|
||||
cmd-hint-savemap-id = <MapID>
|
||||
cmd-hint-savemap-path = <Path>
|
||||
cmd-hint-savemap-force = [bool]
|
||||
|
||||
cmd-loadmap-desc = Carrega um mapa do disco para o jogo.
|
||||
cmd-loadmap-help = loadmap <MapID> <Path> [x] [y] [rotation] [consistentUids]
|
||||
cmd-loadmap-nullspace = Você não pode carregar no mapa 0.
|
||||
cmd-loadmap-exists = Mapa {$mapId} já existe.
|
||||
cmd-loadmap-success = Mapa {$mapId} foi carregado em {$path}.
|
||||
cmd-loadmap-error = Ocorreu um erro ao carregar o mapa de {$path}.
|
||||
cmd-hint-loadmap-x-position = [x-position]
|
||||
cmd-hint-loadmap-y-position = [y-position]
|
||||
cmd-hint-loadmap-rotation = [rotation]
|
||||
cmd-hint-loadmap-uids = [float]
|
||||
|
||||
cmd-hint-savebp-id = <Grid EntityID>
|
||||
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
cmd-flushcookies-desc = Liberar o armazenamento de cookies CEF para o disco
|
||||
cmd-flushcookies-help = Isso garante que os cookies sejam salvos corretamente no disco no caso de desligamentos impróprios.
|
||||
Observe que a operação real é assíncrona.
|
||||
10
Resources/Locale/pt-BR/controls.ftl
Normal file
10
Resources/Locale/pt-BR/controls.ftl
Normal file
@@ -0,0 +1,10 @@
|
||||
color-selector-sliders-red = R
|
||||
color-selector-sliders-green = G
|
||||
color-selector-sliders-blue = B
|
||||
color-selector-sliders-hue = H
|
||||
color-selector-sliders-saturation = S
|
||||
color-selector-sliders-value = V
|
||||
color-selector-sliders-alpha = A
|
||||
|
||||
color-selector-sliders-rgb = RGB
|
||||
color-selector-sliders-hsv = HSV
|
||||
11
Resources/Locale/pt-BR/custom-controls.ftl
Normal file
11
Resources/Locale/pt-BR/custom-controls.ftl
Normal file
@@ -0,0 +1,11 @@
|
||||
## EntitySpawnWindow
|
||||
|
||||
entity-spawn-window-title = Painel de Criação de Entidade
|
||||
entity-spawn-window-search-bar-placeholder = pesquisar
|
||||
entity-spawn-window-clear-button = Limpar
|
||||
entity-spawn-window-erase-button-text = Modo Apagar
|
||||
entity-spawn-window-override-menu-tooltip = Substituir posicionamento
|
||||
|
||||
## Console
|
||||
|
||||
console-line-edit-placeholder = Comando aqui
|
||||
1
Resources/Locale/pt-BR/defaultwindow.ftl
Normal file
1
Resources/Locale/pt-BR/defaultwindow.ftl
Normal file
@@ -0,0 +1 @@
|
||||
defaultwindow-placeholder-title = Exemplo de título de janela aqui
|
||||
54
Resources/Locale/pt-BR/input.ftl
Normal file
54
Resources/Locale/pt-BR/input.ftl
Normal file
@@ -0,0 +1,54 @@
|
||||
input-key-Escape = Escape
|
||||
input-key-Control = Control
|
||||
input-key-Shift = Shift
|
||||
input-key-Alt = Alt
|
||||
input-key-Menu = Menu
|
||||
input-key-F1 = F1
|
||||
input-key-F2 = F2
|
||||
input-key-F3 = F3
|
||||
input-key-F4 = F4
|
||||
input-key-F5 = F5
|
||||
input-key-F6 = F6
|
||||
input-key-F7 = F7
|
||||
input-key-F8 = F8
|
||||
input-key-F9 = F9
|
||||
input-key-F10 = F10
|
||||
input-key-F11 = F11
|
||||
input-key-F12 = F12
|
||||
input-key-F13 = F13
|
||||
input-key-F14 = F14
|
||||
input-key-F15 = F15
|
||||
input-key-Pause = Pause
|
||||
input-key-Left = Left
|
||||
input-key-Up = Up
|
||||
input-key-Down = Down
|
||||
input-key-Right = Right
|
||||
input-key-Space = Space
|
||||
input-key-Return = Return
|
||||
input-key-NumpadEnter = Num Enter
|
||||
input-key-BackSpace = Backspace
|
||||
input-key-Tab = Tab
|
||||
input-key-PageUp = Page Up
|
||||
input-key-PageDown = Page Down
|
||||
input-key-End = End
|
||||
input-key-Home = Home
|
||||
input-key-Insert = Insert
|
||||
input-key-Delete = Delete
|
||||
input-key-MouseLeft = Mouse Left
|
||||
input-key-MouseRight = Mouse Right
|
||||
input-key-MouseMiddle = Mouse Middle
|
||||
input-key-MouseButton4 = Mouse 4
|
||||
input-key-MouseButton5 = Mouse 5
|
||||
input-key-MouseButton6 = Mouse 6
|
||||
input-key-MouseButton7 = Mouse 7
|
||||
input-key-MouseButton8 = Mouse 8
|
||||
input-key-MouseButton9 = Mouse 9
|
||||
|
||||
input-key-LSystem-win = Left Win
|
||||
input-key-RSystem-win = Right Win
|
||||
input-key-LSystem-mac = Left Cmd
|
||||
input-key-RSystem-mac = Right Cmd
|
||||
input-key-LSystem-linux = Left Meta
|
||||
input-key-RSystem-linux = Right Meta
|
||||
|
||||
input-key-unknown = <unknown key>
|
||||
1
Resources/Locale/pt-BR/midi-commands.ftl
Normal file
1
Resources/Locale/pt-BR/midi-commands.ftl
Normal file
@@ -0,0 +1 @@
|
||||
cmd-midipanic-desc = Desliga cada nota para cada renderizador MIDI ativo.
|
||||
1
Resources/Locale/pt-BR/tab-container.ftl
Normal file
1
Resources/Locale/pt-BR/tab-container.ftl
Normal file
@@ -0,0 +1 @@
|
||||
tab-container-not-tab-title-provided = Sem título
|
||||
11
Resources/Locale/pt-BR/view-variables.ftl
Normal file
11
Resources/Locale/pt-BR/view-variables.ftl
Normal file
@@ -0,0 +1,11 @@
|
||||
## ViewVariablesInstanceEntity
|
||||
|
||||
view-variable-instance-entity-server-components-add-component-button-placeholder = Adicionar Componente
|
||||
view-variable-instance-entity-client-variables-tab-title = Variávies do Cliente
|
||||
view-variable-instance-entity-client-components-tab-title = Componentes do Cliente
|
||||
view-variable-instance-entity-server-variables-tab-title = Variávies do Servidor
|
||||
view-variable-instance-entity-server-components-tab-title = Componentes do Servidor
|
||||
view-variable-instance-entity-client-components-search-bar-placeholder = Pesquisar
|
||||
view-variable-instance-entity-server-components-search-bar-placeholder = Pesquisar
|
||||
view-variable-instance-entity-add-window-server-components = Adicionar Componente [S]
|
||||
view-variable-instance-entity-add-window-client-components = Adicionar Componente [C]
|
||||
822
Robust.Analyzers.Tests/AccessAnalyzer_Test.cs
Normal file
822
Robust.Analyzers.Tests/AccessAnalyzer_Test.cs
Normal file
@@ -0,0 +1,822 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing.Verifiers;
|
||||
using NUnit.Framework;
|
||||
using Robust.Analyzers;
|
||||
|
||||
using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.AccessAnalyzer>;
|
||||
using static Microsoft.CodeAnalysis.Testing.DiagnosticResult;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
public sealed class AccessAnalyzer_Test
|
||||
{
|
||||
public Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpAnalyzerTest<AccessAnalyzer, NUnitVerifier>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
AdditionalReferences = { typeof(AccessAnalyzer).Assembly },
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
[Test]
|
||||
public async Task ReadTest()
|
||||
{
|
||||
const string code = @"
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
// ReSharper disable RedundantAssignment
|
||||
// ReSharper disable UnusedVariable
|
||||
// ReSharper disable ArrangeThisQualifier
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
|
||||
public struct MyData
|
||||
{
|
||||
public int MyField;
|
||||
|
||||
public static bool operator ==(MyData lhs, MyData rhs) => lhs.MyField == rhs.MyField;
|
||||
public static bool operator !=(MyData lhs, MyData rhs) => lhs.MyField != rhs.MyField;
|
||||
}
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.None,
|
||||
Friend = AccessPermissions.None,
|
||||
Other = AccessPermissions.None)]
|
||||
public sealed class TypeNobodyCanRead
|
||||
{
|
||||
public MyData Data = default;
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.Read,
|
||||
Friend = AccessPermissions.Read,
|
||||
Other = AccessPermissions.Read)]
|
||||
public MyData Data2 = default;
|
||||
|
||||
public void TestTypeNobodyCanRead(TypeNobodyCanRead obj)
|
||||
{
|
||||
// None of these accesses should be allowed.
|
||||
var copy = Data;
|
||||
var copy2 = this.Data;
|
||||
var copy3 = obj.Data;
|
||||
|
||||
copy = Data;
|
||||
copy = this.Data;
|
||||
copy = obj.Data;
|
||||
|
||||
var copy4 = Data.MyField;
|
||||
var copy5 = this.Data.MyField;
|
||||
var copy6 = obj.Data.MyField;
|
||||
|
||||
if (Data == copy) {}
|
||||
if (this.Data == copy) {}
|
||||
if (obj.Data == copy) {}
|
||||
|
||||
if(Data.MyField == 0) {}
|
||||
if(this.Data.MyField == 0) {}
|
||||
if(obj.Data.MyField == 0) {}
|
||||
|
||||
// All of these accesses should be fine.
|
||||
var copy7 = Data2;
|
||||
var copy8 = this.Data2;
|
||||
var copy9 = obj.Data2;
|
||||
|
||||
copy = Data2;
|
||||
copy = this.Data2;
|
||||
copy = obj.Data2;
|
||||
|
||||
var copy10 = Data2.MyField;
|
||||
var copy11 = this.Data2.MyField;
|
||||
var copy12 = obj.Data2.MyField;
|
||||
|
||||
if (Data2 == copy) {}
|
||||
if (this.Data2 == copy) {}
|
||||
if (obj.Data2 == copy) {}
|
||||
|
||||
if(Data2.MyField == 0) {}
|
||||
if(this.Data2.MyField == 0) {}
|
||||
if(obj.Data2.MyField == 0) {}
|
||||
}
|
||||
}
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.Read,
|
||||
Friend = AccessPermissions.Read,
|
||||
Other = AccessPermissions.Read)]
|
||||
public sealed class MemberNobodyCanRead
|
||||
{
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.None,
|
||||
Friend = AccessPermissions.None,
|
||||
Other = AccessPermissions.None)]
|
||||
public MyData Data = default;
|
||||
|
||||
public MyData Data2 = default;
|
||||
|
||||
public void TestMemberNobodyCanRead(TypeNobodyCanRead obj)
|
||||
{
|
||||
// None of these accesses should be allowed.
|
||||
var copy = Data;
|
||||
var copy2 = this.Data;
|
||||
var copy3 = obj.Data;
|
||||
|
||||
copy = Data;
|
||||
copy = this.Data;
|
||||
copy = obj.Data;
|
||||
|
||||
var copy4 = Data.MyField;
|
||||
var copy5 = this.Data.MyField;
|
||||
var copy6 = obj.Data.MyField;
|
||||
|
||||
if (Data == copy) {}
|
||||
if (this.Data == copy) {}
|
||||
if (obj.Data == copy) {}
|
||||
|
||||
if(Data.MyField == 0) {}
|
||||
if(this.Data.MyField == 0) {}
|
||||
if(obj.Data.MyField == 0) {}
|
||||
|
||||
// All of these accesses should be fine.
|
||||
var copy7 = Data2;
|
||||
var copy8 = this.Data2;
|
||||
var copy9 = obj.Data2;
|
||||
|
||||
copy = Data2;
|
||||
copy = this.Data2;
|
||||
copy = obj.Data2;
|
||||
|
||||
var copy10 = Data2.MyField;
|
||||
var copy11 = this.Data2.MyField;
|
||||
var copy12 = obj.Data2.MyField;
|
||||
|
||||
if (Data2 == copy) {}
|
||||
if (this.Data2 == copy) {}
|
||||
if (obj.Data2 == copy) {}
|
||||
|
||||
if(Data2.MyField == 0) {}
|
||||
if(this.Data2.MyField == 0) {}
|
||||
if(obj.Data2.MyField == 0) {}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FriendlyClass
|
||||
{
|
||||
public void TestTypeNobodyCanRead(TypeNobodyCanRead obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, even if we're a friend..
|
||||
var copy = obj.Data;
|
||||
copy = obj.Data;
|
||||
|
||||
var copy2 = obj.Data.MyField;
|
||||
copy2 = obj.Data.MyField;
|
||||
|
||||
if (obj.Data == copy) {}
|
||||
if(obj.Data.MyField == 0) {}
|
||||
|
||||
// We should be allowed to access all of these, we're friends!
|
||||
var copy3 = obj.Data2;
|
||||
copy = obj.Data2;
|
||||
|
||||
var copy4 = obj.Data2.MyField;
|
||||
copy4 = obj.Data2.MyField;
|
||||
|
||||
if(obj.Data2 == copy) {}
|
||||
if(obj.Data2.MyField == 0) {}
|
||||
}
|
||||
|
||||
public void TestMemberNobodyCanRead(MemberNobodyCanRead obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, even if we're a friend..
|
||||
var copy = obj.Data;
|
||||
copy = obj.Data;
|
||||
|
||||
var copy2 = obj.Data.MyField;
|
||||
copy2 = obj.Data.MyField;
|
||||
|
||||
if (obj.Data == copy) {}
|
||||
if(obj.Data.MyField == 0) {}
|
||||
|
||||
// We should be allowed to access all of these, we're friends!
|
||||
var copy3 = obj.Data2;
|
||||
copy = obj.Data2;
|
||||
|
||||
var copy4 = obj.Data2.MyField;
|
||||
copy4 = obj.Data2.MyField;
|
||||
|
||||
if(obj.Data2 == copy) {}
|
||||
if(obj.Data2.MyField == 0) {}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class OtherClass
|
||||
{
|
||||
public void TestTypeNobodyCanRead(TypeNobodyCanRead obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, as 'other types' can't..
|
||||
var copy = obj.Data;
|
||||
copy = obj.Data;
|
||||
|
||||
var copy2 = obj.Data.MyField;
|
||||
copy2 = obj.Data.MyField;
|
||||
|
||||
if (obj.Data == copy) {}
|
||||
if(obj.Data.MyField == 0) {}
|
||||
|
||||
// We should be allowed to access all of these, they let others read it!
|
||||
var copy3 = obj.Data2;
|
||||
copy = obj.Data2;
|
||||
|
||||
var copy4 = obj.Data2.MyField;
|
||||
copy4 = obj.Data2.MyField;
|
||||
|
||||
if(obj.Data2 == copy) {}
|
||||
if(obj.Data2.MyField == 0) {}
|
||||
}
|
||||
|
||||
public void TestMemberNobodyCanRead(MemberNobodyCanRead obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, as 'other types' can't..
|
||||
var copy = obj.Data;
|
||||
copy = obj.Data;
|
||||
|
||||
var copy2 = obj.Data.MyField;
|
||||
copy2 = obj.Data.MyField;
|
||||
|
||||
if (obj.Data == copy) {}
|
||||
if(obj.Data.MyField == 0) {}
|
||||
|
||||
// We should be allowed to access all of these, they let others read it!
|
||||
var copy3 = obj.Data2;
|
||||
copy = obj.Data2;
|
||||
|
||||
var copy4 = obj.Data2.MyField;
|
||||
copy4 = obj.Data2.MyField;
|
||||
|
||||
if(obj.Data2 == copy) {}
|
||||
if(obj.Data2.MyField == 0) {}
|
||||
}
|
||||
}";
|
||||
|
||||
await Verifier(code,
|
||||
// AUTO-GENERATED DIAGNOSTICS BELOW //
|
||||
// /0/Test0.cs(35,20): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(35, 20, 35, 24).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(36,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(36, 21, 36, 30).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(37,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(37, 21, 37, 29).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(39,16): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(39, 16, 39, 20).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(40,16): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(40, 16, 40, 25).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(41,16): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(41, 16, 41, 24).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(43,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(43, 21, 43, 25).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(44,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(44, 21, 44, 30).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(45,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(45, 21, 45, 29).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(47,13): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(47, 13, 47, 17).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(48,13): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(48, 13, 48, 22).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(49,13): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(49, 13, 49, 21).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(51,12): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(51, 12, 51, 16).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(52,12): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(52, 12, 52, 21).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(53,12): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(53, 12, 53, 20).WithArguments("a 'Read' same-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(95,20): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(95, 20, 95, 24).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(96,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(96, 21, 96, 30).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(97,21): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(97, 21, 97, 29).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(99,16): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(99, 16, 99, 20).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(100,16): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(100, 16, 100, 25).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(101,16): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(101, 16, 101, 24).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(103,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(103, 21, 103, 25).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(104,21): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(104, 21, 104, 30).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(105,21): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(105, 21, 105, 29).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(107,13): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(107, 13, 107, 17).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(108,13): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(108, 13, 108, 22).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(109,13): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(109, 13, 109, 21).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(111,12): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(111, 12, 111, 16).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(112,12): error RA0002: Tried to perform a 'Read' same-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(112, 12, 112, 21).WithArguments("a 'Read' same-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(113,12): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(113, 12, 113, 20).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(143,20): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(143, 20, 143, 28).WithArguments("a 'Read' friend-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(144,16): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(144, 16, 144, 24).WithArguments("a 'Read' friend-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(146,21): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(146, 21, 146, 29).WithArguments("a 'Read' friend-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(147,17): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(147, 17, 147, 25).WithArguments("a 'Read' friend-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(149,13): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(149, 13, 149, 21).WithArguments("a 'Read' friend-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(150,12): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(150, 12, 150, 20).WithArguments("a 'Read' friend-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(166,20): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(166, 20, 166, 28).WithArguments("a 'Read' friend-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(167,16): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(167, 16, 167, 24).WithArguments("a 'Read' friend-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(169,21): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(169, 21, 169, 29).WithArguments("a 'Read' friend-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(170,17): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(170, 17, 170, 25).WithArguments("a 'Read' friend-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(172,13): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(172, 13, 172, 21).WithArguments("a 'Read' friend-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(173,12): error RA0002: Tried to perform a 'Read' friend-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(173, 12, 173, 20).WithArguments("a 'Read' friend-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(192,20): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(192, 20, 192, 28).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(193,16): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(193, 16, 193, 24).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(195,21): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(195, 21, 195, 29).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(196,17): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(196, 17, 196, 25).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(198,13): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(198, 13, 198, 21).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(199,12): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'TypeNobodyCanRead', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(199, 12, 199, 20).WithArguments("a 'Read' other-type", "Data", "TypeNobodyCanRead", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(215,20): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(215, 20, 215, 28).WithArguments("a 'Read' other-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(216,16): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(216, 16, 216, 24).WithArguments("a 'Read' other-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(218,21): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(218, 21, 218, 29).WithArguments("a 'Read' other-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(219,17): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(219, 17, 219, 25).WithArguments("a 'Read' other-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(221,13): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(221, 13, 221, 21).WithArguments("a 'Read' other-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(222,12): error RA0002: Tried to perform a 'Read' other-type access to member 'Data' in type 'MemberNobodyCanRead', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(222, 12, 222, 20).WithArguments("a 'Read' other-type", "Data", "MemberNobodyCanRead", "having no", "Member Permissions: ---------")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WriteTest()
|
||||
{
|
||||
const string code = @"
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
// ReSharper disable RedundantAssignment
|
||||
// ReSharper disable UnusedVariable
|
||||
// ReSharper disable ArrangeThisQualifier
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable NotAccessedField.Global
|
||||
// ReSharper disable RedundantDefaultMemberInitializer
|
||||
|
||||
public struct MyData
|
||||
{
|
||||
public int MyField;
|
||||
}
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.None,
|
||||
Friend = AccessPermissions.None,
|
||||
Other = AccessPermissions.None)]
|
||||
public sealed class TypeNobodyCanWrite
|
||||
{
|
||||
public MyData Data = default;
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.Write,
|
||||
Friend = AccessPermissions.Write,
|
||||
Other = AccessPermissions.Write)]
|
||||
public MyData Data2 = default;
|
||||
|
||||
public void TestTypeNobodyCanWrite(TypeNobodyCanWrite obj)
|
||||
{
|
||||
// None of these accesses should be allowed.
|
||||
Data = default;
|
||||
this.Data = default;
|
||||
obj.Data = default;
|
||||
|
||||
Data.MyField = 0;
|
||||
this.Data.MyField = 0;
|
||||
obj.Data.MyField = 0;
|
||||
|
||||
// All of these accesses should be fine.
|
||||
Data2 = default;
|
||||
this.Data2 = default;
|
||||
obj.Data2 = default;
|
||||
|
||||
Data2.MyField = 0;
|
||||
this.Data2.MyField = 0;
|
||||
obj.Data2.MyField = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.Write,
|
||||
Friend = AccessPermissions.Write,
|
||||
Other = AccessPermissions.Write)]
|
||||
public sealed class MemberNobodyCanWrite
|
||||
{
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.None,
|
||||
Friend = AccessPermissions.None,
|
||||
Other = AccessPermissions.None)]
|
||||
public MyData Data = default;
|
||||
|
||||
public MyData Data2 = default;
|
||||
|
||||
public void TestMemberNobodyCanWrite(TypeNobodyCanWrite obj)
|
||||
{
|
||||
// None of these accesses should be allowed.
|
||||
Data = default;
|
||||
this.Data = default;
|
||||
obj.Data = default;
|
||||
|
||||
Data.MyField = 0;
|
||||
this.Data.MyField = 0;
|
||||
obj.Data.MyField = 0;
|
||||
|
||||
// All of these accesses should be fine.
|
||||
Data2 = default;
|
||||
this.Data2 = default;
|
||||
obj.Data2 = default;
|
||||
|
||||
Data2.MyField = 0;
|
||||
this.Data2.MyField = 0;
|
||||
obj.Data2.MyField = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FriendlyClass
|
||||
{
|
||||
public void TestTypeNobodyCanWrite(TypeNobodyCanWrite obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, even if we're a friend..
|
||||
obj.Data = default;
|
||||
obj.Data.MyField = 0;
|
||||
|
||||
// We should be allowed to access all of these, we're friends!
|
||||
obj.Data2 = default;
|
||||
obj.Data2.MyField = 0;
|
||||
}
|
||||
|
||||
public void TestMemberNobodyCanWrite(MemberNobodyCanWrite obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, even if we're a friend..
|
||||
obj.Data = default;
|
||||
obj.Data.MyField = 0;
|
||||
|
||||
// We should be allowed to access all of these, we're friends!
|
||||
obj.Data2 = default;
|
||||
obj.Data2.MyField = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class OtherClass
|
||||
{
|
||||
public void TestTypeNobodyCanWrite(TypeNobodyCanWrite obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, as 'other types' can't..
|
||||
obj.Data = default;
|
||||
obj.Data.MyField = 0;
|
||||
|
||||
// We should be allowed to access all of these, they let others write!
|
||||
obj.Data2 = default;
|
||||
obj.Data2.MyField = 0;
|
||||
}
|
||||
|
||||
public void TestMemberNobodyCanWrite(MemberNobodyCanWrite obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, as 'other types' can't..
|
||||
obj.Data = default;
|
||||
obj.Data.MyField = 0;
|
||||
|
||||
// We should be allowed to access all of these, they let others write!
|
||||
obj.Data2 = default;
|
||||
obj.Data2.MyField = 0;
|
||||
}
|
||||
}";
|
||||
|
||||
await Verifier(code,
|
||||
// AUTO-GENERATED DIAGNOSTICS BELOW //
|
||||
// /0/Test0.cs(34,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(34, 9, 34, 13).WithArguments("a 'Write' same-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(35,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(35, 9, 35, 18).WithArguments("a 'Write' same-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(36,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(36, 9, 36, 17).WithArguments("a 'Write' same-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(38,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(38, 9, 38, 13).WithArguments("a 'Write' same-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(39,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(39, 9, 39, 18).WithArguments("a 'Write' same-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(40,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(40, 9, 40, 17).WithArguments("a 'Write' same-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(70,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(70, 9, 70, 13).WithArguments("a 'Write' same-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(71,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(71, 9, 71, 18).WithArguments("a 'Write' same-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(72,9): error RA0002: Tried to perform a 'Write' other-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(72, 9, 72, 17).WithArguments("a 'Write' other-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(74,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(74, 9, 74, 13).WithArguments("a 'Write' same-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(75,9): error RA0002: Tried to perform a 'Write' same-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(75, 9, 75, 18).WithArguments("a 'Write' same-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(76,9): error RA0002: Tried to perform a 'Write' other-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(76, 9, 76, 17).WithArguments("a 'Write' other-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(94,9): error RA0002: Tried to perform a 'Write' friend-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(94, 9, 94, 17).WithArguments("a 'Write' friend-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(95,9): error RA0002: Tried to perform a 'Write' friend-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(95, 9, 95, 17).WithArguments("a 'Write' friend-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(105,9): error RA0002: Tried to perform a 'Write' friend-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(105, 9, 105, 17).WithArguments("a 'Write' friend-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(106,9): error RA0002: Tried to perform a 'Write' friend-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(106, 9, 106, 17).WithArguments("a 'Write' friend-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(119,9): error RA0002: Tried to perform a 'Write' other-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(119, 9, 119, 17).WithArguments("a 'Write' other-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(120,9): error RA0002: Tried to perform a 'Write' other-type access to member 'Data' in type 'TypeNobodyCanWrite', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(120, 9, 120, 17).WithArguments("a 'Write' other-type", "Data", "TypeNobodyCanWrite", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(130,9): error RA0002: Tried to perform a 'Write' other-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(130, 9, 130, 17).WithArguments("a 'Write' other-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(131,9): error RA0002: Tried to perform a 'Write' other-type access to member 'Data' in type 'MemberNobodyCanWrite', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(131, 9, 131, 17).WithArguments("a 'Write' other-type", "Data", "MemberNobodyCanWrite", "having no", "Member Permissions: ---------")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExecuteTest()
|
||||
{
|
||||
const string code = @"
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
// ReSharper disable RedundantAssignment
|
||||
// ReSharper disable UnusedVariable
|
||||
// ReSharper disable ArrangeThisQualifier
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable NotAccessedField.Global
|
||||
// ReSharper disable RedundantDefaultMemberInitializer
|
||||
// ReSharper disable ReturnValueOfPureMethodIsNotUsed
|
||||
|
||||
public struct MyData
|
||||
{
|
||||
public int MyField;
|
||||
public void MyMethod() {}
|
||||
}
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.None,
|
||||
Friend = AccessPermissions.None,
|
||||
Other = AccessPermissions.None)]
|
||||
public sealed class TypeNobodyCanExecute
|
||||
{
|
||||
public MyData Data = default;
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.Execute,
|
||||
Friend = AccessPermissions.Execute,
|
||||
Other = AccessPermissions.Execute)]
|
||||
public MyData Data2 = default;
|
||||
|
||||
public void MyMethod() {}
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.Execute,
|
||||
Friend = AccessPermissions.Execute,
|
||||
Other = AccessPermissions.Execute)]
|
||||
public void MyMethod2() {}
|
||||
|
||||
public void TestTypeNobodyCanExecute(TypeNobodyCanExecute obj)
|
||||
{
|
||||
// None of these accesses should be allowed.
|
||||
MyMethod();
|
||||
this.MyMethod();
|
||||
obj.MyMethod();
|
||||
|
||||
Data.MyMethod();
|
||||
this.Data.MyMethod();
|
||||
obj.Data.MyMethod();
|
||||
|
||||
Data.MyField.ToString();
|
||||
this.Data.MyField.ToString();
|
||||
obj.Data.MyField.ToString();
|
||||
|
||||
// All of these accesses should be fine.
|
||||
MyMethod2();
|
||||
this.MyMethod2();
|
||||
obj.MyMethod2();
|
||||
|
||||
Data2.MyMethod();
|
||||
this.Data2.MyMethod();
|
||||
obj.Data2.MyMethod();
|
||||
|
||||
Data2.MyField.ToString();
|
||||
this.Data2.ToString();
|
||||
obj.Data2.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.Execute,
|
||||
Friend = AccessPermissions.Execute,
|
||||
Other = AccessPermissions.Execute)]
|
||||
public sealed class MemberNobodyCanExecute
|
||||
{
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.None,
|
||||
Friend = AccessPermissions.None,
|
||||
Other = AccessPermissions.None)]
|
||||
public MyData Data = default;
|
||||
|
||||
public MyData Data2 = default;
|
||||
|
||||
[Access(typeof(FriendlyClass),
|
||||
Self = AccessPermissions.None,
|
||||
Friend = AccessPermissions.None,
|
||||
Other = AccessPermissions.None)]
|
||||
public void MyMethod() {}
|
||||
|
||||
public void MyMethod2() {}
|
||||
|
||||
public void TestMemberNobodyCanExecute(TypeNobodyCanExecute obj)
|
||||
{
|
||||
// None of these accesses should be allowed.
|
||||
MyMethod();
|
||||
this.MyMethod();
|
||||
obj.MyMethod();
|
||||
|
||||
Data.MyMethod();
|
||||
this.Data.MyMethod();
|
||||
obj.Data.MyMethod();
|
||||
|
||||
Data.MyField.ToString();
|
||||
this.Data.MyField.ToString();
|
||||
obj.Data.MyField.ToString();
|
||||
|
||||
// All of these accesses should be fine.
|
||||
MyMethod2();
|
||||
this.MyMethod2();
|
||||
obj.MyMethod2();
|
||||
|
||||
Data2.MyMethod();
|
||||
this.Data2.MyMethod();
|
||||
obj.Data2.MyMethod();
|
||||
|
||||
Data2.MyField.ToString();
|
||||
this.Data2.ToString();
|
||||
obj.Data2.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FriendlyClass
|
||||
{
|
||||
public void TestTypeNobodyCanExecute(TypeNobodyCanExecute obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, even if we're a friend..
|
||||
obj.MyMethod();
|
||||
obj.Data.MyMethod();
|
||||
obj.Data.MyField.ToString();
|
||||
|
||||
// We should be allowed to access all of these, we're friends!
|
||||
obj.MyMethod2();
|
||||
obj.Data2.MyMethod();
|
||||
obj.Data2.MyField.ToString();
|
||||
}
|
||||
|
||||
public void TestMemberNobodyCanExecute(MemberNobodyCanExecute obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, even if we're a friend..
|
||||
obj.MyMethod();
|
||||
obj.Data.MyMethod();
|
||||
obj.Data.MyField.ToString();
|
||||
|
||||
// We should be allowed to access all of these, we're friends!
|
||||
obj.MyMethod2();
|
||||
obj.Data2.MyMethod();
|
||||
obj.Data2.MyField.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class OtherClass
|
||||
{
|
||||
public void TestTypeNobodyCanExecute(TypeNobodyCanExecute obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, as 'other types' can't..
|
||||
obj.MyMethod();
|
||||
obj.Data.MyMethod();
|
||||
obj.Data.MyField.ToString();
|
||||
|
||||
// We should be allowed to access all of these, they let others Execute!
|
||||
obj.MyMethod2();
|
||||
obj.Data2.MyMethod();
|
||||
obj.Data2.MyField.ToString();
|
||||
}
|
||||
|
||||
public void TestMemberNobodyCanExecute(MemberNobodyCanExecute obj)
|
||||
{
|
||||
// We shouldn't be able to access any of these, as 'other types' can't..
|
||||
obj.MyMethod();
|
||||
obj.Data.MyMethod();
|
||||
obj.Data.MyField.ToString();
|
||||
|
||||
// We should be allowed to access all of these, they let others Execute!
|
||||
obj.MyMethod2();
|
||||
obj.Data2.MyMethod();
|
||||
obj.Data2.MyField.ToString();
|
||||
}
|
||||
}";
|
||||
|
||||
await Verifier(code,
|
||||
// AUTO-GENERATED DIAGNOSTICS BELOW //
|
||||
// /0/Test0.cs(44,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'MyMethod' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(44, 9, 44, 19).WithArguments("an 'Execute' same-type", "MyMethod", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(45,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'MyMethod' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(45, 9, 45, 24).WithArguments("an 'Execute' same-type", "MyMethod", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(46,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'MyMethod' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(46, 9, 46, 23).WithArguments("an 'Execute' same-type", "MyMethod", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(48,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(48, 9, 48, 13).WithArguments("an 'Execute' same-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(49,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(49, 9, 49, 18).WithArguments("an 'Execute' same-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(50,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(50, 9, 50, 17).WithArguments("an 'Execute' same-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(52,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(52, 9, 52, 13).WithArguments("an 'Execute' same-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(53,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(53, 9, 53, 18).WithArguments("an 'Execute' same-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(54,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(54, 9, 54, 17).WithArguments("an 'Execute' same-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(96,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'MyMethod' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(96, 9, 96, 19).WithArguments("an 'Execute' same-type", "MyMethod", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(97,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'MyMethod' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(97, 9, 97, 24).WithArguments("an 'Execute' same-type", "MyMethod", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(98,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'MyMethod' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(98, 9, 98, 23).WithArguments("an 'Execute' other-type", "MyMethod", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(100,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(100, 9, 100, 13).WithArguments("an 'Execute' same-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(101,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(101, 9, 101, 18).WithArguments("an 'Execute' same-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(102,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(102, 9, 102, 17).WithArguments("an 'Execute' other-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(104,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(104, 9, 104, 13).WithArguments("an 'Execute' same-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(105,9): error RA0002: Tried to perform an 'Execute' same-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(105, 9, 105, 18).WithArguments("an 'Execute' same-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(106,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(106, 9, 106, 17).WithArguments("an 'Execute' other-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(128,9): error RA0002: Tried to perform an 'Execute' friend-type access to member 'MyMethod' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(128, 9, 128, 23).WithArguments("an 'Execute' friend-type", "MyMethod", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(129,9): error RA0002: Tried to perform an 'Execute' friend-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(129, 9, 129, 17).WithArguments("an 'Execute' friend-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(130,9): error RA0002: Tried to perform an 'Execute' friend-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(130, 9, 130, 17).WithArguments("an 'Execute' friend-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(141,9): error RA0002: Tried to perform an 'Execute' friend-type access to member 'MyMethod' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(141, 9, 141, 23).WithArguments("an 'Execute' friend-type", "MyMethod", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(142,9): error RA0002: Tried to perform an 'Execute' friend-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(142, 9, 142, 17).WithArguments("an 'Execute' friend-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(143,9): error RA0002: Tried to perform an 'Execute' friend-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(143, 9, 143, 17).WithArguments("an 'Execute' friend-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(157,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'MyMethod' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(157, 9, 157, 23).WithArguments("an 'Execute' other-type", "MyMethod", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(158,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(158, 9, 158, 17).WithArguments("an 'Execute' other-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(159,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'Data' in type 'TypeNobodyCanExecute', despite having no access. Type Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(159, 9, 159, 17).WithArguments("an 'Execute' other-type", "Data", "TypeNobodyCanExecute", "having no", "Type Permissions: ---------"),
|
||||
// /0/Test0.cs(170,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'MyMethod' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(170, 9, 170, 23).WithArguments("an 'Execute' other-type", "MyMethod", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(171,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(171, 9, 171, 17).WithArguments("an 'Execute' other-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------"),
|
||||
// /0/Test0.cs(172,9): error RA0002: Tried to perform an 'Execute' other-type access to member 'Data' in type 'MemberNobodyCanExecute', despite having no access. Member Permissions: ---------
|
||||
VerifyCS.Diagnostic().WithSpan(172, 9, 172, 17).WithArguments("an 'Execute' other-type", "Data", "MemberNobodyCanExecute", "having no", "Member Permissions: ---------")
|
||||
);
|
||||
}
|
||||
}
|
||||
24
Robust.Analyzers.Tests/Robust.Analyzers.Tests.csproj
Normal file
24
Robust.Analyzers.Tests/Robust.Analyzers.Tests.csproj
Normal file
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1"/>
|
||||
<PackageReference Include="NUnit" Version="3.13.2"/>
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.0"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.3.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Analyzers\Robust.Analyzers.csproj"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
253
Robust.Analyzers/AccessAnalyzer.cs
Normal file
253
Robust.Analyzers/AccessAnalyzer.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Shared.Analyzers.Implementation;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class AccessAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string AccessAttributeType = "Robust.Shared.Analyzers.AccessAttribute";
|
||||
private const string PureAttributeType = "System.Diagnostics.Contracts.PureAttribute";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")]
|
||||
private static readonly DiagnosticDescriptor AccessRule = new (
|
||||
Diagnostics.IdAccess,
|
||||
"Invalid access",
|
||||
"Tried to perform {0} access to member '{1}' in type '{2}', despite {3} access. {4}.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to give the accessing type the correct access permissions.");
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||
ImmutableArray.Create(AccessRule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterOperationAction(CheckFriendship,
|
||||
OperationKind.FieldReference,
|
||||
OperationKind.PropertyReference,
|
||||
OperationKind.MethodReference,
|
||||
OperationKind.Invocation);
|
||||
}
|
||||
|
||||
private void CheckFriendship(OperationAnalysisContext context)
|
||||
{
|
||||
var operation = context.Operation;
|
||||
|
||||
// The symbol representing the member being accessed.
|
||||
ISymbol member;
|
||||
|
||||
// The operation to target when determining access type.
|
||||
IOperation targetAccess;
|
||||
|
||||
switch (operation)
|
||||
{
|
||||
case IMemberReferenceOperation memberRef:
|
||||
{
|
||||
member = memberRef.Member;
|
||||
targetAccess = memberRef.Parent;
|
||||
break;
|
||||
}
|
||||
|
||||
case IInvocationOperation invocation:
|
||||
{
|
||||
member = invocation.TargetMethod;
|
||||
targetAccess = invocation;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the info of the type defining the member, so we can check the attributes later...
|
||||
var accessedType = member.ContainingType;
|
||||
|
||||
// Get the attributes
|
||||
var friendAttribute = context.Compilation.GetTypeByMetadataName(AccessAttributeType);
|
||||
|
||||
// Get the type that is containing this expression, or, the type where this is happening.
|
||||
if (context.ContainingSymbol?.ContainingType is not {} accessingType)
|
||||
return;
|
||||
|
||||
// Determine which type of access is happening here... Read, write or execute?
|
||||
var accessAttempt = DetermineAccess(context, targetAccess, operation);
|
||||
|
||||
// Check whether this is a "self" access, including inheritors.
|
||||
var selfAccess = InheritsFromOrEquals(accessingType, accessedType);
|
||||
|
||||
// Helper function to deduplicate attribute-checking code.
|
||||
bool CheckAttributeFriendship(AttributeData attribute, bool isMemberAttribute)
|
||||
{
|
||||
// If the attribute isn't the friend attribute, we don't care about it.
|
||||
if (!SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, friendAttribute))
|
||||
return false;
|
||||
|
||||
var self = AccessAttribute.SelfDefaultPermissions;
|
||||
var friends = AccessAttribute.FriendDefaultPermissions;
|
||||
var others = AccessAttribute.OtherDefaultPermissions;
|
||||
|
||||
foreach (var kv in attribute.NamedArguments)
|
||||
{
|
||||
if (kv.Value.Value is not byte value)
|
||||
continue;
|
||||
|
||||
var permissions = (AccessPermissions) value;
|
||||
|
||||
switch (kv.Key)
|
||||
{
|
||||
case nameof(AccessAttribute.Self):
|
||||
{
|
||||
self = permissions;
|
||||
break;
|
||||
}
|
||||
|
||||
case nameof(AccessAttribute.Friend):
|
||||
{
|
||||
friends = permissions;
|
||||
break;
|
||||
}
|
||||
|
||||
case nameof(AccessAttribute.Other):
|
||||
{
|
||||
others = permissions;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// By default, we will check the "other" permissions unless we find we're dealing with a friend or self.
|
||||
var permissionCheck = others;
|
||||
|
||||
// Human-readable relation between accessing and accessed types.
|
||||
var accessingRelation = "other-type";
|
||||
|
||||
if (!selfAccess)
|
||||
{
|
||||
// This is not a self-access, so we need to determine whether the accessing type is a friend.
|
||||
// Check all types allowed in the friend attribute. (We assume there's only one constructor arg.)
|
||||
var types = attribute.ConstructorArguments[0].Values;
|
||||
|
||||
foreach (var constant in types)
|
||||
{
|
||||
// Check if the value is a type...
|
||||
if (constant.Value is not INamedTypeSymbol friendType)
|
||||
continue;
|
||||
|
||||
// Check if the accessing type is specified in the attribute...
|
||||
if (!InheritsFromOrEquals(accessingType, friendType))
|
||||
continue;
|
||||
|
||||
// Set the permissions check to the friend permissions!
|
||||
permissionCheck = friends;
|
||||
accessingRelation = "friend-type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Self-access, so simply set the permissions check to self.
|
||||
permissionCheck = self;
|
||||
accessingRelation = "same-type";
|
||||
}
|
||||
|
||||
// If we allow this access, return! All is good.
|
||||
if ((accessAttempt & permissionCheck) != 0)
|
||||
return true;
|
||||
|
||||
// Access denied! Report an error.
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(AccessRule, operation.Syntax.GetLocation(),
|
||||
$"a{(accessAttempt == AccessPermissions.Execute ? "n" : "")} '{accessAttempt}' {accessingRelation}",
|
||||
$"{member.Name}",
|
||||
$"{accessedType.Name}",
|
||||
$"{(permissionCheck == AccessPermissions.None ? "having no" : $"only having '{permissionCheck}'")}",
|
||||
$"{(isMemberAttribute ? "Member" : "Type")} Permissions: {self.ToUnixPermissions()}{friends.ToUnixPermissions()}{others.ToUnixPermissions()}"));
|
||||
|
||||
// Only return ONE error.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check attributes in the member first, since they take priority and can override type restrictions.
|
||||
foreach (var attribute in member.GetAttributes())
|
||||
{
|
||||
if(CheckAttributeFriendship(attribute, true))
|
||||
return;
|
||||
}
|
||||
|
||||
// Check attributes in the type containing the member last.
|
||||
foreach (var attribute in accessedType.GetAttributes())
|
||||
{
|
||||
if(CheckAttributeFriendship(attribute, false))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static AccessPermissions DetermineAccess(OperationAnalysisContext context, IOperation operation, IOperation original)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case IAssignmentOperation assign:
|
||||
{
|
||||
return assign.Target.Equals(original) ? AccessPermissions.Write : AccessPermissions.Read;
|
||||
}
|
||||
|
||||
case IInvocationOperation invoke:
|
||||
{
|
||||
var pureAttribute = context.Compilation.GetTypeByMetadataName(PureAttributeType);
|
||||
|
||||
foreach (var attribute in invoke.TargetMethod.GetAttributes())
|
||||
{
|
||||
// Pure methods are treated as read accesses.
|
||||
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, pureAttribute))
|
||||
return AccessPermissions.Read;
|
||||
}
|
||||
|
||||
return AccessPermissions.Execute;
|
||||
}
|
||||
|
||||
case IMemberReferenceOperation member:
|
||||
{
|
||||
return DetermineAccess(context, member.Parent, operation);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return AccessPermissions.Read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool InheritsFromOrEquals(INamedTypeSymbol type, INamedTypeSymbol baseType)
|
||||
{
|
||||
foreach (var otherType in GetBaseTypesAndThis(type))
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(otherType, baseType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<INamedTypeSymbol> GetBaseTypesAndThis(INamedTypeSymbol namedType)
|
||||
{
|
||||
var current = namedType;
|
||||
while (current != null)
|
||||
{
|
||||
yield return current;
|
||||
current = current.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,9 @@ public static class Diagnostics
|
||||
{
|
||||
public const string IdExplicitInterface = "RA0000";
|
||||
public const string IdSerializable = "RA0001";
|
||||
public const string IdFriend = "RA0002";
|
||||
public const string IdAccess = "RA0002";
|
||||
public const string IdExplicitVirtual = "RA0003";
|
||||
public const string IdTaskResult = "RA0004";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class FriendAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
const string FriendAttribute = "Robust.Shared.Analyzers.FriendAttribute";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")]
|
||||
private static readonly DiagnosticDescriptor Rule = new (
|
||||
Diagnostics.IdFriend,
|
||||
"Tried to access friend-only member",
|
||||
"Tried to access member \"{0}\" in class \"{1}\" which can only be accessed by friend classes",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to specify the accessing class in the friends attribute.");
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxNodeAction(CheckFriendship, SyntaxKind.SimpleMemberAccessExpression);
|
||||
}
|
||||
|
||||
private void CheckFriendship(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not MemberAccessExpressionSyntax memberAccess)
|
||||
return;
|
||||
|
||||
// We only do something if our parent is one of a few types.
|
||||
switch (context.Node.Parent)
|
||||
{
|
||||
// If we're being assigned...
|
||||
case AssignmentExpressionSyntax assignParent:
|
||||
{
|
||||
if (assignParent.Left != memberAccess)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we're being invoked...
|
||||
case InvocationExpressionSyntax:
|
||||
break;
|
||||
|
||||
// Otherwise, do nothing.
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the friend attribute
|
||||
var friendAttr = context.Compilation.GetTypeByMetadataName(FriendAttribute);
|
||||
|
||||
// Get the type that is containing this expression, or, the class where this is happening.
|
||||
if (context.ContainingSymbol?.ContainingType is not { } containingType)
|
||||
return;
|
||||
|
||||
// We check all of our children and get only the identifiers.
|
||||
foreach (var identifier in memberAccess.ChildNodes().Select(node => node as IdentifierNameSyntax))
|
||||
{
|
||||
if (identifier == null) continue;
|
||||
|
||||
// Get the type info of the identifier, so we can check the attributes...
|
||||
if (context.SemanticModel.GetTypeInfo(identifier).ConvertedType is not { } type)
|
||||
continue;
|
||||
|
||||
// Same-type access is always fine.
|
||||
if (SymbolEqualityComparer.Default.Equals(type, containingType))
|
||||
continue;
|
||||
|
||||
// Finally, get all attributes of the type, to check if we have any friend classes.
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
{
|
||||
// If the attribute isn't the friend attribute, continue.
|
||||
if (!SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, friendAttr))
|
||||
continue;
|
||||
|
||||
// Check all types allowed in the friend attribute. (We assume there's only one constructor arg.)
|
||||
foreach (var constant in attribute.ConstructorArguments[0].Values)
|
||||
{
|
||||
// Check if the value is a type...
|
||||
if (constant.Value is not INamedTypeSymbol t)
|
||||
continue;
|
||||
|
||||
// If we find that the containing class is specified in the attribute, return! All is good.
|
||||
if (InheritsFromOrEquals(containingType, t))
|
||||
return;
|
||||
}
|
||||
|
||||
// Not in a friend class! Report an error.
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(Rule, context.Node.GetLocation(),
|
||||
$"{context.Node.ToString().Split('.').LastOrDefault()}", $"{type.Name}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool InheritsFromOrEquals(INamedTypeSymbol type, INamedTypeSymbol baseType)
|
||||
{
|
||||
foreach (var otherType in GetBaseTypesAndThis(type))
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(otherType, baseType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<INamedTypeSymbol> GetBaseTypesAndThis(INamedTypeSymbol namedType)
|
||||
{
|
||||
var current = namedType;
|
||||
while (current != null)
|
||||
{
|
||||
yield return current;
|
||||
current = current.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,10 @@
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for FriendAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" />
|
||||
<Compile Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
44
Robust.Analyzers/TaskResultAnalyzer.cs
Normal file
44
Robust.Analyzers/TaskResultAnalyzer.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class TaskResultAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
[SuppressMessage("ReSharper", "RS2008")]
|
||||
private static readonly DiagnosticDescriptor ResultRule = new DiagnosticDescriptor(
|
||||
Diagnostics.IdTaskResult,
|
||||
"Risk of deadlock from accessing Task<T>.Result",
|
||||
"Accessing Task<T>.Result is dangerous and can cause deadlocks in some contexts. If you understand how this works and are certain that you aren't causing a deadlock here, mute this error with #pragma.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||
ImmutableArray.Create(ResultRule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterOperationAction(Check, OperationKind.PropertyReference);
|
||||
}
|
||||
|
||||
private static void Check(OperationAnalysisContext context)
|
||||
{
|
||||
var taskType = context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
|
||||
|
||||
var operation = (IPropertyReferenceOperation) context.Operation;
|
||||
var member = operation.Member;
|
||||
|
||||
if (member.Name == "Result" && taskType.Equals(member.ContainingType.ConstructedFrom, SymbolEqualityComparer.Default))
|
||||
{
|
||||
var diag = Diagnostic.Create(ResultRule, operation.Syntax.GetLocation());
|
||||
context.ReportDiagnostic(diag);
|
||||
}
|
||||
}
|
||||
}
|
||||
225
Robust.Benchmarks/EntityManager/ComponentIndexBenchmark.cs
Normal file
225
Robust.Benchmarks/EntityManager/ComponentIndexBenchmark.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
public class ComponentIndexBenchmark
|
||||
{
|
||||
// Just a bunch of types to bloat the test lists.
|
||||
|
||||
private readonly CompIndexFetcher _compIndexFetcherDirect;
|
||||
private readonly IFetcher _compIndexFetcher;
|
||||
private readonly DictFetcher _dictFetcherDirect;
|
||||
private readonly IFetcher _dictFetcher;
|
||||
|
||||
|
||||
public ComponentIndexBenchmark()
|
||||
{
|
||||
_compIndexFetcherDirect = new CompIndexFetcher();
|
||||
_compIndexFetcher = _compIndexFetcherDirect;
|
||||
_dictFetcherDirect = new DictFetcher();
|
||||
_dictFetcher = _dictFetcherDirect;
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var types = typeof(ComponentIndexBenchmark)
|
||||
.GetNestedTypes(BindingFlags.NonPublic)
|
||||
.Where(t => t.Name.StartsWith("TestType"))
|
||||
.ToArray();
|
||||
|
||||
_compIndexFetcher.Init(types);
|
||||
_dictFetcher.Init(types);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int BenchCompIndex() => _compIndexFetcher.Get<TestType50>();
|
||||
|
||||
[Benchmark]
|
||||
public int BenchDict() => _dictFetcher.Get<TestType50>();
|
||||
|
||||
[Benchmark]
|
||||
public int BenchCompIndexDirect() => _compIndexFetcherDirect.Get<TestType50>();
|
||||
|
||||
[Benchmark]
|
||||
public int BenchDictDirect() => _dictFetcherDirect.Get<TestType50>();
|
||||
|
||||
private static CompIdx ArrayIndexFor<T>() => CompArrayIndex<T>.Idx;
|
||||
|
||||
private static int _compIndexMaster = -1;
|
||||
|
||||
private static class CompArrayIndex<T>
|
||||
{
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
public static readonly CompIdx Idx = new(Interlocked.Increment(ref _compIndexMaster));
|
||||
}
|
||||
|
||||
private static CompIdx GetCompIdIndex(Type type)
|
||||
{
|
||||
return (CompIdx)typeof(CompArrayIndex<>)
|
||||
.MakeGenericType(type)
|
||||
.GetField(nameof(CompArrayIndex<int>.Idx), BindingFlags.Static | BindingFlags.Public)!
|
||||
.GetValue(null)!;
|
||||
}
|
||||
|
||||
private interface IFetcher
|
||||
{
|
||||
void Init(Type[] types);
|
||||
|
||||
int Get<T>();
|
||||
}
|
||||
|
||||
private sealed class CompIndexFetcher : IFetcher
|
||||
{
|
||||
private int[] _values = Array.Empty<int>();
|
||||
|
||||
public void Init(Type[] types)
|
||||
{
|
||||
var max = types.Max(t => GetCompIdIndex(t).Value);
|
||||
|
||||
_values = new int[max + 1];
|
||||
|
||||
var i = 0;
|
||||
foreach (var type in types)
|
||||
{
|
||||
_values[GetCompIdIndex(type).Value] = i++;
|
||||
}
|
||||
}
|
||||
|
||||
public int Get<T>()
|
||||
{
|
||||
return _values[CompArrayIndex<T>.Idx.Value];
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DictFetcher : IFetcher
|
||||
{
|
||||
private readonly Dictionary<Type, int> _values = new();
|
||||
|
||||
public void Init(Type[] types)
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var type in types)
|
||||
{
|
||||
_values[type] = i++;
|
||||
}
|
||||
}
|
||||
|
||||
public int Get<T>()
|
||||
{
|
||||
return _values[typeof(T)];
|
||||
}
|
||||
}
|
||||
|
||||
// Just a bunch of types to pad the size of the arrays and such.
|
||||
|
||||
// @formatter:off
|
||||
// ReSharper disable UnusedType.Local
|
||||
private sealed class TestType1{}
|
||||
private sealed class TestType2{}
|
||||
private sealed class TestType3{}
|
||||
private sealed class TestType4{}
|
||||
private sealed class TestType5{}
|
||||
private sealed class TestType6{}
|
||||
private sealed class TestType7{}
|
||||
private sealed class TestType8{}
|
||||
private sealed class TestType9{}
|
||||
private sealed class TestType10{}
|
||||
private sealed class TestType11{}
|
||||
private sealed class TestType12{}
|
||||
private sealed class TestType13{}
|
||||
private sealed class TestType14{}
|
||||
private sealed class TestType15{}
|
||||
private sealed class TestType16{}
|
||||
private sealed class TestType17{}
|
||||
private sealed class TestType18{}
|
||||
private sealed class TestType19{}
|
||||
private sealed class TestType20{}
|
||||
private sealed class TestType21{}
|
||||
private sealed class TestType22{}
|
||||
private sealed class TestType23{}
|
||||
private sealed class TestType24{}
|
||||
private sealed class TestType25{}
|
||||
private sealed class TestType26{}
|
||||
private sealed class TestType27{}
|
||||
private sealed class TestType28{}
|
||||
private sealed class TestType29{}
|
||||
private sealed class TestType30{}
|
||||
private sealed class TestType31{}
|
||||
private sealed class TestType32{}
|
||||
private sealed class TestType33{}
|
||||
private sealed class TestType34{}
|
||||
private sealed class TestType35{}
|
||||
private sealed class TestType36{}
|
||||
private sealed class TestType37{}
|
||||
private sealed class TestType38{}
|
||||
private sealed class TestType39{}
|
||||
private sealed class TestType40{}
|
||||
private sealed class TestType41{}
|
||||
private sealed class TestType42{}
|
||||
private sealed class TestType43{}
|
||||
private sealed class TestType44{}
|
||||
private sealed class TestType45{}
|
||||
private sealed class TestType46{}
|
||||
private sealed class TestType47{}
|
||||
private sealed class TestType48{}
|
||||
private sealed class TestType49{}
|
||||
private sealed class TestType50{}
|
||||
private sealed class TestType51{}
|
||||
private sealed class TestType52{}
|
||||
private sealed class TestType53{}
|
||||
private sealed class TestType54{}
|
||||
private sealed class TestType55{}
|
||||
private sealed class TestType56{}
|
||||
private sealed class TestType57{}
|
||||
private sealed class TestType58{}
|
||||
private sealed class TestType59{}
|
||||
private sealed class TestType60{}
|
||||
private sealed class TestType61{}
|
||||
private sealed class TestType62{}
|
||||
private sealed class TestType63{}
|
||||
private sealed class TestType64{}
|
||||
private sealed class TestType65{}
|
||||
private sealed class TestType66{}
|
||||
private sealed class TestType67{}
|
||||
private sealed class TestType68{}
|
||||
private sealed class TestType69{}
|
||||
private sealed class TestType70{}
|
||||
private sealed class TestType71{}
|
||||
private sealed class TestType72{}
|
||||
private sealed class TestType73{}
|
||||
private sealed class TestType74{}
|
||||
private sealed class TestType75{}
|
||||
private sealed class TestType76{}
|
||||
private sealed class TestType77{}
|
||||
private sealed class TestType78{}
|
||||
private sealed class TestType79{}
|
||||
private sealed class TestType80{}
|
||||
private sealed class TestType81{}
|
||||
private sealed class TestType82{}
|
||||
private sealed class TestType83{}
|
||||
private sealed class TestType84{}
|
||||
private sealed class TestType85{}
|
||||
private sealed class TestType86{}
|
||||
private sealed class TestType87{}
|
||||
private sealed class TestType88{}
|
||||
private sealed class TestType89{}
|
||||
private sealed class TestType90{}
|
||||
private sealed class TestType91{}
|
||||
private sealed class TestType92{}
|
||||
private sealed class TestType93{}
|
||||
private sealed class TestType94{}
|
||||
private sealed class TestType95{}
|
||||
private sealed class TestType96{}
|
||||
private sealed class TestType97{}
|
||||
private sealed class TestType98{}
|
||||
private sealed class TestType99{}
|
||||
// ReSharper restore UnusedType.Local
|
||||
// @formatter:on
|
||||
}
|
||||
@@ -1,21 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using BenchmarkDotNet.Exporters;
|
||||
using BenchmarkDotNet.Loggers;
|
||||
using BenchmarkDotNet.Mathematics;
|
||||
using BenchmarkDotNet.Parameters;
|
||||
using BenchmarkDotNet.Reports;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Npgsql;
|
||||
using Npgsql.Internal;
|
||||
using Npgsql.Internal.TypeHandlers;
|
||||
using Npgsql.Internal.TypeHandling;
|
||||
|
||||
namespace Robust.Benchmarks.Exporters;
|
||||
|
||||
public sealed class SQLExporter : IExporter
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
|
||||
};
|
||||
|
||||
public static readonly IExporter Default = new SQLExporter();
|
||||
|
||||
private SQLExporter(){}
|
||||
@@ -68,8 +79,15 @@ public sealed class SQLExporter : IExporter
|
||||
using var ctx = new BenchmarkContext(builder.Options);
|
||||
try
|
||||
{
|
||||
ctx.Database.OpenConnection();
|
||||
var con = (NpgsqlConnection) ctx.Database.GetDbConnection();
|
||||
con.TypeMapper.AddTypeResolverFactory(new JsonOverrideTypeHandlerResolverFactory(JsonSerializerOptions));
|
||||
|
||||
ctx.Database.Migrate();
|
||||
ctx.BenchmarkRuns.Add(BenchmarkRun.FromSummary(summary, gitHash));
|
||||
foreach (var run in BenchmarkRun.FromSummary(summary, gitHash))
|
||||
{
|
||||
ctx.BenchmarkRuns.Add(run);
|
||||
}
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
finally
|
||||
@@ -81,6 +99,46 @@ public sealed class SQLExporter : IExporter
|
||||
public string Name => "sql";
|
||||
}
|
||||
|
||||
// https://github.com/npgsql/efcore.pg/issues/1107#issuecomment-945126627
|
||||
class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
|
||||
{
|
||||
private readonly JsonSerializerOptions _options;
|
||||
|
||||
public JsonOverrideTypeHandlerResolverFactory(JsonSerializerOptions options)
|
||||
=> _options = options;
|
||||
|
||||
public override TypeHandlerResolver Create(NpgsqlConnector connector)
|
||||
=> new JsonOverrideTypeHandlerResolver(connector, _options);
|
||||
|
||||
public override string? GetDataTypeNameByClrType(Type clrType)
|
||||
=> null;
|
||||
|
||||
public override TypeMappingInfo? GetMappingByDataTypeName(string dataTypeName)
|
||||
=> null;
|
||||
|
||||
class JsonOverrideTypeHandlerResolver : TypeHandlerResolver
|
||||
{
|
||||
readonly JsonHandler _jsonbHandler;
|
||||
|
||||
internal JsonOverrideTypeHandlerResolver(NpgsqlConnector connector, JsonSerializerOptions options)
|
||||
=> _jsonbHandler ??= new JsonHandler(
|
||||
connector.DatabaseInfo.GetPostgresTypeByName("jsonb"),
|
||||
connector.TextEncoding,
|
||||
isJsonb: true,
|
||||
options);
|
||||
|
||||
public override NpgsqlTypeHandler? ResolveByDataTypeName(string typeName)
|
||||
=> typeName == "jsonb" ? _jsonbHandler : null;
|
||||
|
||||
public override NpgsqlTypeHandler? ResolveByClrType(Type type)
|
||||
// You can add any user-defined CLR types which you want mapped to jsonb
|
||||
=> type == typeof(JsonDocument) ? _jsonbHandler : null;
|
||||
|
||||
public override TypeMappingInfo? GetMappingByDataTypeName(string dataTypeName)
|
||||
=> null; // Let the built-in resolver do this
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<BenchmarkContext>
|
||||
{
|
||||
public BenchmarkContext CreateDbContext(string[] args)
|
||||
@@ -101,43 +159,69 @@ public class BenchmarkContext : DbContext
|
||||
|
||||
public class BenchmarkRun
|
||||
{
|
||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public ulong Id { get; set; }
|
||||
public int Id { get; set; }
|
||||
public string GitHash { get; set; } = string.Empty;
|
||||
[Column(TypeName = "timestamptz")]
|
||||
public DateTime RunDate { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
[Column(TypeName = "jsonb")]
|
||||
public BenchmarkRunReport[] Reports { get; set; } = Array.Empty<BenchmarkRunReport>();
|
||||
|
||||
public static BenchmarkRun FromSummary(Summary summary, string gitHash)
|
||||
[Column(TypeName = "timestamptz")] public DateTime RunDate { get; set; }
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string? ParameterMapping { get; set; }
|
||||
|
||||
[Column(TypeName = "jsonb")]
|
||||
public BenchmarkRunParameter[]? ParameterMappingJson { get; set; }
|
||||
|
||||
[Column(TypeName = "jsonb")]
|
||||
public Statistics Statistics { get; set; } = default!;
|
||||
|
||||
public static IEnumerable<BenchmarkRun> FromSummary(Summary summary, string gitHash)
|
||||
{
|
||||
return new BenchmarkRun
|
||||
var runDate = DateTime.UtcNow;
|
||||
|
||||
foreach (var benchmarkReport in summary.Reports)
|
||||
{
|
||||
Reports = summary.Reports.Select(r => new BenchmarkRunReport
|
||||
var paramString = new StringBuilder();
|
||||
var parametersItems = benchmarkReport.BenchmarkCase.Parameters.Items;
|
||||
var runParameters = new BenchmarkRunParameter[parametersItems.Count];
|
||||
for (var i = 0; i < parametersItems.Count; i++)
|
||||
{
|
||||
Parameters = r.BenchmarkCase.Parameters.Items.Select(p => new BenchmarkRunParameter
|
||||
{
|
||||
Name = p.Name,
|
||||
Value = p.Value
|
||||
}).ToArray(),
|
||||
Statistics = r.ResultStatistics
|
||||
}).ToArray(),
|
||||
Name = summary.BenchmarksCases.First().FolderInfo,
|
||||
RunDate = DateTime.UtcNow,
|
||||
GitHash = gitHash
|
||||
};
|
||||
runParameters[i] = new BenchmarkRunParameter(parametersItems[i]);
|
||||
paramString.Append(runParameters[i].ToString());
|
||||
if (i < parametersItems.Count - 1) paramString.Append(',');
|
||||
}
|
||||
|
||||
if (benchmarkReport.ResultStatistics == null)
|
||||
{
|
||||
Console.WriteLine($"err: No statistics available for {benchmarkReport.BenchmarkCase.Descriptor.DisplayInfo}!");
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return new BenchmarkRun
|
||||
{
|
||||
Name = benchmarkReport.BenchmarkCase.Descriptor.DisplayInfo,
|
||||
RunDate = runDate,
|
||||
GitHash = gitHash,
|
||||
ParameterMapping = runParameters.Length > 0 ? paramString.ToString() : null,
|
||||
ParameterMappingJson = runParameters.Length > 0 ? runParameters : null,
|
||||
Statistics = benchmarkReport.ResultStatistics
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BenchmarkRunReport
|
||||
{
|
||||
public BenchmarkRunParameter[] Parameters { get; set; } = Array.Empty<BenchmarkRunParameter>();
|
||||
public Statistics Statistics { get; set; } = default!;
|
||||
}
|
||||
|
||||
public class BenchmarkRunParameter
|
||||
public struct BenchmarkRunParameter
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public object Value { get; set; } = default!;
|
||||
|
||||
public BenchmarkRunParameter(ParameterInstance instance)
|
||||
{
|
||||
Name = instance.Name;
|
||||
Value = instance.Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name}={Value}";
|
||||
}
|
||||
}
|
||||
|
||||
57
Robust.Benchmarks/Migrations/20220510131430_fix-pk.Designer.cs
generated
Normal file
57
Robust.Benchmarks/Migrations/20220510131430_fix-pk.Designer.cs
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Robust.Benchmarks.Exporters;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
[DbContext(typeof(BenchmarkContext))]
|
||||
[Migration("20220510131430_fix-pk")]
|
||||
partial class fixpk
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("GitHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<BenchmarkRunReport[]>("Reports")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime>("RunDate")
|
||||
.HasColumnType("timestamptz");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BenchmarkRuns");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Robust.Benchmarks/Migrations/20220510131430_fix-pk.cs
Normal file
51
Robust.Benchmarks/Migrations/20220510131430_fix-pk.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
public partial class fixpk : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "RunDate",
|
||||
table: "BenchmarkRuns",
|
||||
type: "timestamptz",
|
||||
nullable: false,
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "Date");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "Id",
|
||||
table: "BenchmarkRuns",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
oldClrType: typeof(decimal),
|
||||
oldType: "numeric(20,0)")
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "RunDate",
|
||||
table: "BenchmarkRuns",
|
||||
type: "Date",
|
||||
nullable: false,
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "timestamptz");
|
||||
|
||||
migrationBuilder.AlterColumn<decimal>(
|
||||
name: "Id",
|
||||
table: "BenchmarkRuns",
|
||||
type: "numeric(20,0)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer")
|
||||
.OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Robust.Benchmarks/Migrations/20221009235705_db.Designer.cs
generated
Normal file
62
Robust.Benchmarks/Migrations/20221009235705_db.Designer.cs
generated
Normal file
@@ -0,0 +1,62 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BenchmarkDotNet.Mathematics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Robust.Benchmarks.Exporters;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
[DbContext(typeof(BenchmarkContext))]
|
||||
[Migration("20221009235705_db")]
|
||||
partial class db
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("GitHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ParameterMapping")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("RunDate")
|
||||
.HasColumnType("timestamptz");
|
||||
|
||||
b.Property<Statistics>("Statistics")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BenchmarkRuns");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Robust.Benchmarks/Migrations/20221009235705_db.cs
Normal file
36
Robust.Benchmarks/Migrations/20221009235705_db.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
public partial class db : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Reports",
|
||||
table: "BenchmarkRuns",
|
||||
newName: "Statistics");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ParameterMapping",
|
||||
table: "BenchmarkRuns",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ParameterMapping",
|
||||
table: "BenchmarkRuns");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Statistics",
|
||||
table: "BenchmarkRuns",
|
||||
newName: "Reports");
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Robust.Benchmarks/Migrations/20221010144620_param_work.Designer.cs
generated
Normal file
64
Robust.Benchmarks/Migrations/20221010144620_param_work.Designer.cs
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BenchmarkDotNet.Mathematics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Robust.Benchmarks.Exporters;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
[DbContext(typeof(BenchmarkContext))]
|
||||
[Migration("20221010144620_param_work")]
|
||||
partial class param_work
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("GitHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ParameterMapping")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<BenchmarkRunParameter[]>("ParameterMappingJson")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime>("RunDate")
|
||||
.HasColumnType("timestamptz");
|
||||
|
||||
b.Property<Statistics>("Statistics")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BenchmarkRuns");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Robust.Benchmarks/Migrations/20221010144620_param_work.cs
Normal file
44
Robust.Benchmarks/Migrations/20221010144620_param_work.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Robust.Benchmarks.Exporters;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
public partial class param_work : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ParameterMapping",
|
||||
table: "BenchmarkRuns",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AddColumn<BenchmarkRunParameter[]>(
|
||||
name: "ParameterMappingJson",
|
||||
table: "BenchmarkRuns",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ParameterMappingJson",
|
||||
table: "BenchmarkRuns");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ParameterMapping",
|
||||
table: "BenchmarkRuns",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BenchmarkDotNet.Mathematics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
@@ -17,16 +18,18 @@ namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.0")
|
||||
.HasAnnotation("ProductVersion", "6.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Robust.Benchmarks.Exporters.BenchmarkRun", b =>
|
||||
{
|
||||
b.Property<decimal>("Id")
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("numeric(20,0)");
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("GitHash")
|
||||
.IsRequired()
|
||||
@@ -36,12 +39,18 @@ namespace Robust.Benchmarks.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<BenchmarkRunReport[]>("Reports")
|
||||
.IsRequired()
|
||||
b.Property<string>("ParameterMapping")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<BenchmarkRunParameter[]>("ParameterMappingJson")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime>("RunDate")
|
||||
.HasColumnType("Date");
|
||||
.HasColumnType("timestamptz");
|
||||
|
||||
b.Property<Statistics>("Statistics")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
||||
11
Robust.Benchmarks/Migrations/TypeIApparentlyHaveToKeep.cs
Normal file
11
Robust.Benchmarks/Migrations/TypeIApparentlyHaveToKeep.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Mathematics;
|
||||
using Robust.Benchmarks.Exporters;
|
||||
|
||||
namespace Robust.Benchmarks.Migrations;
|
||||
|
||||
public class BenchmarkRunReport
|
||||
{
|
||||
public BenchmarkRunParameter[] Parameters { get; set; } = Array.Empty<BenchmarkRunParameter>();
|
||||
public Statistics Statistics { get; set; } = default!;
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>../bin/Benchmarks</OutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
<NoWarn>RA0003</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
@@ -18,7 +19,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0-rc.2" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
@@ -19,13 +18,14 @@ namespace Robust.Benchmarks.Serialization
|
||||
: new ErrorNode(node, $"Failed parsing int value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
public int Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null, int _ = default)
|
||||
{
|
||||
return new DeserializedValue<int>(int.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
return int.Parse(node.Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,
|
||||
public DataNode Write(ISerializationManager serializationManager, int value, IDependencyCollection dependencies,
|
||||
bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Robust.Benchmarks.Serialization.Copy
|
||||
|
||||
var seedMapping = yamlStream.Documents[0].RootNode.ToDataNodeCast<SequenceDataNode>().Cast<MappingDataNode>(0);
|
||||
|
||||
Seed = SerializationManager.ReadValueOrThrow<SeedDataDefinition>(seedMapping);
|
||||
Seed = SerializationManager.Read<SeedDataDefinition>(seedMapping);
|
||||
}
|
||||
|
||||
private const string String = "ABC";
|
||||
@@ -46,25 +46,25 @@ namespace Robust.Benchmarks.Serialization.Copy
|
||||
[Benchmark]
|
||||
public string? CreateCopyString()
|
||||
{
|
||||
return SerializationManager.CreateCopy(String);
|
||||
return SerializationManager.Copy(String);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int? CreateCopyInteger()
|
||||
{
|
||||
return SerializationManager.CreateCopy(Integer);
|
||||
return SerializationManager.Copy(Integer);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public DataDefinitionWithString? CreateCopyDataDefinitionWithString()
|
||||
{
|
||||
return SerializationManager.CreateCopy(DataDefinitionWithString);
|
||||
return SerializationManager.Copy(DataDefinitionWithString);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public SeedDataDefinition? CreateCopySeedDataDefinition()
|
||||
{
|
||||
return SerializationManager.CreateCopy(Seed);
|
||||
return SerializationManager.Copy(Seed);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
name: tobacco
|
||||
seedName: tobacco
|
||||
displayName: tobacco plant
|
||||
plantRsi: Objects/Specific/Hydroponics/tobacco.rsi
|
||||
productPrototypes:
|
||||
- LeavesTobacco
|
||||
harvestRepeat: Repeat
|
||||
@@ -36,7 +37,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
Max: 10
|
||||
PotencyDivisor: 10";
|
||||
|
||||
[DataField("id", required: true)] public string ID { get; set; } = default!;
|
||||
[IdDataFieldAttribute] public string ID { get; set; } = default!;
|
||||
|
||||
#region Tracking
|
||||
[DataField("name")] public string Name { get; set; } = string.Empty;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Benchmarks.Serialization.Definitions;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Sequence;
|
||||
@@ -42,32 +41,32 @@ namespace Robust.Benchmarks.Serialization.Read
|
||||
private ValueDataNode FlagThirtyOne { get; } = new("ThirtyOne");
|
||||
|
||||
[Benchmark]
|
||||
public string? ReadString()
|
||||
public string ReadString()
|
||||
{
|
||||
return SerializationManager.ReadValue<string>(StringNode);
|
||||
return SerializationManager.Read<string>(StringNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int? ReadInteger()
|
||||
public int ReadInteger()
|
||||
{
|
||||
return SerializationManager.ReadValue<int>(IntNode);
|
||||
return SerializationManager.Read<int>(IntNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public DataDefinitionWithString? ReadDataDefinitionWithString()
|
||||
public DataDefinitionWithString ReadDataDefinitionWithString()
|
||||
{
|
||||
return SerializationManager.ReadValue<DataDefinitionWithString>(StringDataDefNode);
|
||||
return SerializationManager.Read<DataDefinitionWithString>(StringDataDefNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public SeedDataDefinition? ReadSeedDataDefinition()
|
||||
public SeedDataDefinition ReadSeedDataDefinition()
|
||||
{
|
||||
return SerializationManager.ReadValue<SeedDataDefinition>(SeedNode);
|
||||
return SerializationManager.Read<SeedDataDefinition>(SeedNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("flag")]
|
||||
public DeserializationResult ReadFlagZero()
|
||||
public object? ReadFlagZero()
|
||||
{
|
||||
return SerializationManager.ReadWithTypeSerializer(
|
||||
typeof(int),
|
||||
@@ -77,7 +76,7 @@ namespace Robust.Benchmarks.Serialization.Read
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("flag")]
|
||||
public DeserializationResult ReadThirtyOne()
|
||||
public object? ReadThirtyOne()
|
||||
{
|
||||
return SerializationManager.ReadWithTypeSerializer(
|
||||
typeof(int),
|
||||
@@ -87,7 +86,7 @@ namespace Robust.Benchmarks.Serialization.Read
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("customTypeSerializer")]
|
||||
public DeserializationResult ReadIntegerCustomSerializer()
|
||||
public object? ReadIntegerCustomSerializer()
|
||||
{
|
||||
return SerializationManager.ReadWithTypeSerializer(
|
||||
typeof(int),
|
||||
|
||||
@@ -46,84 +46,84 @@ namespace Robust.Benchmarks.Serialization
|
||||
[BenchmarkCategory("read")]
|
||||
public string[]? ReadEmptyString()
|
||||
{
|
||||
return SerializationManager.ReadValue<string[]>(EmptyNode);
|
||||
return SerializationManager.Read<string[]>(EmptyNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public string[]? ReadOneString()
|
||||
{
|
||||
return SerializationManager.ReadValue<string[]>(OneIntNode);
|
||||
return SerializationManager.Read<string[]>(OneIntNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public string[]? ReadTenStrings()
|
||||
{
|
||||
return SerializationManager.ReadValue<string[]>(TenIntsNode);
|
||||
return SerializationManager.Read<string[]>(TenIntsNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public int[]? ReadEmptyInt()
|
||||
{
|
||||
return SerializationManager.ReadValue<int[]>(EmptyNode);
|
||||
return SerializationManager.Read<int[]>(EmptyNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public int[]? ReadOneInt()
|
||||
{
|
||||
return SerializationManager.ReadValue<int[]>(OneIntNode);
|
||||
return SerializationManager.Read<int[]>(OneIntNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public int[]? ReadTenInts()
|
||||
{
|
||||
return SerializationManager.ReadValue<int[]>(TenIntsNode);
|
||||
return SerializationManager.Read<int[]>(TenIntsNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public DataDefinitionWithString[]? ReadEmptyStringDataDef()
|
||||
{
|
||||
return SerializationManager.ReadValue<DataDefinitionWithString[]>(EmptyNode);
|
||||
return SerializationManager.Read<DataDefinitionWithString[]>(EmptyNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public DataDefinitionWithString[]? ReadOneStringDataDef()
|
||||
{
|
||||
return SerializationManager.ReadValue<DataDefinitionWithString[]>(OneStringDefNode);
|
||||
return SerializationManager.Read<DataDefinitionWithString[]>(OneStringDefNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public DataDefinitionWithString[]? ReadTenStringDataDefs()
|
||||
{
|
||||
return SerializationManager.ReadValue<DataDefinitionWithString[]>(TenStringDefsNode);
|
||||
return SerializationManager.Read<DataDefinitionWithString[]>(TenStringDefsNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public SealedDataDefinitionWithString[]? ReadEmptySealedStringDataDef()
|
||||
{
|
||||
return SerializationManager.ReadValue<SealedDataDefinitionWithString[]>(EmptyNode);
|
||||
return SerializationManager.Read<SealedDataDefinitionWithString[]>(EmptyNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public SealedDataDefinitionWithString[]? ReadOneSealedStringDataDef()
|
||||
{
|
||||
return SerializationManager.ReadValue<SealedDataDefinitionWithString[]>(OneStringDefNode);
|
||||
return SerializationManager.Read<SealedDataDefinitionWithString[]>(OneStringDefNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("read")]
|
||||
public SealedDataDefinitionWithString[]? ReadTenSealedStringDataDefs()
|
||||
{
|
||||
return SerializationManager.ReadValue<SealedDataDefinitionWithString[]>(TenStringDefsNode);
|
||||
return SerializationManager.Read<SealedDataDefinitionWithString[]>(TenStringDefsNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Robust.Benchmarks.Serialization.Write
|
||||
|
||||
var seedMapping = yamlStream.Documents[0].RootNode.ToDataNodeCast<SequenceDataNode>().Cast<MappingDataNode>(0);
|
||||
|
||||
Seed = SerializationManager.ReadValueOrThrow<SeedDataDefinition>(seedMapping);
|
||||
Seed = SerializationManager.Read<SeedDataDefinition>(seedMapping);
|
||||
}
|
||||
|
||||
private const string String = "ABC";
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Robust.Build.Tasks
|
||||
},
|
||||
ContentAttributes =
|
||||
{
|
||||
typeSystem.GetType("Robust.Client.UserInterface.XAML.ContentAttribute")
|
||||
typeSystem.GetType("Avalonia.Metadata.ContentAttribute")
|
||||
},
|
||||
UsableDuringInitializationAttributes =
|
||||
{
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
@@ -19,24 +16,11 @@ namespace Robust.Client.WebView.Cef
|
||||
Array.Copy(args, 0, argv, 1, args.Length);
|
||||
argv[0] = "-";
|
||||
}
|
||||
/*
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
// Chromium tries to load libEGL.so and libGLESv2.so relative to the process executable on Linux.
|
||||
// (Compared to Windows where it is relative to Chromium's *module*)
|
||||
// There is a TODO "is this correct?" in the Chromium code for this.
|
||||
// Great.
|
||||
|
||||
//CopyDllToExecutableDir("libEGL.so");
|
||||
//CopyDllToExecutableDir("libGLESv2.so");
|
||||
|
||||
// System.Threading.Thread.Sleep(200000);
|
||||
}
|
||||
*/
|
||||
var mainArgs = new CefMainArgs(argv);
|
||||
|
||||
// This will block executing until the subprocess is shut down.
|
||||
var code = CefRuntime.ExecuteProcess(mainArgs, null, IntPtr.Zero);
|
||||
var code = CefRuntime.ExecuteProcess(mainArgs, new RobustCefApp(), IntPtr.Zero);
|
||||
|
||||
if (code != 0)
|
||||
{
|
||||
@@ -45,44 +29,5 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/* private static void CopyDllToExecutableDir(string dllName)
|
||||
{
|
||||
var executableDir = PathHelpers.GetExecutableDirectory();
|
||||
var targetPath = Path.Combine(executableDir, dllName);
|
||||
if (File.Exists(targetPath))
|
||||
return;
|
||||
|
||||
// Find source file.
|
||||
string? srcFile = null;
|
||||
foreach (var searchDir in WebViewManagerCef.NativeDllSearchDirectories())
|
||||
{
|
||||
var searchPath = Path.Combine(searchDir, dllName);
|
||||
if (File.Exists(searchPath))
|
||||
{
|
||||
srcFile = searchPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcFile == null)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(targetPath))
|
||||
return;
|
||||
|
||||
File.Copy(srcFile, targetPath);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Catching race condition lock errors and stuff I guess.
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
internal class RobustCefApp : CefApp
|
||||
internal sealed class RobustCefApp : CefApp
|
||||
{
|
||||
private readonly BrowserProcessHandler _browserProcessHandler = new();
|
||||
private readonly RenderProcessHandler _renderProcessHandler = new();
|
||||
@@ -24,12 +24,12 @@ namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
// Disable zygote on Linux.
|
||||
commandLine.AppendSwitch("--no-zygote");
|
||||
|
||||
|
||||
// Work around https://bitbucket.org/chromiumembedded/cef/issues/3213/ozone-egl-initialization-does-not-have
|
||||
// Desktop GL force makes Chromium not try to load its own ANGLE/Swiftshader so load paths aren't problematic.
|
||||
if (OperatingSystem.IsLinux())
|
||||
commandLine.AppendSwitch("--use-gl", "desktop");
|
||||
|
||||
|
||||
// commandLine.AppendSwitch("--single-process");
|
||||
|
||||
//commandLine.AppendSwitch("--disable-gpu");
|
||||
@@ -43,12 +43,18 @@ namespace Robust.Client.WebView.Cef
|
||||
Logger.Debug($"{commandLine}");
|
||||
}
|
||||
|
||||
private class BrowserProcessHandler : CefBrowserProcessHandler
|
||||
protected override void OnRegisterCustomSchemes(CefSchemeRegistrar registrar)
|
||||
{
|
||||
registrar.AddCustomScheme("res", CefSchemeOptions.Secure | CefSchemeOptions.Standard);
|
||||
registrar.AddCustomScheme("usr", CefSchemeOptions.Secure | CefSchemeOptions.Standard);
|
||||
}
|
||||
|
||||
private sealed class BrowserProcessHandler : CefBrowserProcessHandler
|
||||
{
|
||||
}
|
||||
|
||||
// TODO CEF: Research - Is this even needed?
|
||||
private class RenderProcessHandler : CefRenderProcessHandler
|
||||
private sealed class RenderProcessHandler : CefRenderProcessHandler
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Xilium.CefGlue;
|
||||
namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
// Simple CEF client.
|
||||
internal class RobustCefClient : CefClient
|
||||
internal sealed class RobustCefClient : CefClient
|
||||
{
|
||||
private readonly CefRenderHandler _renderHandler;
|
||||
private readonly CefRequestHandler _requestHandler;
|
||||
|
||||
@@ -57,6 +57,14 @@ namespace Robust.Client.WebView.Cef
|
||||
string requestInitiator,
|
||||
ref bool disableDefaultHandling)
|
||||
{
|
||||
var url = new Uri(request.Url);
|
||||
if (url.Scheme == "file")
|
||||
{
|
||||
// Deny file:// access.
|
||||
disableDefaultHandling = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
lock (_resourceRequestHandlers)
|
||||
{
|
||||
_sawmill.Debug($"HANDLING REQUEST: {request.Url}");
|
||||
@@ -113,7 +121,7 @@ namespace Robust.Client.WebView.Cef
|
||||
CefFrame frame,
|
||||
CefRequest request)
|
||||
{
|
||||
return null;
|
||||
return new CookieHandler();
|
||||
}
|
||||
|
||||
protected override CefResourceHandler GetResourceHandler(
|
||||
@@ -124,5 +132,18 @@ namespace Robust.Client.WebView.Cef
|
||||
return _handler;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CookieHandler : CefCookieAccessFilter
|
||||
{
|
||||
protected override bool CanSendCookie(CefBrowser browser, CefFrame frame, CefRequest request, CefCookie cookie)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool CanSaveCookie(CefBrowser browser, CefFrame frame, CefRequest request, CefResponse response, CefCookie cookie)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
Robust.Client.WebView/Cef/WebViewManagerCef.Mime.cs
Normal file
75
Robust.Client.WebView/Cef/WebViewManagerCef.Mime.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.WebView.Cef;
|
||||
|
||||
internal sealed partial class WebViewManagerCef
|
||||
{
|
||||
// Loosely based on:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
private readonly Dictionary<string, string> _resourceMimeTypes = new()
|
||||
{
|
||||
{ "aac", "audio/aac" },
|
||||
{ "avif", "image/avif" },
|
||||
{ "avi", "video/x-msvideo" },
|
||||
{ "bmp", "image/bmp" },
|
||||
{ "css", "text/css" },
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "ico", "image/vnd.microsoft.icon" },
|
||||
{ "jpeg", "image/jpeg" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "js", "text/javascript" },
|
||||
{ "json", "application/json" },
|
||||
{ "jsonld", "application/ld+json" },
|
||||
{ "midi", "audio/midi" },
|
||||
{ "mid", "audio/midi" },
|
||||
{ "mjs", "text/javascript" },
|
||||
{ "mp3", "audio/mpeg" },
|
||||
{ "mp4", "video/mp4" },
|
||||
{ "mpeg", "video/mpeg" },
|
||||
{ "oga", "audio/ogg" },
|
||||
{ "ogg", "audio/ogg" },
|
||||
{ "ogv", "video/ogg" },
|
||||
{ "ogx", "application/ogg" },
|
||||
{ "opus", "audio/opus" },
|
||||
{ "otf", "font/otf" },
|
||||
{ "png", "image/png" },
|
||||
{ "pdf", "application/pdf" },
|
||||
{ "svg", "image/svg+xml" },
|
||||
{ "tiff", "image/tiff" },
|
||||
{ "tif", "image/tiff" },
|
||||
{ "ts", "video/mp2t" },
|
||||
{ "ttf", "font/ttf" },
|
||||
{ "txt", "text/plain" },
|
||||
{ "wav", "audio/wav" },
|
||||
{ "weba", "audio/webm" },
|
||||
{ "webm", "video/webm" },
|
||||
{ "webp", "image/webp" },
|
||||
{ "woff", "font/woff" },
|
||||
{ "woff2", "font/woff2" },
|
||||
{ "xhtml", "application/xhtml+xml" },
|
||||
{ "xml", "application/xml" },
|
||||
{ "zip", "application/zip" },
|
||||
};
|
||||
|
||||
public void SetResourceMimeType(string extension, string mimeType)
|
||||
{
|
||||
DebugTools.Assert(!extension.StartsWith("."), "SetResourceMimeType extension must not include starting dot.");
|
||||
|
||||
lock (_resourceMimeTypes)
|
||||
{
|
||||
_resourceMimeTypes[extension] = mimeType;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType)
|
||||
{
|
||||
lock (_resourceMimeTypes)
|
||||
{
|
||||
return _resourceMimeTypes.TryGetValue(extension, out mimeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,21 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
internal partial class WebViewManagerCef : IWebViewManagerImpl
|
||||
internal sealed partial class WebViewManagerCef : IWebViewManagerImpl
|
||||
{
|
||||
private static readonly string BasePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!;
|
||||
|
||||
@@ -17,11 +23,19 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
IoCManager.Instance!.InjectDependencies(this, oneOff: true);
|
||||
|
||||
_consoleHost.RegisterCommand("flushcookies", Loc.GetString("cmd-flushcookies-desc"), Loc.GetString("cmd-flushcookies-help"), (_, _, _) =>
|
||||
{
|
||||
CefCookieManager.GetGlobal(null).FlushStore(null);
|
||||
});
|
||||
|
||||
string subProcessName;
|
||||
if (OperatingSystem.IsWindows())
|
||||
subProcessName = "Robust.Client.WebView.exe";
|
||||
@@ -38,6 +52,10 @@ namespace Robust.Client.WebView.Cef
|
||||
if (cefResourcesPath == null)
|
||||
throw new InvalidOperationException("Unable to locate cef_resources directory!");
|
||||
|
||||
var cachePath = "";
|
||||
if (_resourceManager.UserData is WritableDirProvider userData)
|
||||
cachePath = userData.GetFullPath(new ResourcePath("/cef_cache"));
|
||||
|
||||
var settings = new CefSettings()
|
||||
{
|
||||
WindowlessRenderingEnabled = true, // So we can render to our UI controls.
|
||||
@@ -47,6 +65,8 @@ namespace Robust.Client.WebView.Cef
|
||||
LocalesDirPath = Path.Combine(cefResourcesPath, "locales"),
|
||||
ResourcesDirPath = cefResourcesPath,
|
||||
RemoteDebuggingPort = 9222,
|
||||
CookieableSchemesList = "usr,res",
|
||||
CachePath = cachePath,
|
||||
};
|
||||
|
||||
Logger.Info($"CEF Version: {CefRuntime.ChromeVersion}");
|
||||
@@ -59,6 +79,12 @@ namespace Robust.Client.WebView.Cef
|
||||
// TODO CEF: After this point, debugging breaks. No, literally. My client crashes but ONLY with the debugger.
|
||||
// I have tried using the DEBUG and RELEASE versions of libcef.so, stripped or non-stripped...
|
||||
// And nothing seemed to work. Odd.
|
||||
|
||||
if (_cfg.GetCVar(WCVars.WebResProtocol))
|
||||
{
|
||||
var handler = new ResourceSchemeFactoryHandler(this, _resourceManager, Logger.GetSawmill("web.res"));
|
||||
CefRuntime.RegisterSchemeHandlerFactory("res", "", handler);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? LocateCefResources()
|
||||
@@ -102,5 +128,48 @@ namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
CefRuntime.Shutdown();
|
||||
}
|
||||
|
||||
private sealed class ResourceSchemeFactoryHandler : CefSchemeHandlerFactory
|
||||
{
|
||||
private readonly WebViewManagerCef _parent;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
public ResourceSchemeFactoryHandler(
|
||||
WebViewManagerCef parent,
|
||||
IResourceManager resourceManager,
|
||||
ISawmill sawmill)
|
||||
{
|
||||
_parent = parent;
|
||||
_resourceManager = resourceManager;
|
||||
_sawmill = sawmill;
|
||||
}
|
||||
|
||||
protected override CefResourceHandler Create(
|
||||
CefBrowser browser,
|
||||
CefFrame frame,
|
||||
string schemeName,
|
||||
CefRequest request)
|
||||
{
|
||||
var uri = new Uri(request.Url);
|
||||
|
||||
_sawmill.Debug($"HANDLING: {request.Url}");
|
||||
|
||||
var resourcePath = new ResourcePath(uri.AbsolutePath);
|
||||
if (_resourceManager.TryContentFileRead(resourcePath, out var stream))
|
||||
{
|
||||
if (!_parent.TryGetResourceMimeType(resourcePath.Extension, out var mime))
|
||||
mime = "application/octet-stream";
|
||||
|
||||
return new RequestResultStream(stream, mime, HttpStatusCode.OK).MakeHandler();
|
||||
}
|
||||
|
||||
var notFoundStream = new MemoryStream();
|
||||
notFoundStream.Write(Encoding.UTF8.GetBytes("Not found"));
|
||||
notFoundStream.Position = 0;
|
||||
|
||||
return new RequestResultStream(notFoundStream, "text/plain", HttpStatusCode.NotFound).MakeHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
@@ -11,6 +12,17 @@ namespace Robust.Client.WebView.Headless
|
||||
return new WebViewWindowDummy();
|
||||
}
|
||||
|
||||
public void SetResourceMimeType(string extension, string mimeType)
|
||||
{
|
||||
// Nop
|
||||
}
|
||||
|
||||
public bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType)
|
||||
{
|
||||
mimeType = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
|
||||
{
|
||||
return new WebViewControlImplDummy();
|
||||
|
||||
@@ -1,7 +1,40 @@
|
||||
namespace Robust.Client.WebView
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Robust.Client.WebView
|
||||
{
|
||||
public interface IWebViewManager
|
||||
{
|
||||
IWebViewWindow CreateBrowserWindow(BrowserWindowCreateParameters createParams);
|
||||
|
||||
/// <summary>
|
||||
/// Overrides file extension -> mime type mappings for the <c>res://</c> protocol.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The built-in <c>res://</c> protocol needs to guess MIME types to report to CEF when resolving files.
|
||||
/// A limited set of extensions have pre-set MIME types in the engine.
|
||||
/// This method allows you to replace or add entries if need be.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method is thread safe.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="extension">
|
||||
/// The extension to specify the MIME type for.
|
||||
/// The argument must not include the starting "." of the file extension.
|
||||
/// </param>
|
||||
/// <param name="mimeType">The mime type for this file extension.</param>
|
||||
/// <seealso cref="TryGetResourceMimeType"/>
|
||||
void SetResourceMimeType(string extension, string mimeType);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to resolve an entry from the <see cref="SetResourceMimeType"/> list.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method is thread safe.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
||||
<PackageReference Include="Robust.Natives.Cef" Version="95.7.14" />
|
||||
<PackageReference Include="Robust.Natives.Cef" Version="102.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
17
Robust.Client.WebView/WCVars.cs
Normal file
17
Robust.Client.WebView/WCVars.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Robust.Client.WebView;
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
/// <summary>
|
||||
/// CVars for <c>Robust.Client.WebView</c>
|
||||
/// </summary>
|
||||
[CVarDefs]
|
||||
public static class WCVars
|
||||
{
|
||||
/// <summary>
|
||||
/// Enable the <c>res://</c> protocol inside WebView browsers, allowing access to the Robust resources.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> WebResProtocol =
|
||||
CVarDef.Create("web.res_protocol", true, CVar.CLIENTONLY);
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using Robust.Client.WebView;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Client.WebView;
|
||||
using Robust.Client.WebView.Cef;
|
||||
using Robust.Client.WebView.Headless;
|
||||
using Robust.Client.WebViewHook;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -17,6 +19,9 @@ namespace Robust.Client.WebView
|
||||
{
|
||||
DebugTools.Assert(_impl == null, "WebViewManager has already been initialized!");
|
||||
|
||||
var cfg = IoCManager.Resolve<IConfigurationManagerInternal>();
|
||||
cfg.LoadCVarsFromAssembly(typeof(WebViewManager).Assembly);
|
||||
|
||||
IoCManager.RegisterInstance<IWebViewManager>(this);
|
||||
IoCManager.RegisterInstance<IWebViewManagerInternal>(this);
|
||||
|
||||
@@ -49,6 +54,20 @@ namespace Robust.Client.WebView
|
||||
return _impl!.CreateBrowserWindow(createParams);
|
||||
}
|
||||
|
||||
public void SetResourceMimeType(string extension, string mimeType)
|
||||
{
|
||||
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
|
||||
|
||||
_impl!.SetResourceMimeType(extension, mimeType);
|
||||
}
|
||||
|
||||
public bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType)
|
||||
{
|
||||
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
|
||||
|
||||
return _impl!.TryGetResourceMimeType(extension, out mimeType);
|
||||
}
|
||||
|
||||
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
|
||||
{
|
||||
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
|
||||
|
||||
@@ -3,6 +3,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -20,7 +21,14 @@ namespace Robust.Client.Animations
|
||||
}
|
||||
|
||||
var entity = (EntityUid) context;
|
||||
var component = IoCManager.Resolve<IEntityManager>().GetComponent(entity, ComponentType);
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entManager.TryGetComponent(entity, ComponentType, out var component))
|
||||
{
|
||||
// This gets checked when the animation is first played, but the component may also be removed while the animation plays
|
||||
Logger.Error($"Couldn't find component {ComponentType} on {entManager.ToPrettyString(entity)} for animation playback!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (component is IAnimationProperties properties)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Robust.Client.Animations
|
||||
|
||||
var keyFrame = KeyFrames[keyFrameIndex];
|
||||
|
||||
SoundSystem.Play(Filter.Local(), keyFrame.Resource, entity, keyFrame.AudioParamsFunc.Invoke());
|
||||
SoundSystem.Play(keyFrame.Resource, Filter.Local(), entity, keyFrame.AudioParamsFunc.Invoke());
|
||||
}
|
||||
|
||||
return (keyFrameIndex, playingTime);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -101,7 +101,7 @@ namespace Robust.Client.Animations
|
||||
case double d:
|
||||
return MathHelper.Lerp(d, (double) b, t);
|
||||
case Angle angle:
|
||||
return (Angle) MathHelper.Lerp(angle, (Angle) b, t);
|
||||
return Angle.Lerp(angle, (Angle) b, t);
|
||||
case Color color:
|
||||
return Color.InterpolateBetween(color, (Color) b, t);
|
||||
case int i:
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Robust.Client.Audio
|
||||
{
|
||||
public sealed class AudioStream
|
||||
{
|
||||
public TimeSpan Length { get; }
|
||||
internal ClydeHandle? ClydeHandle { get; }
|
||||
public string? Name { get; }
|
||||
public int ChannelCount { get; }
|
||||
namespace Robust.Client.Audio;
|
||||
|
||||
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null)
|
||||
{
|
||||
ClydeHandle = handle;
|
||||
Length = length;
|
||||
ChannelCount = channelCount;
|
||||
Name = name;
|
||||
}
|
||||
public sealed class AudioStream
|
||||
{
|
||||
public TimeSpan Length { get; }
|
||||
internal ClydeHandle? ClydeHandle { get; }
|
||||
public string? Name { get; }
|
||||
public int ChannelCount { get; }
|
||||
|
||||
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null)
|
||||
{
|
||||
ClydeHandle = handle;
|
||||
Length = length;
|
||||
ChannelCount = channelCount;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
20
Robust.Client/Audio/Midi/Commands/MidiPanicCommand.cs
Normal file
20
Robust.Client/Audio/Midi/Commands/MidiPanicCommand.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Client.Audio.Midi.Commands;
|
||||
|
||||
public sealed class MidiPanicCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IMidiManager _midiManager = default!;
|
||||
|
||||
public override string Command => "midipanic";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
foreach (var renderer in _midiManager.Renderers)
|
||||
{
|
||||
renderer.StopAllNotes();
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Robust.Client/Audio/Midi/IMidiManager.cs
Normal file
62
Robust.Client/Audio/Midi/IMidiManager.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
using NFluidsynth;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
public interface IMidiManager
|
||||
{
|
||||
/// <summary>
|
||||
/// A read-only list of all existing MIDI Renderers.
|
||||
/// </summary>
|
||||
IReadOnlyList<IMidiRenderer> Renderers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, MIDI support is available.
|
||||
/// </summary>
|
||||
bool IsAvailable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Volume, in db.
|
||||
/// </summary>
|
||||
float Volume { get; set; }
|
||||
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This method tries to return a midi renderer ready to be used.
|
||||
/// You only need to set the <see cref="IMidiRenderer.MidiProgram"/> afterwards.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method can fail if MIDI support is not available.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// <c>null</c> if MIDI support is not available.
|
||||
/// </returns>
|
||||
IMidiRenderer? GetNewRenderer(bool mono = true);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="RobustMidiEvent"/> given a <see cref="MidiEvent"/> and a sequencer tick.
|
||||
/// </summary>
|
||||
RobustMidiEvent FromFluidEvent(MidiEvent midiEvent, uint tick);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SequencerEvent"/> given a <see cref="RobustMidiEvent"/>.
|
||||
/// Be sure to dispose of the result after you've used it.
|
||||
/// </summary>
|
||||
SequencerEvent ToSequencerEvent(RobustMidiEvent midiEvent);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="RobustMidiEvent"/> given a <see cref="SequencerEvent"/> and a sequencer tick.
|
||||
/// </summary>
|
||||
RobustMidiEvent FromSequencerEvent(SequencerEvent midiEvent, uint tick);
|
||||
|
||||
/// <summary>
|
||||
/// Method called every frame.
|
||||
/// Should be used to update positional audio.
|
||||
/// </summary>
|
||||
/// <param name="frameTime"></param>
|
||||
void FrameUpdate(float frameTime);
|
||||
|
||||
void Shutdown();
|
||||
}
|
||||
181
Robust.Client/Audio/Midi/IMidiRenderer.cs
Normal file
181
Robust.Client/Audio/Midi/IMidiRenderer.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
public enum MidiRendererStatus : byte
|
||||
{
|
||||
None,
|
||||
Input,
|
||||
File,
|
||||
}
|
||||
|
||||
public interface IMidiRenderer : IDisposable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The buffered audio source of this renderer.
|
||||
/// </summary>
|
||||
internal IClydeBufferedAudioSource Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this renderer has been disposed or not.
|
||||
/// </summary>
|
||||
bool Disposed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This controls whether the midi file being played will loop or not.
|
||||
/// </summary>
|
||||
bool LoopMidi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This increases all note on velocities to 127.
|
||||
/// </summary>
|
||||
bool VolumeBoost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The midi program (instrument) the renderer is using.
|
||||
/// </summary>
|
||||
byte MidiProgram { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The instrument bank the renderer is using.
|
||||
/// </summary>
|
||||
byte MidiBank { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The soundfont currently selected by the renderer.
|
||||
/// </summary>
|
||||
uint MidiSoundfont { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current status of the renderer.
|
||||
/// "None" if the renderer isn't playing from input or a midi file.
|
||||
/// "Input" if the renderer is playing from midi input.
|
||||
/// "File" if the renderer is playing from a midi file.
|
||||
/// </summary>
|
||||
MidiRendererStatus Status { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the sound will play in stereo or mono.
|
||||
/// </summary>
|
||||
bool Mono { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to drop messages on the percussion channel.
|
||||
/// </summary>
|
||||
bool DisablePercussionChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to drop messages for program change events.
|
||||
/// </summary>
|
||||
bool DisableProgramChangeEvent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of ticks possible for the MIDI player.
|
||||
/// </summary>
|
||||
int PlayerTotalTick { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets (seeks) the current tick of the MIDI player.
|
||||
/// </summary>
|
||||
int PlayerTick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tick of the sequencer.
|
||||
/// </summary>
|
||||
uint SequencerTick { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Time Scale of the sequencer in ticks per second. Default is 1000 for 1 tick per millisecond.
|
||||
/// </summary>
|
||||
double SequencerTimeScale { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Start listening for midi input.
|
||||
/// </summary>
|
||||
bool OpenInput();
|
||||
|
||||
/// <summary>
|
||||
/// Start playing a midi file.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Bytes of the midi file</param>
|
||||
bool OpenMidi(ReadOnlySpan<byte> buffer);
|
||||
|
||||
/// <summary>
|
||||
/// Stops listening for midi input.
|
||||
/// </summary>
|
||||
bool CloseInput();
|
||||
|
||||
/// <summary>
|
||||
/// Stops playing midi files.
|
||||
/// </summary>
|
||||
bool CloseMidi();
|
||||
|
||||
/// <summary>
|
||||
/// Stops all notes being played currently.
|
||||
/// </summary>
|
||||
void StopAllNotes();
|
||||
|
||||
/// <summary>
|
||||
/// Render and play MIDI to the audio source.
|
||||
/// </summary>
|
||||
internal void Render();
|
||||
|
||||
/// <summary>
|
||||
/// Loads a new soundfont into the renderer.
|
||||
/// </summary>
|
||||
void LoadSoundfont(string filename, bool resetPresets = false);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever a new midi event is registered.
|
||||
/// </summary>
|
||||
event Action<RobustMidiEvent> OnMidiEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the midi player finishes playing a song.
|
||||
/// </summary>
|
||||
event Action OnMidiPlayerFinished;
|
||||
|
||||
/// <summary>
|
||||
/// The entity whose position will be used for positional audio.
|
||||
/// This is only used if <see cref="Mono"/> is set to True.
|
||||
/// </summary>
|
||||
EntityUid? TrackingEntity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The position that will be used for positional audio.
|
||||
/// This is only used if <see cref="Mono"/> is set to True
|
||||
/// and <see cref="TrackingEntity"/> is null.
|
||||
/// </summary>
|
||||
EntityCoordinates? TrackingCoordinates { get; set; }
|
||||
|
||||
MidiRendererState RendererState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Send a midi event for the renderer to play.
|
||||
/// </summary>
|
||||
/// <param name="midiEvent">The midi event to be played</param>
|
||||
void SendMidiEvent(RobustMidiEvent midiEvent);
|
||||
|
||||
/// <summary>
|
||||
/// Schedule a MIDI event to be played at a later time.
|
||||
/// </summary>
|
||||
/// <param name="midiEvent">the midi event in question</param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="absolute"></param>
|
||||
void ScheduleMidiEvent(RobustMidiEvent midiEvent, uint time, bool absolute);
|
||||
|
||||
/// <summary>
|
||||
/// Apply a certain state to the renderer.
|
||||
/// </summary>
|
||||
void ApplyState(MidiRendererState state);
|
||||
|
||||
/// <summary>
|
||||
/// Actually disposes of this renderer. Do NOT use outside the MIDI thread.
|
||||
/// </summary>
|
||||
internal void InternalDispose();
|
||||
}
|
||||
155
Robust.Client/Audio/Midi/MidiManager.Conversion.cs
Normal file
155
Robust.Client/Audio/Midi/MidiManager.Conversion.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using NFluidsynth;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
internal sealed partial class MidiManager
|
||||
{
|
||||
public RobustMidiEvent FromFluidEvent(MidiEvent midiEvent, uint tick)
|
||||
{
|
||||
var status = RobustMidiEvent.MakeStatus((byte) midiEvent.Channel, (byte) midiEvent.Type);
|
||||
|
||||
// Control is always the first data byte. Value is always the second data byte. Fluidsynth's API ain't great.
|
||||
var data1 = (byte) midiEvent.Control;
|
||||
var data2 = (byte) midiEvent.Value;
|
||||
|
||||
// PitchBend is handled specially.
|
||||
if (midiEvent.Type == (int) RobustMidiCommand.PitchBend)
|
||||
{
|
||||
// We pack pitch into both data values.
|
||||
var pitch = (ushort) midiEvent.Pitch;
|
||||
data1 = (byte) pitch;
|
||||
data2 = (byte) (pitch >> 8);
|
||||
}
|
||||
|
||||
return new RobustMidiEvent(status, data1, data2, tick);
|
||||
}
|
||||
|
||||
public SequencerEvent ToSequencerEvent(RobustMidiEvent midiEvent)
|
||||
{
|
||||
var sequencerEvent = new SequencerEvent();
|
||||
|
||||
switch (midiEvent.MidiCommand)
|
||||
{
|
||||
case RobustMidiCommand.NoteOff:
|
||||
sequencerEvent.NoteOff(midiEvent.Channel, midiEvent.Key);
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.NoteOn:
|
||||
sequencerEvent.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.AfterTouch:
|
||||
sequencerEvent.KeyPressure(midiEvent.Channel, midiEvent.Key, midiEvent.Value);
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.ControlChange:
|
||||
sequencerEvent.ControlChange(midiEvent.Channel, midiEvent.Control, midiEvent.Value);
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.ProgramChange:
|
||||
sequencerEvent.ProgramChange(midiEvent.Channel, midiEvent.Program);
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.ChannelPressure:
|
||||
sequencerEvent.ChannelPressure(midiEvent.Channel, midiEvent.Value);
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.PitchBend:
|
||||
sequencerEvent.PitchBend(midiEvent.Channel, midiEvent.Pitch);
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.SystemMessage:
|
||||
switch (midiEvent.Control)
|
||||
{
|
||||
case 0x0 when midiEvent.Status == 0xFF:
|
||||
sequencerEvent.SystemReset();
|
||||
break;
|
||||
|
||||
case 0x0B:
|
||||
sequencerEvent.AllNotesOff(midiEvent.Channel);
|
||||
break;
|
||||
|
||||
default:
|
||||
_midiSawmill.Warning($"Tried to convert unsupported event to sequencer event:\n{midiEvent}");
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
_midiSawmill.Warning($"Tried to convert unsupported event to sequencer event:\n{midiEvent}");
|
||||
break;
|
||||
}
|
||||
|
||||
return sequencerEvent;
|
||||
}
|
||||
|
||||
public RobustMidiEvent FromSequencerEvent(SequencerEvent midiEvent, uint tick)
|
||||
{
|
||||
byte channel = (byte) midiEvent.Channel;
|
||||
RobustMidiCommand command = 0x0;
|
||||
byte data1 = 0;
|
||||
byte data2 = 0;
|
||||
|
||||
switch (midiEvent.Type)
|
||||
{
|
||||
case FluidSequencerEventType.NoteOn:
|
||||
command = RobustMidiCommand.NoteOn;
|
||||
data1 = (byte) midiEvent.Key;
|
||||
data2 = (byte) midiEvent.Velocity;
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.NoteOff:
|
||||
command = RobustMidiCommand.NoteOff;
|
||||
data1 = (byte) midiEvent.Key;
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.PitchBend:
|
||||
command = RobustMidiCommand.PitchBend;
|
||||
// We pack pitch into both data values
|
||||
var pitch = (ushort) midiEvent.Pitch;
|
||||
data1 = (byte) pitch;
|
||||
data2 = (byte) (pitch >> 8);
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.ProgramChange:
|
||||
command = RobustMidiCommand.ProgramChange;
|
||||
data1 = (byte) midiEvent.Program;
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.KeyPressure:
|
||||
command = RobustMidiCommand.AfterTouch;
|
||||
data1 = (byte) midiEvent.Key;
|
||||
data2 = (byte) midiEvent.Value;
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.ControlChange:
|
||||
command = RobustMidiCommand.ControlChange;
|
||||
data1 = (byte) midiEvent.Control;
|
||||
data2 = (byte) midiEvent.Value;
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.ChannelPressure:
|
||||
command = RobustMidiCommand.ChannelPressure;
|
||||
data1 = (byte) midiEvent.Value;
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.AllNotesOff:
|
||||
command = RobustMidiCommand.SystemMessage;
|
||||
data1 = 0x0B;
|
||||
break;
|
||||
|
||||
case FluidSequencerEventType.SystemReset:
|
||||
command = RobustMidiCommand.SystemMessage;
|
||||
channel = 0x0F;
|
||||
break;
|
||||
|
||||
default:
|
||||
_midiSawmill.Warning($"Unsupported Sequencer Event: {tick:D8}: {SequencerEventToString(midiEvent)}");
|
||||
break;
|
||||
}
|
||||
|
||||
return new RobustMidiEvent(RobustMidiEvent.MakeStatus(channel, (byte)command), data1, data2, tick);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using NFluidsynth;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -15,269 +16,297 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
namespace Robust.Client.Audio.Midi
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
public interface IMidiManager
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfgMan = default!;
|
||||
[Dependency] private readonly IClydeAudio _clydeAudio = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
[Dependency] private readonly ILogManager _logger = default!;
|
||||
|
||||
private SharedPhysicsSystem _broadPhaseSystem = default!;
|
||||
|
||||
public IReadOnlyList<IMidiRenderer> Renderers
|
||||
{
|
||||
/// <summary>
|
||||
/// This method tries to return a midi renderer ready to be used.
|
||||
/// You only need to set the <see cref="IMidiRenderer.MidiProgram"/> afterwards.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method can fail if MIDI support is not available.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// <c>null</c> if MIDI support is not available.
|
||||
/// </returns>
|
||||
IMidiRenderer? GetNewRenderer();
|
||||
|
||||
/// <summary>
|
||||
/// Method called every frame.
|
||||
/// Should be used to update positional audio.
|
||||
/// </summary>
|
||||
/// <param name="frameTime"></param>
|
||||
void FrameUpdate(float frameTime);
|
||||
|
||||
/// <summary>
|
||||
/// Volume, in db.
|
||||
/// </summary>
|
||||
float Volume { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, MIDI support is available.
|
||||
/// </summary>
|
||||
bool IsAvailable { get; }
|
||||
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
void Shutdown();
|
||||
get
|
||||
{
|
||||
lock (_renderers)
|
||||
{
|
||||
// Perform a copy. Sadly, we can't return a reference to the original list due to threading concerns.
|
||||
return _renderers.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class MidiManager : IMidiManager
|
||||
[ViewVariables]
|
||||
public bool IsAvailable
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfgMan = default!;
|
||||
|
||||
private SharedPhysicsSystem _broadPhaseSystem = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public bool IsAvailable
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
InitializeFluidsynth();
|
||||
InitializeFluidsynth();
|
||||
|
||||
return FluidsynthInitialized;
|
||||
}
|
||||
return FluidsynthInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<IMidiRenderer> _renderers = new();
|
||||
|
||||
private bool _alive = true;
|
||||
private Settings? _settings;
|
||||
private Thread? _midiThread;
|
||||
private ISawmill _midiSawmill = default!;
|
||||
private float _volume = 0f;
|
||||
private bool _volumeDirty = true;
|
||||
|
||||
// Not reliable until Fluidsynth is initialized!
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Volume
|
||||
{
|
||||
get => _volume;
|
||||
set
|
||||
{
|
||||
if (MathHelper.CloseToPercent(_volume, value))
|
||||
return;
|
||||
|
||||
_cfgMan.SetCVar(CVars.MidiVolume, value);
|
||||
_volumeDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string[] LinuxSoundfonts =
|
||||
{
|
||||
"/usr/share/soundfonts/default.sf2",
|
||||
"/usr/share/soundfonts/default.dls",
|
||||
"/usr/share/soundfonts/FluidR3_GM.sf2",
|
||||
"/usr/share/soundfonts/freepats-general-midi.sf2",
|
||||
"/usr/share/sounds/sf2/default.sf2",
|
||||
"/usr/share/sounds/sf2/default.dls",
|
||||
"/usr/share/sounds/sf2/FluidR3_GM.sf2",
|
||||
"/usr/share/sounds/sf2/TimGM6mb.sf2",
|
||||
};
|
||||
|
||||
private const string WindowsSoundfont = @"C:\WINDOWS\system32\drivers\gm.dls";
|
||||
|
||||
private const string OsxSoundfont =
|
||||
"/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls";
|
||||
|
||||
private const string FallbackSoundfont = "/Midi/fallback.sf2";
|
||||
|
||||
private const string ContentCustomSoundfontDirectory = "/Audio/MidiCustom/";
|
||||
|
||||
private const float MaxDistanceForOcclusion = 1000;
|
||||
|
||||
private static ResourcePath CustomSoundfontDirectory = new ResourcePath("/soundfonts/");
|
||||
|
||||
private readonly ResourceLoaderCallbacks _soundfontLoaderCallbacks = new();
|
||||
|
||||
private bool FluidsynthInitialized;
|
||||
private bool _failedInitialize;
|
||||
|
||||
private NFluidsynth.Logger.LoggerDelegate _loggerDelegate = default!;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
private void InitializeFluidsynth()
|
||||
{
|
||||
if (FluidsynthInitialized || _failedInitialize) return;
|
||||
|
||||
_volume = _cfgMan.GetCVar(CVars.MidiVolume);
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
|
||||
{
|
||||
_volume = value;
|
||||
_volumeDirty = true;
|
||||
}, true);
|
||||
|
||||
_midiSawmill = _logger.GetSawmill("midi");
|
||||
#if DEBUG
|
||||
_midiSawmill.Level = LogLevel.Debug;
|
||||
#else
|
||||
_midiSawmill.Level = LogLevel.Error;
|
||||
#endif
|
||||
_sawmill = _logger.GetSawmill("midi.fluidsynth");
|
||||
_loggerDelegate = LoggerDelegate;
|
||||
|
||||
if (!_resourceManager.UserData.Exists(CustomSoundfontDirectory))
|
||||
{
|
||||
_resourceManager.UserData.CreateDir(CustomSoundfontDirectory);
|
||||
}
|
||||
// not a directory, preserve the old file and create an actual directory
|
||||
else if (!_resourceManager.UserData.IsDir(CustomSoundfontDirectory))
|
||||
{
|
||||
_resourceManager.UserData.Rename(CustomSoundfontDirectory, CustomSoundfontDirectory.WithName(CustomSoundfontDirectory.Filename + ".old"));
|
||||
_resourceManager.UserData.CreateDir(CustomSoundfontDirectory);
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<IMidiRenderer> _renderers = new();
|
||||
|
||||
private bool _alive = true;
|
||||
private Settings? _settings;
|
||||
private Thread? _midiThread;
|
||||
private ISawmill _midiSawmill = default!;
|
||||
private float _volume = 0f;
|
||||
private bool _volumeDirty = true;
|
||||
|
||||
// Not reliable until Fluidsynth is initialized!
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Volume
|
||||
try
|
||||
{
|
||||
get => _volume;
|
||||
set
|
||||
{
|
||||
if (MathHelper.CloseToPercent(_volume, value))
|
||||
return;
|
||||
NFluidsynth.Logger.SetLoggerMethod(_loggerDelegate); // Will cause a safe DllNotFoundException if not available.
|
||||
|
||||
_cfgMan.SetCVar(CVars.MidiVolume, value);
|
||||
_volumeDirty = true;
|
||||
}
|
||||
_settings = new Settings();
|
||||
_settings["synth.sample-rate"].DoubleValue = 44100;
|
||||
_settings["player.timing-source"].StringValue = "sample";
|
||||
_settings["synth.lock-memory"].IntValue = 0;
|
||||
_settings["synth.threadsafe-api"].IntValue = 1;
|
||||
_settings["synth.gain"].DoubleValue = 1.0d;
|
||||
_settings["synth.polyphony"].IntValue = 1024;
|
||||
_settings["synth.cpu-cores"].IntValue = 2;
|
||||
_settings["synth.midi-channels"].IntValue = 16;
|
||||
_settings["synth.overflow.age"].DoubleValue = 3000;
|
||||
_settings["audio.driver"].StringValue = "file";
|
||||
_settings["audio.periods"].IntValue = 8;
|
||||
_settings["audio.period-size"].IntValue = 4096;
|
||||
_settings["midi.autoconnect"].IntValue = 1;
|
||||
_settings["player.reset-synth"].IntValue = 0;
|
||||
_settings["synth.midi-bank-select"].StringValue = "gm";
|
||||
//_settings["synth.verbose"].IntValue = 1; // Useful for debugging.
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_midiSawmill.Error("Failed to initialize fluidsynth due to exception, disabling MIDI support:\n{0}", e);
|
||||
_failedInitialize = true;
|
||||
return;
|
||||
}
|
||||
|
||||
private static readonly string[] LinuxSoundfonts =
|
||||
_midiThread = new Thread(ThreadUpdate);
|
||||
_midiThread.Start();
|
||||
|
||||
_broadPhaseSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedPhysicsSystem>();
|
||||
FluidsynthInitialized = true;
|
||||
}
|
||||
|
||||
private void LoggerDelegate(NFluidsynth.Logger.LogLevel level, string message, IntPtr data)
|
||||
{
|
||||
var rLevel = level switch
|
||||
{
|
||||
"/usr/share/soundfonts/default.sf2",
|
||||
"/usr/share/soundfonts/FluidR3_GM.sf2",
|
||||
"/usr/share/soundfonts/freepats-general-midi.sf2",
|
||||
"/usr/share/sounds/sf2/default.sf2",
|
||||
"/usr/share/sounds/sf2/FluidR3_GM.sf2",
|
||||
"/usr/share/sounds/sf2/TimGM6mb.sf2",
|
||||
NFluidsynth.Logger.LogLevel.Panic => LogLevel.Error,
|
||||
NFluidsynth.Logger.LogLevel.Error => LogLevel.Error,
|
||||
NFluidsynth.Logger.LogLevel.Warning => LogLevel.Warning,
|
||||
NFluidsynth.Logger.LogLevel.Information => LogLevel.Info,
|
||||
NFluidsynth.Logger.LogLevel.Debug => LogLevel.Debug,
|
||||
_ => LogLevel.Debug
|
||||
};
|
||||
_sawmill.Log(rLevel, message);
|
||||
}
|
||||
|
||||
private const string WindowsSoundfont = @"C:\WINDOWS\system32\drivers\gm.dls";
|
||||
|
||||
private const string OsxSoundfont =
|
||||
"/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls";
|
||||
|
||||
private const string FallbackSoundfont = "/Midi/fallback.sf2";
|
||||
|
||||
private readonly ResourceLoaderCallbacks _soundfontLoaderCallbacks = new();
|
||||
|
||||
private bool FluidsynthInitialized;
|
||||
private bool _failedInitialize;
|
||||
|
||||
private NFluidsynth.Logger.LoggerDelegate _loggerDelegate = default!;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int OcclusionCollisionMask { get; set; }
|
||||
|
||||
private void InitializeFluidsynth()
|
||||
public IMidiRenderer? GetNewRenderer(bool mono = true)
|
||||
{
|
||||
if (!FluidsynthInitialized)
|
||||
{
|
||||
if (FluidsynthInitialized || _failedInitialize) return;
|
||||
InitializeFluidsynth();
|
||||
|
||||
_volume = _cfgMan.GetCVar(CVars.MidiVolume);
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
|
||||
if (!FluidsynthInitialized) // init failed
|
||||
{
|
||||
_volume = value;
|
||||
_volumeDirty = true;
|
||||
}, true);
|
||||
|
||||
_midiSawmill = Logger.GetSawmill("midi");
|
||||
_sawmill = Logger.GetSawmill("midi.fluidsynth");
|
||||
_loggerDelegate = LoggerDelegate;
|
||||
|
||||
try
|
||||
{
|
||||
NFluidsynth.Logger.SetLoggerMethod(_loggerDelegate); // Will cause a safe DllNotFoundException if not available.
|
||||
|
||||
_settings = new Settings();
|
||||
_settings["synth.sample-rate"].DoubleValue = 44100;
|
||||
_settings["player.timing-source"].StringValue = "sample";
|
||||
_settings["synth.lock-memory"].IntValue = 0;
|
||||
_settings["synth.threadsafe-api"].IntValue = 1;
|
||||
_settings["synth.gain"].DoubleValue = 1.0d;
|
||||
_settings["synth.polyphony"].IntValue = 1024;
|
||||
_settings["synth.cpu-cores"].IntValue = 2;
|
||||
_settings["synth.overflow.age"].DoubleValue = 3000;
|
||||
_settings["audio.driver"].StringValue = "file";
|
||||
_settings["audio.periods"].IntValue = 8;
|
||||
_settings["audio.period-size"].IntValue = 4096;
|
||||
_settings["midi.autoconnect"].IntValue = 1;
|
||||
_settings["player.reset-synth"].IntValue = 0;
|
||||
_settings["synth.midi-bank-select"].StringValue = "gm";
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.WarningS("midi",
|
||||
"Failed to initialize fluidsynth due to exception, disabling MIDI support:\n{0}", e);
|
||||
_failedInitialize = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_midiThread = new Thread(ThreadUpdate);
|
||||
_midiThread.Start();
|
||||
|
||||
_broadPhaseSystem = EntitySystem.Get<SharedPhysicsSystem>();
|
||||
FluidsynthInitialized = true;
|
||||
}
|
||||
|
||||
private void LoggerDelegate(NFluidsynth.Logger.LogLevel level, string message, IntPtr data)
|
||||
var soundfontLoader = SoundFontLoader.NewDefaultSoundFontLoader(_settings);
|
||||
|
||||
// Just making double sure these don't get GC'd.
|
||||
// They shouldn't, MidiRenderer keeps a ref, but making sure...
|
||||
var handle = GCHandle.Alloc(soundfontLoader);
|
||||
|
||||
try
|
||||
{
|
||||
var rLevel = level switch {
|
||||
NFluidsynth.Logger.LogLevel.Panic => LogLevel.Error,
|
||||
NFluidsynth.Logger.LogLevel.Error => LogLevel.Error,
|
||||
NFluidsynth.Logger.LogLevel.Warning => LogLevel.Warning,
|
||||
NFluidsynth.Logger.LogLevel.Information => LogLevel.Info,
|
||||
NFluidsynth.Logger.LogLevel.Debug => LogLevel.Debug,
|
||||
_ => LogLevel.Debug
|
||||
};
|
||||
_sawmill.Log(rLevel, message);
|
||||
}
|
||||
soundfontLoader.SetCallbacks(_soundfontLoaderCallbacks);
|
||||
|
||||
public IMidiRenderer? GetNewRenderer()
|
||||
{
|
||||
if (!FluidsynthInitialized)
|
||||
var renderer = new MidiRenderer(_settings!, soundfontLoader, mono, this, _clydeAudio, _taskManager, _midiSawmill);
|
||||
|
||||
_midiSawmill.Debug($"Loading soundfont {FallbackSoundfont}");
|
||||
// Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont.
|
||||
renderer.LoadSoundfont(FallbackSoundfont);
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
InitializeFluidsynth();
|
||||
|
||||
if (!FluidsynthInitialized) // init failed
|
||||
foreach (var filepath in LinuxSoundfonts)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!File.Exists(filepath) || !SoundFont.IsSoundFont(filepath))
|
||||
continue;
|
||||
|
||||
var soundfontLoader = SoundFontLoader.NewDefaultSoundFontLoader(_settings);
|
||||
|
||||
// Just making double sure these don't get GC'd.
|
||||
// They shouldn't, MidiRenderer keeps a ref, but making sure...
|
||||
var handle = GCHandle.Alloc(soundfontLoader);
|
||||
|
||||
try
|
||||
{
|
||||
soundfontLoader.SetCallbacks(_soundfontLoaderCallbacks);
|
||||
|
||||
var renderer = new MidiRenderer(_settings!, soundfontLoader);
|
||||
|
||||
foreach (var file in _resourceManager.ContentFindFiles(("/Audio/MidiCustom/")))
|
||||
{
|
||||
if (file.Extension != "sf2" && file.Extension != "dls") continue;
|
||||
renderer.LoadSoundfont(file.ToString());
|
||||
}
|
||||
|
||||
// Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont.
|
||||
renderer.LoadSoundfont(FallbackSoundfont);
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
foreach (var filepath in LinuxSoundfonts)
|
||||
try
|
||||
{
|
||||
if (!File.Exists(filepath) || !SoundFont.IsSoundFont(filepath)) continue;
|
||||
|
||||
try
|
||||
{
|
||||
renderer.LoadSoundfont(filepath, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
renderer.LoadSoundfont(filepath);
|
||||
_midiSawmill.Debug($"Loaded Linux soundfont {filepath}");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
if (File.Exists(OsxSoundfont) && SoundFont.IsSoundFont(OsxSoundfont))
|
||||
renderer.LoadSoundfont(OsxSoundfont, true);
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
if (File.Exists(WindowsSoundfont) && SoundFont.IsSoundFont(WindowsSoundfont))
|
||||
renderer.LoadSoundfont(WindowsSoundfont, true);
|
||||
}
|
||||
|
||||
renderer.Source.SetVolume(Volume);
|
||||
|
||||
lock (_renderers)
|
||||
{
|
||||
_renderers.Add(renderer);
|
||||
break;
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
finally
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
handle.Free();
|
||||
if (File.Exists(OsxSoundfont) && SoundFont.IsSoundFont(OsxSoundfont))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading soundfont {OsxSoundfont}");
|
||||
renderer.LoadSoundfont(OsxSoundfont);
|
||||
}
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
if (File.Exists(WindowsSoundfont) && SoundFont.IsSoundFont(WindowsSoundfont))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading soundfont {WindowsSoundfont}");
|
||||
renderer.LoadSoundfont(WindowsSoundfont);
|
||||
}
|
||||
}
|
||||
|
||||
// Load content-specific custom soundfonts, which could override the system/fallback soundfont.
|
||||
_midiSawmill.Debug($"Loading soundfonts from {ContentCustomSoundfontDirectory}");
|
||||
foreach (var file in _resourceManager.ContentFindFiles(ContentCustomSoundfontDirectory))
|
||||
{
|
||||
if (file.Extension != "sf2" && file.Extension != "dls") continue;
|
||||
_midiSawmill.Debug($"Loading soundfont {file}");
|
||||
renderer.LoadSoundfont(file.ToString());
|
||||
}
|
||||
|
||||
// Load every soundfont from the user data directory last, since those may override any other soundfont.
|
||||
_midiSawmill.Debug($"Loading soundfonts from {{USERDATA}} {CustomSoundfontDirectory}");
|
||||
var enumerator = _resourceManager.UserData.Find($"{CustomSoundfontDirectory.ToRelativePath()}/*").Item1;
|
||||
foreach (var file in enumerator)
|
||||
{
|
||||
if (file.Extension != "sf2" && file.Extension != "dls") continue;
|
||||
_midiSawmill.Debug($"Loading soundfont {{USERDATA}} {file}");
|
||||
renderer.LoadSoundfont(file.ToString());
|
||||
}
|
||||
|
||||
renderer.Source.SetVolume(Volume);
|
||||
|
||||
lock (_renderers)
|
||||
{
|
||||
_renderers.Add(renderer);
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
finally
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public void FrameUpdate(float frameTime)
|
||||
{
|
||||
if (!FluidsynthInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void FrameUpdate(float frameTime)
|
||||
// Update positions of streams every frame.
|
||||
lock (_renderers)
|
||||
{
|
||||
if (!FluidsynthInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update positions of streams every frame.
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
if (renderer.Disposed)
|
||||
@@ -318,7 +347,7 @@ namespace Robust.Client.Audio.Midi
|
||||
pos.Position,
|
||||
sourceRelative.Normalized,
|
||||
OcclusionCollisionMask),
|
||||
sourceRelative.Length,
|
||||
MathF.Min(sourceRelative.Length, MaxDistanceForOcclusion),
|
||||
renderer.TrackingEntity);
|
||||
}
|
||||
|
||||
@@ -339,80 +368,109 @@ namespace Robust.Client.Audio.Midi
|
||||
renderer.Source.SetOcclusion(float.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
_volumeDirty = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main method for the thread rendering the midi audio.
|
||||
/// </summary>
|
||||
private void ThreadUpdate()
|
||||
_volumeDirty = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main method for the thread rendering the midi audio.
|
||||
/// </summary>
|
||||
private void ThreadUpdate()
|
||||
{
|
||||
while (_alive)
|
||||
{
|
||||
while (_alive)
|
||||
{
|
||||
lock (_renderers)
|
||||
{
|
||||
for (var i = 0; i < _renderers.Count; i++)
|
||||
{
|
||||
var renderer = _renderers[i];
|
||||
if (!renderer.Disposed)
|
||||
renderer.Render();
|
||||
else
|
||||
{
|
||||
renderer.InternalDispose();
|
||||
_renderers.Remove(renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
_alive = false;
|
||||
_midiThread?.Join();
|
||||
_settings?.Dispose();
|
||||
|
||||
lock (_renderers)
|
||||
{
|
||||
foreach (var renderer in _renderers)
|
||||
for (var i = 0; i < _renderers.Count; i++)
|
||||
{
|
||||
renderer?.Dispose();
|
||||
var renderer = _renderers[i];
|
||||
if (!renderer.Disposed)
|
||||
renderer.Render();
|
||||
else
|
||||
{
|
||||
renderer.InternalDispose();
|
||||
_renderers.Remove(renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FluidsynthInitialized && !_failedInitialize)
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
_alive = false;
|
||||
_midiThread?.Join();
|
||||
_settings?.Dispose();
|
||||
|
||||
lock (_renderers)
|
||||
{
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
NFluidsynth.Logger.SetLoggerMethod(null);
|
||||
renderer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to load soundfonts.
|
||||
/// </summary>
|
||||
private sealed class ResourceLoaderCallbacks : SoundFontLoaderCallbacks
|
||||
if (FluidsynthInitialized && !_failedInitialize)
|
||||
{
|
||||
private readonly Dictionary<int, Stream> _openStreams = new();
|
||||
private int _nextStreamId = 1;
|
||||
NFluidsynth.Logger.SetLoggerMethod(null);
|
||||
}
|
||||
}
|
||||
|
||||
public override IntPtr Open(string filename)
|
||||
/// <summary>
|
||||
/// Internal method to get a human-readable representation of a <see cref="SequencerEvent"/>.
|
||||
/// </summary>
|
||||
internal static string SequencerEventToString(SequencerEvent midiEvent)
|
||||
{
|
||||
// ReSharper disable once UseStringInterpolation
|
||||
return string.Format(
|
||||
"{0} chan:{1:D2} key:{2:D5} bank:{3:D2} ctrl:{4:D5} dur:{5:D5} pitch:{6:D5} prog:{7:D3} val:{8:D5} vel:{9:D5}",
|
||||
midiEvent.Type.ToString().PadLeft(22),
|
||||
midiEvent.Channel,
|
||||
midiEvent.Key,
|
||||
midiEvent.Bank,
|
||||
midiEvent.Control,
|
||||
midiEvent.Duration,
|
||||
midiEvent.Pitch,
|
||||
midiEvent.Program,
|
||||
midiEvent.Value,
|
||||
midiEvent.Velocity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to load soundfonts.
|
||||
/// </summary>
|
||||
private sealed class ResourceLoaderCallbacks : SoundFontLoaderCallbacks
|
||||
{
|
||||
private readonly Dictionary<int, Stream> _openStreams = new();
|
||||
private int _nextStreamId = 1;
|
||||
|
||||
public override IntPtr Open(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
Stream? stream;
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var resourcePath = new ResourcePath(filename);
|
||||
Stream? stream;
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var resourcePath = new ResourcePath(filename);
|
||||
|
||||
if (resourcePath.IsRooted && resourceCache.ContentFileExists(filename))
|
||||
if (resourcePath.IsRooted)
|
||||
{
|
||||
// is it in content?
|
||||
if (resourceCache.ContentFileExists(filename))
|
||||
{
|
||||
if (!resourceCache.TryContentFileRead(filename, out stream))
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
// is it in userdata?
|
||||
else if (resourceCache.UserData.Exists(resourcePath))
|
||||
{
|
||||
stream = resourceCache.UserData.OpenRead(resourcePath);
|
||||
}
|
||||
else if (File.Exists(filename))
|
||||
{
|
||||
stream = File.OpenRead(filename);
|
||||
@@ -421,73 +479,81 @@ namespace Robust.Client.Audio.Midi
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
var id = _nextStreamId++;
|
||||
|
||||
_openStreams.Add(id, stream);
|
||||
|
||||
return (IntPtr) id;
|
||||
}
|
||||
else if (File.Exists(filename))
|
||||
{
|
||||
stream = File.OpenRead(filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
public override unsafe int Read(IntPtr buf, long count, IntPtr sfHandle)
|
||||
{
|
||||
var length = (int) count;
|
||||
var span = new Span<byte>(buf.ToPointer(), length);
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
var id = _nextStreamId++;
|
||||
|
||||
// Fluidsynth's docs state that this method should leave the buffer unmodified if it fails. (returns -1)
|
||||
try
|
||||
_openStreams.Add(id, stream);
|
||||
|
||||
return (IntPtr) id;
|
||||
}
|
||||
|
||||
public override unsafe int Read(IntPtr buf, long count, IntPtr sfHandle)
|
||||
{
|
||||
var length = (int) count;
|
||||
var span = new Span<byte>(buf.ToPointer(), length);
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
|
||||
// Fluidsynth's docs state that this method should leave the buffer unmodified if it fails. (returns -1)
|
||||
try
|
||||
{
|
||||
// Fluidsynth does a LOT of tiny allocations (frankly, way too much).
|
||||
if (count < 1024)
|
||||
{
|
||||
// Fluidsynth does a LOT of tiny allocations (frankly, way too much).
|
||||
if (count < 1024)
|
||||
{
|
||||
// ReSharper disable once SuggestVarOrType_Elsewhere
|
||||
Span<byte> buffer = stackalloc byte[(int)count];
|
||||
// ReSharper disable once SuggestVarOrType_Elsewhere
|
||||
Span<byte> buffer = stackalloc byte[(int)count];
|
||||
|
||||
stream.ReadExact(buffer);
|
||||
stream.ReadExact(buffer);
|
||||
|
||||
buffer.CopyTo(span);
|
||||
}
|
||||
else
|
||||
{
|
||||
var buffer = stream.ReadExact(length);
|
||||
|
||||
buffer.CopyTo(span);
|
||||
}
|
||||
buffer.CopyTo(span);
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
var buffer = stream.ReadExact(length);
|
||||
|
||||
buffer.CopyTo(span);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int Seek(IntPtr sfHandle, int offset, SeekOrigin origin)
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
|
||||
stream.Seek(offset, origin);
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override int Tell(IntPtr sfHandle)
|
||||
{
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) stream.Position;
|
||||
}
|
||||
public override int Seek(IntPtr sfHandle, int offset, SeekOrigin origin)
|
||||
{
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
|
||||
public override int Close(IntPtr sfHandle)
|
||||
{
|
||||
if (!_openStreams.Remove((int) sfHandle, out var stream))
|
||||
return -1;
|
||||
stream.Seek(offset, origin);
|
||||
|
||||
stream.Dispose();
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int Tell(IntPtr sfHandle)
|
||||
{
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
|
||||
return (int) stream.Position;
|
||||
}
|
||||
|
||||
public override int Close(IntPtr sfHandle)
|
||||
{
|
||||
if (!_openStreams.Remove((int) sfHandle, out var stream))
|
||||
return -1;
|
||||
|
||||
stream.Dispose();
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
80
Robust.Client/Audio/Midi/MidiRendererState.cs
Normal file
80
Robust.Client/Audio/Midi/MidiRendererState.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
public struct MidiRendererState
|
||||
{
|
||||
internal FixedArray16<FixedArray128<byte>> NoteVelocities;
|
||||
internal FixedArray16<FixedArray128<byte>> Controllers;
|
||||
internal FixedArray16<byte> Program;
|
||||
internal FixedArray16<byte> ChannelPressure;
|
||||
internal FixedArray16<ushort> PitchBend;
|
||||
|
||||
internal Span<byte> AsSpan => MemoryMarshal.CreateSpan(ref NoteVelocities._00._00, 4160);
|
||||
|
||||
static unsafe MidiRendererState()
|
||||
{
|
||||
var s = new MidiRendererState();
|
||||
DebugTools.Assert(s.AsSpan.Length == sizeof(MidiRendererState),
|
||||
$"{nameof(MidiRendererState)}'s {nameof(AsSpan)} length does not match struct size! Was: {s.AsSpan.Length} Expected: {sizeof(MidiRendererState)}");
|
||||
}
|
||||
|
||||
public MidiRendererState()
|
||||
{
|
||||
NoteVelocities = default;
|
||||
Program = default;
|
||||
ChannelPressure = default;
|
||||
PitchBend = default;
|
||||
Controllers = default;
|
||||
|
||||
// PitchBend is at 8192 by default.
|
||||
PitchBend.AsSpan.Fill(8192);
|
||||
|
||||
// Controller defaults
|
||||
Controllers.AsSpan.Fill(new FixedArray128<byte>
|
||||
{
|
||||
// Bank selection default
|
||||
_00 = 0,
|
||||
|
||||
// Volume controller default
|
||||
_07 = 100,
|
||||
|
||||
// Balance controller default
|
||||
_08 = 64,
|
||||
|
||||
// Pan controller default
|
||||
_10 = 64,
|
||||
|
||||
// Expression controller default
|
||||
_11 = 127,
|
||||
|
||||
// Controller 11 default
|
||||
_43 = 127,
|
||||
|
||||
// Sound controllers 1 to 10 defaults
|
||||
_70 = 64,
|
||||
_71 = 64,
|
||||
_72 = 64,
|
||||
_73 = 64,
|
||||
_74 = 64,
|
||||
_75 = 64,
|
||||
_76 = 64,
|
||||
_77 = 64,
|
||||
_78 = 64,
|
||||
_79 = 64,
|
||||
|
||||
// Portamento default
|
||||
_84 = 255,
|
||||
|
||||
// Non-Registered Parameter Number defaults
|
||||
_98 = 127, // LSB
|
||||
_99 = 127, // MSB
|
||||
|
||||
// Registered Parameter Number defaults
|
||||
_100 = 127, // LSB
|
||||
_101 = 127, // MSB
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Net;
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -45,6 +46,8 @@ namespace Robust.Client
|
||||
|
||||
public string? LastDisconnectReason { get; private set; }
|
||||
|
||||
private (TimeSpan, GameTick) _timeBase;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -52,20 +55,34 @@ namespace Robust.Client
|
||||
_net.ConnectFailed += OnConnectFailed;
|
||||
_net.Disconnect += OnNetDisconnect;
|
||||
|
||||
_net.RegisterNetMessage<MsgSyncTimeBase>(
|
||||
SyncTimeBase,
|
||||
NetMessageAccept.Handshake | NetMessageAccept.Client);
|
||||
|
||||
_configManager.OnValueChanged(CVars.NetTickrate, TickRateChanged, invokeImmediately: true);
|
||||
|
||||
_playMan.Initialize();
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void TickRateChanged(int tickrate)
|
||||
private void SyncTimeBase(MsgSyncTimeBase message)
|
||||
{
|
||||
Logger.DebugS("client", $"Synchronized time base: {message.Tick}: {message.Time}");
|
||||
|
||||
if (RunLevel >= ClientRunLevel.Connected)
|
||||
_timing.TimeBase = (message.Time, message.Tick);
|
||||
else
|
||||
_timeBase = (message.Time, message.Tick);
|
||||
}
|
||||
|
||||
private void TickRateChanged(int tickrate, in CVarChangeInfo info)
|
||||
{
|
||||
if (GameInfo != null)
|
||||
{
|
||||
GameInfo.TickRate = (byte) tickrate;
|
||||
}
|
||||
|
||||
_timing.TickRate = (byte) tickrate;
|
||||
_timing.SetTickRateAt((byte) tickrate, info.TickChanged);
|
||||
Logger.InfoS("client", $"Tickrate changed to: {tickrate} on tick {_timing.CurTick}");
|
||||
}
|
||||
|
||||
@@ -74,7 +91,7 @@ namespace Robust.Client
|
||||
{
|
||||
if (RunLevel == ClientRunLevel.Connecting)
|
||||
{
|
||||
_net.Shutdown("Client mashing that connect button.");
|
||||
_net.Reset("Client mashing that connect button.");
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -202,7 +219,9 @@ namespace Robust.Client
|
||||
{
|
||||
DebugTools.Assert(RunLevel > ClientRunLevel.Initialize);
|
||||
|
||||
PlayerLeaveServer?.Invoke(this, new PlayerEventArgs(_playMan.LocalPlayer?.Session));
|
||||
// Don't invoke PlayerLeaveServer if PlayerJoinedServer & GameStartedSetup hasn't been called yet.
|
||||
if (RunLevel > ClientRunLevel.Connecting)
|
||||
PlayerLeaveServer?.Invoke(this, new PlayerEventArgs(_playMan.LocalPlayer?.Session));
|
||||
|
||||
LastDisconnectReason = args.Reason;
|
||||
GameStoppedReset();
|
||||
@@ -213,7 +232,7 @@ namespace Robust.Client
|
||||
_entityManager.Startup();
|
||||
_mapManager.Startup();
|
||||
|
||||
_timing.ResetSimTime();
|
||||
_timing.ResetSimTime(_timeBase);
|
||||
_timing.Paused = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ using Robust.Client.Input;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Profiling;
|
||||
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.UserInterface.Themes;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
using Robust.Shared;
|
||||
@@ -31,6 +33,7 @@ using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client
|
||||
{
|
||||
@@ -61,6 +64,8 @@ namespace Robust.Client
|
||||
IoCManager.Register<IResourceCache, ResourceCache>();
|
||||
IoCManager.Register<IResourceCacheInternal, ResourceCache>();
|
||||
IoCManager.Register<IClientNetManager, NetManager>();
|
||||
IoCManager.Register<EntityManager, ClientEntityManager>();
|
||||
IoCManager.Register<ClientEntityManager>();
|
||||
IoCManager.Register<IClientEntityManager, ClientEntityManager>();
|
||||
IoCManager.Register<IClientEntityManagerInternal, ClientEntityManager>();
|
||||
IoCManager.Register<IEntityNetworkManager, ClientEntityManager>();
|
||||
@@ -75,6 +80,7 @@ namespace Robust.Client
|
||||
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
|
||||
IoCManager.Register<IMidiManager, MidiManager>();
|
||||
IoCManager.Register<IAuthManager, AuthManager>();
|
||||
IoCManager.Register<ProfViewManager>();
|
||||
IoCManager.Register<IPhysicsManager, PhysicsManager>();
|
||||
switch (mode)
|
||||
{
|
||||
@@ -108,8 +114,9 @@ namespace Robust.Client
|
||||
IoCManager.Register<IPlacementManager, PlacementManager>();
|
||||
IoCManager.Register<IOverlayManager, OverlayManager>();
|
||||
IoCManager.Register<IOverlayManagerInternal, OverlayManager>();
|
||||
IoCManager.Register<IViewVariablesManager, ViewVariablesManager>();
|
||||
IoCManager.Register<IViewVariablesManagerInternal, ViewVariablesManager>();
|
||||
IoCManager.Register<IViewVariablesManager, ClientViewVariablesManager>();
|
||||
IoCManager.Register<IClientViewVariablesManager, ClientViewVariablesManager>();
|
||||
IoCManager.Register<IClientViewVariablesManagerInternal, ClientViewVariablesManager>();
|
||||
IoCManager.Register<IClientConGroupController, ClientConGroupController>();
|
||||
IoCManager.Register<IScriptClient, ScriptClient>();
|
||||
}
|
||||
|
||||
98
Robust.Client/Console/ClientConsoleHost.Completions.cs
Normal file
98
Robust.Client/Console/ClientConsoleHost.Completions.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Network.Messages;
|
||||
|
||||
namespace Robust.Client.Console;
|
||||
|
||||
internal sealed partial class ClientConsoleHost
|
||||
{
|
||||
private readonly Dictionary<int, PendingCompletion> _completionsPending = new();
|
||||
private int _completionSeq;
|
||||
|
||||
|
||||
public async Task<CompletionResult> GetCompletions(List<string> args, CancellationToken cancel)
|
||||
{
|
||||
// Last element is the command currently being typed. May be empty.
|
||||
|
||||
// Logger.Debug($"Running completions: {string.Join(", ", args)}");
|
||||
|
||||
var delay = _cfg.GetCVar(CVars.ConCompletionDelay);
|
||||
if (delay > 0)
|
||||
await Task.Delay((int)(delay * 1000), cancel);
|
||||
|
||||
return await CalcCompletions(args, cancel);
|
||||
}
|
||||
|
||||
private Task<CompletionResult> CalcCompletions(List<string> args, CancellationToken cancel)
|
||||
{
|
||||
if (args.Count == 1)
|
||||
{
|
||||
// Typing out command name, handle this ourselves.
|
||||
var cmdOptions = CompletionResult.FromOptions(
|
||||
RegisteredCommands.Values
|
||||
.Where(c => CanExecute(c.Command))
|
||||
.OrderBy(c => c.Command)
|
||||
.Select(c => new CompletionOption(c.Command, c.Description)));
|
||||
|
||||
return Task.FromResult(cmdOptions);
|
||||
}
|
||||
|
||||
if (!RegisteredCommands.TryGetValue(args[0], out var cmd))
|
||||
return Task.FromResult(CompletionResult.Empty);
|
||||
|
||||
return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], cancel).AsTask();
|
||||
}
|
||||
|
||||
private Task<CompletionResult> DoServerCompletions(List<string> args, CancellationToken cancel)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<CompletionResult>();
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel);
|
||||
var seq = _completionSeq++;
|
||||
|
||||
var pending = new PendingCompletion
|
||||
{
|
||||
Cts = cts,
|
||||
Tcs = tcs
|
||||
};
|
||||
|
||||
var msg = new MsgConCompletion
|
||||
{
|
||||
Args = args.ToArray(),
|
||||
Seq = seq
|
||||
};
|
||||
|
||||
cts.Token.Register(() =>
|
||||
{
|
||||
tcs.SetCanceled(cts.Token);
|
||||
cts.Dispose();
|
||||
_completionsPending.Remove(seq);
|
||||
}, true);
|
||||
|
||||
NetManager.ClientSendMessage(msg);
|
||||
|
||||
_completionsPending.Add(seq, pending);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private void ProcessCompletionResp(MsgConCompletionResp message)
|
||||
{
|
||||
if (!_completionsPending.TryGetValue(message.Seq, out var pending))
|
||||
return;
|
||||
|
||||
pending.Cts.Dispose();
|
||||
pending.Tcs.SetResult(message.Result);
|
||||
|
||||
_completionsPending.Remove(message.Seq);
|
||||
}
|
||||
|
||||
private struct PendingCompletion
|
||||
{
|
||||
public TaskCompletionSource<CompletionResult> Tcs;
|
||||
public CancellationTokenSource Cts;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Log;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
@@ -41,18 +46,24 @@ namespace Robust.Client.Console
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IClientConsoleHost" />
|
||||
internal sealed class ClientConsoleHost : ConsoleHost, IClientConsoleHost
|
||||
internal sealed partial class ClientConsoleHost : ConsoleHost, IClientConsoleHost, IConsoleHostInternal
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _conGroup = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
private bool _requestedCommands;
|
||||
|
||||
public ClientConsoleHost() : base(isServer: false) {}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(HandleConCmdReg);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(HandleConCmdAck);
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCompletion>();
|
||||
NetManager.RegisterNetMessage<MsgConCompletionResp>(ProcessCompletionResp);
|
||||
|
||||
_requestedCommands = false;
|
||||
NetManager.Connected += OnNetworkConnected;
|
||||
@@ -88,6 +99,11 @@ namespace Robust.Client.Console
|
||||
OutputText(text, true, true);
|
||||
}
|
||||
|
||||
public bool IsCmdServer(IConsoleCommand cmd)
|
||||
{
|
||||
return cmd is ServerDummyCommand;
|
||||
}
|
||||
|
||||
public override event ConAnyCommandCallback? AnyCommandExecuted;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -108,14 +124,12 @@ namespace Robust.Client.Console
|
||||
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
{
|
||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
#if !DEBUG
|
||||
if (!_conGroup.CanCommand(commandName) && playerManager.LocalPlayer?.Session.Status > SessionStatus.Connecting)
|
||||
if (!CanExecute(commandName))
|
||||
{
|
||||
WriteError(null, $"Insufficient perms for command: {commandName}");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
var command1 = AvailableCommands[commandName];
|
||||
args.RemoveAt(0);
|
||||
var shell = new ConsoleShell(this, null);
|
||||
@@ -128,13 +142,23 @@ namespace Robust.Client.Console
|
||||
WriteError(null, "Unknown command: " + commandName);
|
||||
}
|
||||
|
||||
private bool CanExecute(string cmdName)
|
||||
{
|
||||
// When not connected to a server, you can run all local commands.
|
||||
// When connected to a server, you can only run commands according to the con group controller.
|
||||
|
||||
return _player.LocalPlayer == null
|
||||
|| _player.LocalPlayer.Session.Status <= SessionStatus.Connecting
|
||||
|| _conGroup.CanCommand(cmdName);
|
||||
}
|
||||
|
||||
/// <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>();
|
||||
var msg = new MsgConCmd();
|
||||
msg.Text = command;
|
||||
NetManager.ClientSendMessage(msg);
|
||||
}
|
||||
@@ -198,36 +222,69 @@ namespace Robust.Client.Console
|
||||
if (!NetManager.IsConnected)
|
||||
return;
|
||||
|
||||
var msg = NetManager.CreateNetMessage<MsgConCmdReg>();
|
||||
var msg = new 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 sealed class ServerDummyCommand : IConsoleCommand
|
||||
{
|
||||
internal ServerDummyCommand(string command, string help, string description)
|
||||
/// <summary>
|
||||
/// These dummies are made purely so list and help can list server-side commands.
|
||||
/// </summary>
|
||||
[Reflect(false)]
|
||||
private sealed class ServerDummyCommand : IConsoleCommand
|
||||
{
|
||||
Command = command;
|
||||
Help = help;
|
||||
Description = description;
|
||||
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);
|
||||
}
|
||||
|
||||
public async ValueTask<CompletionResult> GetCompletionAsync(
|
||||
IConsoleShell shell,
|
||||
string[] args,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var host = (ClientConsoleHost)shell.ConsoleHost;
|
||||
var argsList = args.ToList();
|
||||
argsList.Insert(0, Command);
|
||||
|
||||
return await host.DoServerCompletions(argsList, cancel);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
private sealed class RemoteExecCommand : LocalizedCommands
|
||||
{
|
||||
shell.RemoteExecuteCommand(argStr);
|
||||
public override string Command => ">";
|
||||
public override string Description => LocalizationManager.GetString("cmd-remoteexec-desc");
|
||||
public override string Help => LocalizationManager.GetString("cmd-remoteexec-help");
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
shell.RemoteExecuteCommand(argStr["> ".Length..]);
|
||||
}
|
||||
|
||||
public override async ValueTask<CompletionResult> GetCompletionAsync(
|
||||
IConsoleShell shell,
|
||||
string[] args,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var host = (ClientConsoleHost)shell.ConsoleHost;
|
||||
return await host.DoServerCompletions(args.ToList(), cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@ using Robust.Shared.IoC;
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class AddCompCommand : IConsoleCommand
|
||||
internal sealed class AddCompCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "addcompc";
|
||||
public string Description => "Adds a component to an entity on the client";
|
||||
public string Help => "addcompc <uid> <componentName>";
|
||||
public override string Command => "addcompc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
|
||||
if (args.Length != 2)
|
||||
@@ -36,13 +34,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
internal sealed class RemoveCompCommand : IConsoleCommand
|
||||
internal sealed class RemoveCompCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "rmcompc";
|
||||
public string Description => "Removes a component from an entity.";
|
||||
public string Help => "rmcompc <uid> <componentName>";
|
||||
public override string Command => "rmcompc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
|
||||
@@ -7,13 +7,11 @@ using Robust.Shared.IoC;
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class ClientSpawnCommand : IConsoleCommand
|
||||
internal sealed class ClientSpawnCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "cspawn";
|
||||
public string Description => "Spawns a client-side entity with specific type at your feet.";
|
||||
public string Help => "cspawn <entity type>";
|
||||
public override string Command => "cspawn";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var controlled = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
if (controlled == EntityUid.Invalid)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
@@ -8,64 +6,11 @@ using Robust.Shared.IoC;
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class CVarCommand : SharedCVarCommand, IConsoleCommand
|
||||
public sealed class SaveConfig : LocalizedCommands
|
||||
{
|
||||
public override string Command => "saveconfig";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1 || args.Length > 2)
|
||||
{
|
||||
shell.WriteError("Must provide exactly one or two arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
var name = args[0];
|
||||
|
||||
if (name == "?")
|
||||
{
|
||||
var cvars = configManager.GetRegisteredCVars().OrderBy(c => c);
|
||||
shell.WriteLine(string.Join("\n", cvars));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configManager.IsCVarRegistered(name))
|
||||
{
|
||||
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);
|
||||
shell.WriteLine(value.ToString() ?? "");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write CVar
|
||||
var value = args[1];
|
||||
var type = configManager.GetCVarType(name);
|
||||
try
|
||||
{
|
||||
var parsed = ParseObject(type, value);
|
||||
configManager.SetCVar(name, parsed);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
shell.WriteLine($"Input value is in incorrect format for type {type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class SaveConfig : IConsoleCommand
|
||||
{
|
||||
public string Command => "saveconfig";
|
||||
public string Description => "Saves the client configuration to the config file";
|
||||
public string Help => "saveconfig";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IConfigurationManager>().SaveToFile();
|
||||
}
|
||||
|
||||
@@ -6,25 +6,21 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
sealed class ClearCommand : IConsoleCommand
|
||||
sealed class ClearCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "cls";
|
||||
public string Help => "Clears the debug console of all messages.";
|
||||
public string Description => "Clears the console.";
|
||||
public override string Command => "cls";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
shell.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
sealed class FillCommand : IConsoleCommand
|
||||
sealed class FillCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "fill";
|
||||
public string Help => "Fills the console with some nonsense for debugging.";
|
||||
public string Description => "Fill up the console for debugging.";
|
||||
public override string Command => "fill";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
for (int x = 0; x < 50; x++)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
@@ -30,30 +31,28 @@ using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class DumpEntitiesCommand : IConsoleCommand
|
||||
internal sealed class DumpEntitiesCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "dumpentities";
|
||||
public string Help => "Dump entity list";
|
||||
public string Description => "Dumps entity list of UIDs and prototype.";
|
||||
public override string Command => "dumpentities";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
foreach (var e in entityManager.GetEntities().OrderBy(e => e))
|
||||
{
|
||||
shell.WriteLine($"entity {e}, {entityManager.GetComponent<MetaDataComponent>(e).EntityPrototype?.ID}, {entityManager.GetComponent<TransformComponent>(e).Coordinates}.");
|
||||
shell.WriteLine(
|
||||
$"entity {e}, {entityManager.GetComponent<MetaDataComponent>(e).EntityPrototype?.ID}, {entityManager.GetComponent<TransformComponent>(e).Coordinates}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GetComponentRegistrationCommand : IConsoleCommand
|
||||
internal sealed class GetComponentRegistrationCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "getcomponentregistration";
|
||||
public string Help => "Usage: getcomponentregistration <componentName>";
|
||||
public string Description => "Gets component registration information";
|
||||
public override string Command => "getcomponentregistration";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
@@ -93,93 +92,103 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ToggleMonitorCommand : IConsoleCommand
|
||||
internal sealed class ToggleMonitorCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "monitor";
|
||||
public override string Command => "monitor";
|
||||
|
||||
public string Help =>
|
||||
"Usage: monitor <name>\nPossible monitors are: fps, net, bandwidth, coord, time, frames, mem, clyde, input";
|
||||
|
||||
public string Description => "Toggles a debug monitor in the F3 menu.";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override string Help
|
||||
{
|
||||
var monitor = IoCManager.Resolve<IUserInterfaceManager>().DebugMonitors;
|
||||
get
|
||||
{
|
||||
var monitors = string.Join(", ", Enum.GetNames<DebugMonitor>());
|
||||
return Loc.GetString("cmd-monitor-help", ("monitors", monitors));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var monitors = IoCManager.Resolve<IUserInterfaceManager>().DebugMonitors;
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
shell.WriteLine(Loc.GetString("cmd-monitor-arg-count"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0])
|
||||
var monitorArg = args[0];
|
||||
if (monitorArg.Equals("-all", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
case "fps":
|
||||
monitor.ShowFPS ^= true;
|
||||
break;
|
||||
case "net":
|
||||
monitor.ShowNet ^= true;
|
||||
break;
|
||||
case "bandwidth":
|
||||
monitor.ShowNetBandwidth ^= true;
|
||||
break;
|
||||
case "coord":
|
||||
monitor.ShowCoords ^= true;
|
||||
break;
|
||||
case "time":
|
||||
monitor.ShowTime ^= true;
|
||||
break;
|
||||
case "frames":
|
||||
monitor.ShowFrameGraph ^= true;
|
||||
break;
|
||||
case "mem":
|
||||
monitor.ShowMemory ^= true;
|
||||
break;
|
||||
case "clyde":
|
||||
monitor.ShowClyde ^= true;
|
||||
break;
|
||||
case "input":
|
||||
monitor.ShowInput ^= true;
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine($"Invalid key: {args[0]}");
|
||||
break;
|
||||
foreach (var monitor in Enum.GetValues<DebugMonitor>())
|
||||
{
|
||||
monitors.SetMonitor(monitor, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (monitorArg.Equals("+all", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foreach (var monitor in Enum.GetValues<DebugMonitor>())
|
||||
{
|
||||
monitors.SetMonitor(monitor, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse(monitorArg, true, out DebugMonitor parsedMonitor))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-monitor-invalid-name"));
|
||||
return;
|
||||
}
|
||||
|
||||
monitors.ToggleMonitor(parsedMonitor);
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
var allOptions = new CompletionOption[]
|
||||
{
|
||||
new("-all", Loc.GetString("cmd-monitor-minus-all-hint")),
|
||||
new("+all", Loc.GetString("cmd-monitor-plus-all-hint"))
|
||||
};
|
||||
|
||||
var options = allOptions.Concat(Enum.GetNames<DebugMonitor>().Select(c => new CompletionOption(c)));
|
||||
return CompletionResult.FromHintOptions(options, Loc.GetString("cmd-monitor-arg-monitor"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ExceptionCommand : IConsoleCommand
|
||||
internal sealed class ExceptionCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "fuck";
|
||||
public string Help => "Throws an exception";
|
||||
public string Description => "Throws an exception";
|
||||
public override string Command => "fuck";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
throw new InvalidOperationException("Fuck");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowPositionsCommand : IConsoleCommand
|
||||
internal sealed class ShowPositionsCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "showpos";
|
||||
public string Help => "";
|
||||
public string Description => "Enables debug drawing over all entity positions in the game.";
|
||||
public override string Command => "showpos";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = EntitySystem.Get<DebugDrawingSystem>();
|
||||
var mgr = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<DebugDrawingSystem>();
|
||||
mgr.DebugPositions = !mgr.DebugPositions;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowRayCommand : IConsoleCommand
|
||||
internal sealed class ShowRayCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "showrays";
|
||||
public string Help => "Usage: showrays <raylifetime>";
|
||||
public string Description => "Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided";
|
||||
public override string Command => "showrays";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
@@ -200,28 +209,21 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DisconnectCommand : IConsoleCommand
|
||||
internal sealed class DisconnectCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "disconnect";
|
||||
public string Help => "";
|
||||
public string Description => "Immediately disconnect from the server and go back to the main menu.";
|
||||
public override string Command => "disconnect";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IClientNetManager>().ClientDisconnect("Disconnect command used.");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class EntityInfoCommand : IConsoleCommand
|
||||
internal sealed class EntityInfoCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "entfo";
|
||||
public override string Command => "entfo";
|
||||
|
||||
public string Help =>
|
||||
"entfo <entityuid>\nThe entity UID can be prefixed with 'c' to convert it to a client entity UID.";
|
||||
|
||||
public string Description => "Displays verbose diagnostics for an entity.";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
@@ -242,9 +244,11 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteError("That entity does not exist. Sorry lad.");
|
||||
return;
|
||||
}
|
||||
|
||||
var meta = entmgr.GetComponent<MetaDataComponent>(uid);
|
||||
shell.WriteLine($"{uid}: {meta.EntityPrototype?.ID}/{meta.EntityName}");
|
||||
shell.WriteLine($"init/del/lmt: {meta.EntityInitialized}/{meta.EntityDeleted}/{meta.EntityLastModifiedTick}");
|
||||
shell.WriteLine(
|
||||
$"init/del/lmt: {meta.EntityInitialized}/{meta.EntityDeleted}/{meta.EntityLastModifiedTick}");
|
||||
foreach (var component in entmgr.GetComponents(uid))
|
||||
{
|
||||
shell.WriteLine(component.ToString() ?? "");
|
||||
@@ -264,13 +268,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SnapGridGetCell : IConsoleCommand
|
||||
internal sealed class SnapGridGetCell : LocalizedCommands
|
||||
{
|
||||
public string Command => "sggcell";
|
||||
public string Help => "sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.";
|
||||
public string Description => "Lists entities on a snap grid cell.";
|
||||
public override string Command => "sggcell";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
@@ -278,12 +280,11 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
string gridId = args[0];
|
||||
string indices = args[1];
|
||||
|
||||
if (!int.TryParse(args[0], out var id))
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
{
|
||||
shell.WriteError($"{args[0]} is not a valid integer.");
|
||||
shell.WriteError($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -294,14 +295,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
if (mapMan.GridExists(new GridId(int.Parse(gridId, CultureInfo.InvariantCulture))))
|
||||
if (mapMan.TryGetGrid(gridUid, out var grid))
|
||||
{
|
||||
foreach (var entity in
|
||||
mapMan.GetGrid(new GridId(int.Parse(gridId, CultureInfo.InvariantCulture))).GetAnchoredEntities(
|
||||
new Vector2i(
|
||||
int.Parse(indices.Split(',')[0], CultureInfo.InvariantCulture),
|
||||
int.Parse(indices.Split(',')[1], CultureInfo.InvariantCulture))))
|
||||
foreach (var entity in grid.GetAnchoredEntities(new Vector2i(
|
||||
int.Parse(indices.Split(',')[0], CultureInfo.InvariantCulture),
|
||||
int.Parse(indices.Split(',')[1], CultureInfo.InvariantCulture))))
|
||||
{
|
||||
shell.WriteLine(entity.ToString());
|
||||
}
|
||||
@@ -313,19 +311,18 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SetPlayerName : IConsoleCommand
|
||||
internal sealed class SetPlayerName : LocalizedCommands
|
||||
{
|
||||
public string Command => "overrideplayername";
|
||||
public string Description => "Changes the name used when attempting to connect to the server.";
|
||||
public string Help => Command + " <name>";
|
||||
public override string Command => "overrideplayername";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var client = IoCManager.Resolve<IBaseClient>();
|
||||
client.PlayerNameOverride = args[0];
|
||||
|
||||
@@ -333,19 +330,18 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LoadResource : IConsoleCommand
|
||||
internal sealed class LoadResource : LocalizedCommands
|
||||
{
|
||||
public string Command => "ldrsc";
|
||||
public string Description => "Pre-caches a resource.";
|
||||
public string Help => "ldrsc <path> <type>";
|
||||
public override string Command => "ldrsc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var reflection = IoCManager.Resolve<IReflectionManager>();
|
||||
Type type;
|
||||
@@ -354,7 +350,7 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
type = reflection.LooseGetType(args[1]);
|
||||
}
|
||||
catch(ArgumentException)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
shell.WriteError("Unable to find type");
|
||||
return;
|
||||
@@ -370,19 +366,18 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ReloadResource : IConsoleCommand
|
||||
internal sealed class ReloadResource : LocalizedCommands
|
||||
{
|
||||
public string Command => "rldrsc";
|
||||
public string Description => "Reloads a resource.";
|
||||
public string Help => "rldrsc <path> <type>";
|
||||
public override string Command => "rldrsc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var reflection = IoCManager.Resolve<IReflectionManager>();
|
||||
|
||||
@@ -391,7 +386,7 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
type = reflection.LooseGetType(args[1]);
|
||||
}
|
||||
catch(ArgumentException)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
shell.WriteError("Unable to find type");
|
||||
return;
|
||||
@@ -404,13 +399,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GridTileCount : IConsoleCommand
|
||||
internal sealed class GridTileCount : LocalizedCommands
|
||||
{
|
||||
public string Command => "gridtc";
|
||||
public string Description => "Gets the tile count of a grid";
|
||||
public string Help => "Usage: gridtc <gridId>";
|
||||
public override string Command => "gridtc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
@@ -418,33 +411,29 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[0], out var id))
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid integer.");
|
||||
shell.WriteLine($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
}
|
||||
|
||||
var gridId = new GridId(int.Parse(args[0]));
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
if (mapManager.TryGetGrid(gridId, out var grid))
|
||||
if (mapManager.TryGetGrid(gridUid, out var grid))
|
||||
{
|
||||
shell.WriteLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
|
||||
shell.WriteLine(grid.GetAllTiles().Count().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteError($"No grid exists with id {id}");
|
||||
shell.WriteError($"No grid exists with id {gridUid}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GuiDumpCommand : IConsoleCommand
|
||||
internal sealed class GuiDumpCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "guidump";
|
||||
public string Description => "Dump GUI tree to /guidump.txt in user data.";
|
||||
public string Help => "guidump";
|
||||
public override string Command => "guidump";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var uiMgr = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
var res = IoCManager.Resolve<IResourceManager>();
|
||||
@@ -512,171 +501,33 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class UITestCommand : IConsoleCommand
|
||||
internal sealed class SetClipboardCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "uitest";
|
||||
public string Description => "Open a dummy UI testing window";
|
||||
public string Help => "uitest";
|
||||
public override string Command => "setclipboard";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var window = new DefaultWindow { MinSize = (500, 400)};
|
||||
var tabContainer = new TabContainer();
|
||||
window.Contents.AddChild(tabContainer);
|
||||
var scroll = new ScrollContainer();
|
||||
tabContainer.AddChild(scroll);
|
||||
//scroll.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide);
|
||||
var vBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
scroll.AddChild(vBox);
|
||||
|
||||
var progressBar = new ProgressBar { MaxValue = 10, Value = 5 };
|
||||
vBox.AddChild(progressBar);
|
||||
|
||||
var optionButton = new OptionButton();
|
||||
optionButton.AddItem("Honk");
|
||||
optionButton.AddItem("Foo");
|
||||
optionButton.AddItem("Bar");
|
||||
optionButton.AddItem("Baz");
|
||||
optionButton.OnItemSelected += eventArgs => optionButton.SelectId(eventArgs.Id);
|
||||
vBox.AddChild(optionButton);
|
||||
|
||||
var tree = new Tree { VerticalExpand = true };
|
||||
var root = tree.CreateItem();
|
||||
root.Text = "Honk!";
|
||||
var child = tree.CreateItem();
|
||||
child.Text = "Foo";
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
child = tree.CreateItem();
|
||||
child.Text = $"Bar {i}";
|
||||
}
|
||||
|
||||
vBox.AddChild(tree);
|
||||
|
||||
var rich = new RichTextLabel();
|
||||
var message = new FormattedMessage();
|
||||
message.AddText("Foo\n");
|
||||
message.PushColor(Color.Red);
|
||||
message.AddText("Bar");
|
||||
message.Pop();
|
||||
rich.SetMessage(message);
|
||||
vBox.AddChild(rich);
|
||||
|
||||
var itemList = new ItemList();
|
||||
tabContainer.AddChild(itemList);
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
itemList.AddItem(i.ToString());
|
||||
}
|
||||
|
||||
var grid = new GridContainer { Columns = 3 };
|
||||
tabContainer.AddChild(grid);
|
||||
for (var y = 0; y < 3; y++)
|
||||
{
|
||||
for (var x = 0; x < 3; x++)
|
||||
{
|
||||
grid.AddChild(new Button
|
||||
{
|
||||
MinSize = (50, 50),
|
||||
Text = $"{x}, {y}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var group = new ButtonGroup();
|
||||
var vBoxRadioButtons = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
vBoxRadioButtons.AddChild(new Button
|
||||
{
|
||||
Text = i.ToString(),
|
||||
Group = group
|
||||
});
|
||||
|
||||
// ftftftftftftft
|
||||
}
|
||||
|
||||
tabContainer.AddChild(vBoxRadioButtons);
|
||||
|
||||
TabContainer.SetTabTitle(vBoxRadioButtons, "Radio buttons!!");
|
||||
|
||||
tabContainer.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Name = "Slider",
|
||||
Children =
|
||||
{
|
||||
new Slider()
|
||||
}
|
||||
});
|
||||
|
||||
tabContainer.AddChild(new SplitContainer
|
||||
{
|
||||
Orientation = SplitContainer.SplitOrientation.Horizontal,
|
||||
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"},
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
window.OpenCentered();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SetClipboardCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "setclipboard";
|
||||
public string Description => "Sets the system clipboard";
|
||||
public string Help => "setclipboard <text>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IClipboardManager>();
|
||||
mgr.SetText(args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GetClipboardCommand : IConsoleCommand
|
||||
internal sealed class GetClipboardCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "getclipboard";
|
||||
public string Description => "Gets the system clipboard";
|
||||
public string Help => "getclipboard";
|
||||
public override string Command => "getclipboard";
|
||||
|
||||
public async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IClipboardManager>();
|
||||
shell.WriteLine(await mgr.GetText());
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ToggleLight : IConsoleCommand
|
||||
internal sealed class ToggleLight : LocalizedCommands
|
||||
{
|
||||
public string Command => "togglelight";
|
||||
public string Description => "Toggles light rendering.";
|
||||
public string Help => "togglelight";
|
||||
public override string Command => "togglelight";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
if (!mgr.LockConsoleAccess)
|
||||
@@ -684,27 +535,23 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ToggleFOV : IConsoleCommand
|
||||
internal sealed class ToggleFOV : LocalizedCommands
|
||||
{
|
||||
public string Command => "togglefov";
|
||||
public string Description => "Toggles fov for client.";
|
||||
public string Help => "togglefov";
|
||||
public override string Command => "togglefov";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<IEyeManager>();
|
||||
if (mgr.CurrentEye != null)
|
||||
mgr.CurrentEye.DrawFov = !mgr.CurrentEye.DrawFov;
|
||||
var mgr = IoCManager.Resolve<IEyeManager>();
|
||||
if (mgr.CurrentEye != null)
|
||||
mgr.CurrentEye.DrawFov = !mgr.CurrentEye.DrawFov;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ToggleHardFOV : IConsoleCommand
|
||||
internal sealed class ToggleHardFOV : LocalizedCommands
|
||||
{
|
||||
public string Command => "togglehardfov";
|
||||
public string Description => "Toggles hard fov for client (for debugging space-station-14#2353).";
|
||||
public string Help => "togglehardfov";
|
||||
public override string Command => "togglehardfov";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
if (!mgr.LockConsoleAccess)
|
||||
@@ -712,26 +559,23 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ToggleShadows : IConsoleCommand
|
||||
internal sealed class ToggleShadows : LocalizedCommands
|
||||
{
|
||||
public string Command => "toggleshadows";
|
||||
public string Description => "Toggles shadow rendering.";
|
||||
public string Help => "toggleshadows";
|
||||
public override string Command => "toggleshadows";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
if (!mgr.LockConsoleAccess)
|
||||
mgr.DrawShadows = !mgr.DrawShadows;
|
||||
}
|
||||
}
|
||||
internal sealed 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)
|
||||
internal sealed class ToggleLightBuf : LocalizedCommands
|
||||
{
|
||||
public override string Command => "togglelightbuf";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = IoCManager.Resolve<ILightManager>();
|
||||
if (!mgr.LockConsoleAccess)
|
||||
@@ -739,110 +583,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GcCommand : IConsoleCommand
|
||||
internal sealed class ChunkInfoCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "gc";
|
||||
public string Description => "Run the GC.";
|
||||
public string Help => "gc [generation]";
|
||||
public override string Command => "chunkinfo";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
GC.Collect();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (int.TryParse(args[0], out int result))
|
||||
GC.Collect(result);
|
||||
else
|
||||
shell.WriteError("Failed to parse argument.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed 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);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GcModeCommand : IConsoleCommand
|
||||
{
|
||||
|
||||
public string Command => "gc_mode";
|
||||
|
||||
public string Description => "Change/Read the GC Latency mode.";
|
||||
|
||||
public string Help => "gc_mode\nSee current GC Latencymode\ngc_mode [type]\n Change GC Latency mode to [type]";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var prevMode = GCSettings.LatencyMode;
|
||||
if (args.Length == 0)
|
||||
{
|
||||
shell.WriteLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
|
||||
shell.WriteLine("possible modes:");
|
||||
foreach (int mode in (int[]) Enum.GetValues(typeof(GCLatencyMode)))
|
||||
{
|
||||
shell.WriteLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GCLatencyMode mode;
|
||||
if (char.IsDigit(args[0][0]) && int.TryParse(args[0], out var modeNum))
|
||||
{
|
||||
mode = (GCLatencyMode) modeNum;
|
||||
}
|
||||
else if (!Enum.TryParse(args[0], true, out mode))
|
||||
{
|
||||
shell.WriteLine($"unknown gc latency mode: {args[0]}");
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
|
||||
GCSettings.LatencyMode = mode;
|
||||
shell.WriteLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal sealed class SerializeStatsCommand : IConsoleCommand
|
||||
{
|
||||
|
||||
public string Command => "szr_stats";
|
||||
|
||||
public string Description => "Report serializer statistics.";
|
||||
|
||||
public string Help => "szr_stats";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal sealed class ChunkInfoCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "chunkinfo";
|
||||
public string Description => "Gets info about a chunk under your mouse cursor.";
|
||||
public string Help => Command;
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
var inputMan = IoCManager.Resolve<IInputManager>();
|
||||
@@ -865,20 +610,15 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ReloadShadersCommand : IConsoleCommand
|
||||
internal sealed class ReloadShadersCommand : LocalizedCommands
|
||||
{
|
||||
|
||||
public string Command => "rldshader";
|
||||
|
||||
public string Description => "Reloads all shaders";
|
||||
|
||||
public string Help => "rldshader";
|
||||
public override string Command => "rldshader";
|
||||
|
||||
public static Dictionary<string, FileSystemWatcher>? _watchers;
|
||||
|
||||
public static ConcurrentDictionary<string, bool>? _reloadShadersQueued = new();
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IResourceCacheInternal resC;
|
||||
if (args.Length == 1)
|
||||
@@ -890,12 +630,14 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteLine("Already watching.");
|
||||
return;
|
||||
}
|
||||
|
||||
resC = IoCManager.Resolve<IResourceCacheInternal>();
|
||||
|
||||
_watchers = new Dictionary<string, FileSystemWatcher>();
|
||||
|
||||
var stringComparer = PathHelpers.IsFileSystemCaseSensitive()
|
||||
? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;
|
||||
? StringComparer.Ordinal
|
||||
: StringComparer.OrdinalIgnoreCase;
|
||||
|
||||
var reversePathResolution = new ConcurrentDictionary<string, HashSet<ResourcePath>>(stringComparer);
|
||||
|
||||
@@ -1033,16 +775,13 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
shell.WriteLine("Done.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal sealed class ClydeDebugLayerCommand : IConsoleCommand
|
||||
internal sealed class ClydeDebugLayerCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "cldbglyr";
|
||||
public string Description => "Toggle fov and light debug layers";
|
||||
public string Help => "cldbglyr <layer>: Toggle <layer>\ncldbglyr: Turn all Layers off";
|
||||
public override string Command => "cldbglyr";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClydeInternal>();
|
||||
|
||||
@@ -1061,13 +800,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GetKeyInfoCommand : IConsoleCommand
|
||||
internal sealed class GetKeyInfoCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "keyinfo";
|
||||
public string Description => "Keys key info for a key";
|
||||
public string Help => "keyinfo <Key>";
|
||||
public override string Command => "keyinfo";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
@@ -1079,7 +816,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
if (Enum.TryParse(typeof(Keyboard.Key), args[0], true, out var parsed))
|
||||
{
|
||||
var key = (Keyboard.Key) parsed!;
|
||||
var key = (Keyboard.Key)parsed!;
|
||||
|
||||
var name = clyde.GetKeyName(key);
|
||||
|
||||
|
||||
@@ -5,12 +5,11 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class DebugAnchoredCommand : IConsoleCommand
|
||||
public sealed class DebugAnchoredCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "showanchored";
|
||||
public string Description => $"Shows anchored entities on a particular tile";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override string Command => "showanchored";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugAnchoringSystem>().Enabled ^= true;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,11 @@ using Robust.Shared.ContentPack;
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
#if DEBUG
|
||||
internal sealed class DumpMetadataMembersCommand : IConsoleCommand
|
||||
internal sealed class DumpMetadataMembersCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "dmetamem";
|
||||
public string Description => "Dumps a type's members in a format suitable for the sandbox configuration file.";
|
||||
public string Help => "Usage: dmetamem <type>";
|
||||
public override string Command => "dmetamem";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var type = Type.GetType(args[0]);
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class GridChunkBBCommand : IConsoleCommand
|
||||
public sealed class GridChunkBBCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "showchunkbb";
|
||||
public string Description => "Displays chunk bounds for the purposes of rendering";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override string Command => "showchunkbb";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<GridChunkBoundsDebugSystem>().Enabled ^= true;
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
using System.Linq;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
sealed class HelpCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "help";
|
||||
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 void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
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 (!shell.ConsoleHost.RegisteredCommands.ContainsKey(commandname))
|
||||
{
|
||||
if (!IoCManager.Resolve<IClientNetManager>().IsConnected)
|
||||
{
|
||||
// No server so nothing to respond with unknown command.
|
||||
shell.WriteError("Unknown command: " + commandname);
|
||||
return;
|
||||
}
|
||||
// TODO: Maybe have a server side help?
|
||||
return;
|
||||
}
|
||||
IConsoleCommand command = shell.ConsoleHost.RegisteredCommands[commandname];
|
||||
shell.WriteLine(string.Format("{0} - {1}", command.Command, command.Description));
|
||||
shell.WriteLine(command.Help);
|
||||
break;
|
||||
|
||||
default:
|
||||
shell.WriteError("Invalid amount of arguments.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ListCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "list";
|
||||
public string Help => "Usage: list [filter]\n" +
|
||||
"Lists all available commands, and their short descriptions.\n" +
|
||||
"If a filter is provided, " +
|
||||
"only commands that contain the given string in their name will be listed.";
|
||||
public string Description => "List all commands, optionally with a filter.";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var filter = "";
|
||||
if (args.Length == 1)
|
||||
{
|
||||
filter = args[0];
|
||||
}
|
||||
|
||||
var conGroup = IoCManager.Resolve<IClientConGroupController>();
|
||||
foreach (var command in shell.ConsoleHost.RegisteredCommands.Values
|
||||
.Where(p => p.Command.Contains(filter) && (p is not ServerDummyCommand || conGroup.CanCommand(p.Command)))
|
||||
.OrderBy(c => c.Command))
|
||||
{
|
||||
shell.WriteLine(command.Command + ": " + command.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,19 +9,20 @@ using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class LauncherAuthCommand : IConsoleCommand
|
||||
internal sealed class LauncherAuthCommand : LocalizedCommands
|
||||
{
|
||||
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 override string Command => "launchauth";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var wantName = args.Length > 0 ? args[0] : null;
|
||||
|
||||
var basePath = Path.GetDirectoryName(UserDataDir.GetUserDataDir())!;
|
||||
var dbPath = Path.Combine(basePath, "launcher", "settings.db");
|
||||
|
||||
#if USE_SYSTEM_SQLITE
|
||||
SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
|
||||
#endif
|
||||
using var con = new SqliteConnection($"Data Source={dbPath};Mode=ReadOnly");
|
||||
con.Open();
|
||||
using var cmd = con.CreateCommand();
|
||||
|
||||
@@ -5,12 +5,11 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class LightDebugCommand : IConsoleCommand
|
||||
internal sealed class LightDebugCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "lightbb";
|
||||
public string Description => "Toggles whether to show light bounding boxes";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override string Command => "lightbb";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugLightTreeSystem>().Enabled ^= true;
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.Loader;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class ListAssembliesCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "lsasm";
|
||||
public string Description => "Lists loaded assemblies by load context.";
|
||||
public string Help => Command;
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
foreach (var context in AssemblyLoadContext.All)
|
||||
{
|
||||
shell.WriteLine($"{context.Name}:");
|
||||
foreach (var assembly in context.Assemblies.OrderBy(a => a.FullName))
|
||||
{
|
||||
shell.WriteLine($" {assembly.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
using Robust.Shared.Log;
|
||||
using System;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
sealed class LogSetLevelCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "loglevel";
|
||||
public string Description => "Changes the log level for a provided sawmill.";
|
||||
public string Help => "Usage: loglevel <sawmill> <level>"
|
||||
+ "\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 void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteError("Invalid argument amount. Expected 2 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args[0];
|
||||
var levelname = args[1];
|
||||
LogLevel? level;
|
||||
if (levelname == "null")
|
||||
{
|
||||
level = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Enum.TryParse<LogLevel>(levelname, out var result))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TestLog : IConsoleCommand
|
||||
{
|
||||
public string Command => "testlog";
|
||||
public string Description => "Writes a test log to a sawmill.";
|
||||
public string Help => "Usage: testlog <sawmill> <level> <message>"
|
||||
+ "\n sawmill: A label prefixing the logged message."
|
||||
+ "\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 void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 3)
|
||||
{
|
||||
shell.WriteError("Invalid argument amount. Expected 3 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args[0];
|
||||
var levelname = args[1];
|
||||
var message = args[2]; // yes this doesn't support spaces idgaf.
|
||||
if (!Enum.TryParse<LogLevel>(levelname, out var result))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,11 @@ using Robust.Shared.IoC;
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class LsMonitorCommand : IConsoleCommand
|
||||
public sealed class LsMonitorCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "lsmonitor";
|
||||
public string Description => "";
|
||||
public string Help => "";
|
||||
public override string Command => "lsmonitor";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
@@ -26,13 +24,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class MonitorInfoCommand : IConsoleCommand
|
||||
public sealed class MonitorInfoCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "monitorinfo";
|
||||
public string Description => "";
|
||||
public string Help => "Usage: monitorinfo <id>";
|
||||
public override string Command => "monitorinfo";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
@@ -54,13 +50,11 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class SetMonitorCommand : IConsoleCommand
|
||||
public sealed class SetMonitorCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "setmonitor";
|
||||
public string Description => "";
|
||||
public string Help => "Usage: setmonitor <id>";
|
||||
public override string Command => "setmonitor";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
using Robust.Client.Debugging;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class PhysicsOverlayCommands : IConsoleCommand
|
||||
public sealed class PhysicsOverlayCommands : LocalizedCommands
|
||||
{
|
||||
public string Command => "physics";
|
||||
public string Description => $"Shows a debug physics overlay. The arg supplied specifies the overlay.";
|
||||
public string Help => $"{Command} <aabbs / contactnormals / contactpoints / joints / shapeinfo / shapes>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override string Command => "physics";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine($"Invalid number of args supplied");
|
||||
shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", ("properAmount", 1), ("currentAmount", args.Length)));
|
||||
return;
|
||||
}
|
||||
|
||||
var system = EntitySystem.Get<DebugPhysicsSystem>();
|
||||
var system = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<DebugPhysicsSystem>();
|
||||
|
||||
switch (args[0])
|
||||
{
|
||||
@@ -33,6 +34,9 @@ namespace Robust.Client.Console.Commands
|
||||
case "contactpoints":
|
||||
system.Flags ^= PhysicsDebugFlags.ContactPoints;
|
||||
break;
|
||||
case "distance":
|
||||
system.Flags ^= PhysicsDebugFlags.Distance;
|
||||
break;
|
||||
case "joints":
|
||||
system.Flags ^= PhysicsDebugFlags.Joints;
|
||||
break;
|
||||
@@ -43,11 +47,28 @@ namespace Robust.Client.Console.Commands
|
||||
system.Flags ^= PhysicsDebugFlags.Shapes;
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine($"{args[0]} is not a recognised overlay");
|
||||
shell.WriteError(Loc.GetString("cmd-physics-overlay", ("overlay", args[0])));
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length != 1) return CompletionResult.Empty;
|
||||
|
||||
return CompletionResult.FromOptions(new[]
|
||||
{
|
||||
"aabbs",
|
||||
"com",
|
||||
"contactnormals",
|
||||
"contactpoints",
|
||||
"distance",
|
||||
"joints",
|
||||
"shapeinfo",
|
||||
"shapes",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#if !FULL_RELEASE
|
||||
using System;
|
||||
using JetBrains.Profiler.Api;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Console.Commands;
|
||||
|
||||
public sealed class ProfileEntitySpawningCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
|
||||
public string Command => "profileEntitySpawning";
|
||||
public string Description => "Profiles entity spawning with n entities";
|
||||
public string Help => $"Usage: {Command} | {Command} <amount> <prototype>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var amount = 1000;
|
||||
string? prototype = null;
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
if (!int.TryParse(args[0], out amount))
|
||||
{
|
||||
shell.WriteError($"First argument is not an integer: {args[0]}");
|
||||
return;
|
||||
}
|
||||
|
||||
prototype = args[1];
|
||||
|
||||
break;
|
||||
default:
|
||||
shell.WriteError(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
MeasureProfiler.StartCollectingData();
|
||||
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
_entities.SpawnEntity(prototype, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
MeasureProfiler.SaveData();
|
||||
|
||||
shell.WriteLine($"Client: Profiled spawning {amount} entities in {stopwatch.Elapsed.TotalMilliseconds:N3} ms");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4,25 +4,21 @@ using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
sealed class HardQuitCommand : IConsoleCommand
|
||||
sealed class HardQuitCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "hardquit";
|
||||
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 override string Command => "hardquit";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class QuitCommand : IConsoleCommand
|
||||
sealed class QuitCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "quit";
|
||||
public string Description => "Shuts down the game client gracefully.";
|
||||
public string Help => "Properly shuts down the game client, notifying the connected server and such.";
|
||||
public override string Command => "quit";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IGameController>().Shutdown("quit command used");
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
internal sealed class ReloadLocalizationsCommand : IConsoleCommand
|
||||
internal sealed class ReloadLocalizationsCommand : LocalizedCommands
|
||||
{
|
||||
public string Command => "rldloc";
|
||||
public string Description => "Reloads localization (client & server)";
|
||||
public string Help => "Usage: rldloc";
|
||||
public override string Command => "rldloc";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<ILocalizationManager>().ReloadLocalizations();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user