mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
902 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77654a1628 | ||
|
|
f3af813b57 | ||
|
|
0623baedcf | ||
|
|
2ade6c04c5 | ||
|
|
a9df9097c1 | ||
|
|
755dac719f | ||
|
|
7095a58685 | ||
|
|
16e68a4351 | ||
|
|
0152f9d1d8 | ||
|
|
16d916796a | ||
|
|
e865157432 | ||
|
|
24d5ce4bd4 | ||
|
|
662195e4ff | ||
|
|
d00fd6f736 | ||
|
|
2d58c1071d | ||
|
|
a9db89d023 | ||
|
|
684cabf3e6 | ||
|
|
a4f51f0cd9 | ||
|
|
a8ddd837c8 | ||
|
|
82aace7997 | ||
|
|
01ce244b7b | ||
|
|
58aa6e5c75 | ||
|
|
4818c3aab4 | ||
|
|
3b6adeb5ff | ||
|
|
889b8351be | ||
|
|
ac37b0a131 | ||
|
|
f6f1fc425a | ||
|
|
7476628840 | ||
|
|
668cdbe76b | ||
|
|
a0a6e9b111 | ||
|
|
06d28f04e6 | ||
|
|
57897161d0 | ||
|
|
c4c528478e | ||
|
|
a6c295b89c | ||
|
|
165913a4de | ||
|
|
675dfdaabd | ||
|
|
fab172d6f6 | ||
|
|
e75c1659f6 | ||
|
|
0c440a8fc9 | ||
|
|
0c2c8f352a | ||
|
|
0a4a2b7a36 | ||
|
|
b5b59c1d2f | ||
|
|
f4f0967fdc | ||
|
|
3d69766112 | ||
|
|
d1eb3438d5 | ||
|
|
8f6b189d29 | ||
|
|
ef8b278b47 | ||
|
|
c53ce2c907 | ||
|
|
f063aa3ea1 | ||
|
|
30f63254ef | ||
|
|
30a5b6152c | ||
|
|
910a7f8bff | ||
|
|
526a88293e | ||
|
|
22cd840b83 | ||
|
|
415c518bc7 | ||
|
|
2417dbb0e0 | ||
|
|
005673a957 | ||
|
|
942db3120c | ||
|
|
c0a5fab19e | ||
|
|
d16c62b132 | ||
|
|
7da22557fe | ||
|
|
92f47c0f20 | ||
|
|
9576d0739f | ||
|
|
10f25faabf | ||
|
|
74831a177e | ||
|
|
88d3168913 | ||
|
|
902519093c | ||
|
|
366266a8ae | ||
|
|
a22cce7783 | ||
|
|
e5e738b8cd | ||
|
|
b8f6e83473 | ||
|
|
c5fb186c57 | ||
|
|
131d7f5422 | ||
|
|
217996f1ed | ||
|
|
fc718d68a5 | ||
|
|
d7d9578803 | ||
|
|
9bbeb54569 | ||
|
|
1ea7071ffb | ||
|
|
196028b619 | ||
|
|
c102da052f | ||
|
|
5d46cdcfa4 | ||
|
|
cd646d3b07 | ||
|
|
922165fa19 | ||
|
|
4879252e99 | ||
|
|
0e21f5727a | ||
|
|
3ce8a00389 | ||
|
|
9a283fe541 | ||
|
|
f3e3e64db3 | ||
|
|
4a4a135089 | ||
|
|
e323a67806 | ||
|
|
3a328ffdd5 | ||
|
|
bc5107e297 | ||
|
|
2abf33c9be | ||
|
|
71c46828c2 | ||
|
|
814d6fe2d0 | ||
|
|
77b98b8308 | ||
|
|
34b0a7fc6d | ||
|
|
1b6123c79f | ||
|
|
1476f9d462 | ||
|
|
d62efe7301 | ||
|
|
6af0c88f27 | ||
|
|
5f05b0aa2a | ||
|
|
e6c335b6cd | ||
|
|
5cd8e8276e | ||
|
|
22aeec45f9 | ||
|
|
aed53fb63d | ||
|
|
5ebe97aec1 | ||
|
|
3ff374a4af | ||
|
|
c5bcf853ac | ||
|
|
0624ac36cd | ||
|
|
dd906e9b01 | ||
|
|
7f99b44e5c | ||
|
|
268eb862ea | ||
|
|
467f518421 | ||
|
|
4666a87aa5 | ||
|
|
25007a743f | ||
|
|
8ce3a03136 | ||
|
|
c4d6690a71 | ||
|
|
5486bc7686 | ||
|
|
cdf44ef3d9 | ||
|
|
49ec5b9ca3 | ||
|
|
8b53b89423 | ||
|
|
3fd731d917 | ||
|
|
cb1d4ae843 | ||
|
|
039b70f502 | ||
|
|
7892cc895f | ||
|
|
77108284b8 | ||
|
|
5e21dbdd7f | ||
|
|
8274623edb | ||
|
|
e923d69083 | ||
|
|
6e8ab5ce78 | ||
|
|
f905ea631b | ||
|
|
be54c41891 | ||
|
|
33184ecfa5 | ||
|
|
11cf0c1703 | ||
|
|
528544b7a2 | ||
|
|
8571d7e7b5 | ||
|
|
0f06423b7a | ||
|
|
eb9e0ffefc | ||
|
|
903619ecef | ||
|
|
879c6ea538 | ||
|
|
5478545aeb | ||
|
|
650929dcbb | ||
|
|
a289659b49 | ||
|
|
85d15c21e1 | ||
|
|
bcd1566440 | ||
|
|
749ac2c364 | ||
|
|
5eed3bc281 | ||
|
|
d78f378493 | ||
|
|
eef44c15cf | ||
|
|
3d1b2418f9 | ||
|
|
6b49a86ee5 | ||
|
|
cd13cd3cd8 | ||
|
|
2b8d8d6636 | ||
|
|
409fe1a125 | ||
|
|
ab5db4641c | ||
|
|
064e8ee365 | ||
|
|
02dcff7eae | ||
|
|
e1e5f8de54 | ||
|
|
d5ba822a79 | ||
|
|
f448c6b8fa | ||
|
|
5e1d80be35 | ||
|
|
01546f32da | ||
|
|
aeeaaaefc5 | ||
|
|
b6c8060af1 | ||
|
|
99685838da | ||
|
|
8917b29255 | ||
|
|
f0c4d7c5eb | ||
|
|
6a00c62d3c | ||
|
|
fc3116fca5 | ||
|
|
98c1397b3a | ||
|
|
2464bb6c2f | ||
|
|
709142acee | ||
|
|
af4e3e5e1c | ||
|
|
d51a18c6ea | ||
|
|
d5c3d4c0c9 | ||
|
|
a4474d8df8 | ||
|
|
d66f7c7c06 | ||
|
|
b6879869d6 | ||
|
|
815b8e0c48 | ||
|
|
ef4e3baa7f | ||
|
|
270ddb5a53 | ||
|
|
6133fe0808 | ||
|
|
909fd326a0 | ||
|
|
876de4065a | ||
|
|
60e159f0d0 | ||
|
|
80f3aae30c | ||
|
|
98b1862433 | ||
|
|
d2311c193f | ||
|
|
f05ed96461 | ||
|
|
dc23dfaf4d | ||
|
|
62315f7c2e | ||
|
|
b2d121e780 | ||
|
|
fb4b029122 | ||
|
|
66239d23ea | ||
|
|
dbb45f1c13 | ||
|
|
6284e16b64 | ||
|
|
f6c55085fe | ||
|
|
30eafd26e7 | ||
|
|
63423d96b4 | ||
|
|
474334aff2 | ||
|
|
be102f86bf | ||
|
|
d7962c7190 | ||
|
|
7fe9385c3b | ||
|
|
a9d9d1348a | ||
|
|
4eaf624555 | ||
|
|
e37c131fb4 | ||
|
|
9df4606492 | ||
|
|
3be8070274 | ||
|
|
8a440d705f | ||
|
|
5849474022 | ||
|
|
b7d67c0ece | ||
|
|
b04cf71bc0 | ||
|
|
ea87df649a | ||
|
|
dab7a9112f | ||
|
|
d3dc89832a | ||
|
|
3ade9ca447 | ||
|
|
149e9a2613 | ||
|
|
d164148ce2 | ||
|
|
d898b52449 | ||
|
|
dcf7a1e580 | ||
|
|
65c6bb74eb | ||
|
|
d6d88bea91 | ||
|
|
d6467f768a | ||
|
|
4f0f020f56 | ||
|
|
5ce8369fb9 | ||
|
|
2446e64033 | ||
|
|
bdd65cda4b | ||
|
|
77e949bfe8 | ||
|
|
d4171351f4 | ||
|
|
e67d0ad3d6 | ||
|
|
aff5711fde | ||
|
|
a886222946 | ||
|
|
5843f1087e | ||
|
|
93c0ce815f | ||
|
|
1c64fa1f28 | ||
|
|
c825c1e413 | ||
|
|
f30fb47834 | ||
|
|
5d255e06c8 | ||
|
|
80357c8ec4 | ||
|
|
ac3a434bdf | ||
|
|
21719b8884 | ||
|
|
dbb6b90654 | ||
|
|
4b92be5324 | ||
|
|
95169b7a71 | ||
|
|
b699e22c85 | ||
|
|
e48cc62d0b | ||
|
|
aade062a49 | ||
|
|
e02166d5c4 | ||
|
|
8037bfae14 | ||
|
|
49781791af | ||
|
|
92719aa29f | ||
|
|
1d47a9677d | ||
|
|
14fe8eba6d | ||
|
|
2ff99d4a62 | ||
|
|
ce8b2d82a3 | ||
|
|
f8f99450db | ||
|
|
a3cf4877e4 | ||
|
|
6ab08f7dc1 | ||
|
|
819f6921cf | ||
|
|
cf91369d27 | ||
|
|
0e1328675c | ||
|
|
a7315b1c95 | ||
|
|
c8f2a55cbe | ||
|
|
7812502b0b | ||
|
|
3149f99954 | ||
|
|
ef8c6379cd | ||
|
|
f932e023ee | ||
|
|
a137c839fc | ||
|
|
3e43b88518 | ||
|
|
3d974e0305 | ||
|
|
b3682017ac | ||
|
|
194743a9b0 | ||
|
|
34d65a7960 | ||
|
|
6cd4a37a8f | ||
|
|
40d879fddc | ||
|
|
df398c5b13 | ||
|
|
804b698172 | ||
|
|
346514c6e0 | ||
|
|
d65e2eb169 | ||
|
|
ff41329ad7 | ||
|
|
6151a26622 | ||
|
|
1c7ae13bfa | ||
|
|
cb6645aebe | ||
|
|
fffe3c56e9 | ||
|
|
204e881690 | ||
|
|
161b1874c2 | ||
|
|
7c3634f1f5 | ||
|
|
9969899f38 | ||
|
|
ed35839942 | ||
|
|
380ccfacd8 | ||
|
|
cc0cc6afb1 | ||
|
|
8cc2a17444 | ||
|
|
9c64fbfce2 | ||
|
|
0218c4b969 | ||
|
|
cd72523701 | ||
|
|
76bb9b4b19 | ||
|
|
afdfbba312 | ||
|
|
8b4925863e | ||
|
|
e1597da4c7 | ||
|
|
8375a4038b | ||
|
|
8270442d66 | ||
|
|
85e1920b95 | ||
|
|
5347eb3350 | ||
|
|
e4a14d1ec8 | ||
|
|
c52db4d3f2 | ||
|
|
89f78d76ab | ||
|
|
bbc4668f9c | ||
|
|
ce4016965e | ||
|
|
21e74c9881 | ||
|
|
aa52e8c2ef | ||
|
|
e106d3f72b | ||
|
|
8eae802fb6 | ||
|
|
681feaf0c7 | ||
|
|
0a5a214a06 | ||
|
|
d80be16f6c | ||
|
|
d967bc9fdc | ||
|
|
177ca6b627 | ||
|
|
3c262afaa4 | ||
|
|
bce2901b0f | ||
|
|
c392d4f996 | ||
|
|
d7ee2bccd7 | ||
|
|
4e816fa5e7 | ||
|
|
545e55055e | ||
|
|
6b059ed356 | ||
|
|
b69b4fd8fe | ||
|
|
cb63499ec9 | ||
|
|
dd12110c34 | ||
|
|
a811cfc1a1 | ||
|
|
229a45bea2 | ||
|
|
78376ccca1 | ||
|
|
e4a1415627 | ||
|
|
69589195e0 | ||
|
|
ce3b92aea2 | ||
|
|
5dc980ae92 | ||
|
|
6edeafeed1 | ||
|
|
623aa6a0ae | ||
|
|
1399b71572 | ||
|
|
bbf8827efd | ||
|
|
591c261ff5 | ||
|
|
75f0cf9dd7 | ||
|
|
bde8c2c6e8 | ||
|
|
7d1ad527d9 | ||
|
|
6da708f285 | ||
|
|
f6a5e0ed93 | ||
|
|
21534e7568 | ||
|
|
e7aa2cbc7d | ||
|
|
5ddba87487 | ||
|
|
a69573178f | ||
|
|
1ac39cc65f | ||
|
|
70897b6ea9 | ||
|
|
e763d59617 | ||
|
|
d4902a9714 | ||
|
|
23a40e58e4 | ||
|
|
e71f1cc8a5 | ||
|
|
b82d246988 | ||
|
|
d309872334 | ||
|
|
e33c37ed09 | ||
|
|
3ee95b1a71 | ||
|
|
9a4721a3ee | ||
|
|
8fd1e9047f | ||
|
|
e44e4ac7ed | ||
|
|
581ef074a0 | ||
|
|
9bb61b8a35 | ||
|
|
e7497c7e4f | ||
|
|
7bc54d8f73 | ||
|
|
1631d93e41 | ||
|
|
a32ff39516 | ||
|
|
2715581f48 | ||
|
|
061c4352c6 | ||
|
|
8c5a34f538 | ||
|
|
55d99155e0 | ||
|
|
4c994eb599 | ||
|
|
25fa6b2b2e | ||
|
|
530321bcb6 | ||
|
|
da860d4f56 | ||
|
|
33702b10f3 | ||
|
|
b035159de5 | ||
|
|
ad1b9b1d2b | ||
|
|
9d4105abc7 | ||
|
|
35902ad839 | ||
|
|
f5b8c8813f | ||
|
|
a9eafd4025 | ||
|
|
3bd7309536 | ||
|
|
f5b87b5140 | ||
|
|
05e38e0880 | ||
|
|
2bcb457786 | ||
|
|
e11cef3335 | ||
|
|
b15b800535 | ||
|
|
8345cfea60 | ||
|
|
497674f306 | ||
|
|
fd5e5471be | ||
|
|
1c90be0abc | ||
|
|
b895da5fda | ||
|
|
0ae409a795 | ||
|
|
2c529510e7 | ||
|
|
33cdc0d31f | ||
|
|
8dec3ea922 | ||
|
|
60f9ffb423 | ||
|
|
f2c2750f8c | ||
|
|
00e8afe8fa | ||
|
|
4cde4abbb8 | ||
|
|
1da229549d | ||
|
|
3160437e0f | ||
|
|
82e1a1003f | ||
|
|
3c7b3bd653 | ||
|
|
c993c24284 | ||
|
|
720740ce0c | ||
|
|
1959f6f328 | ||
|
|
596b8e5538 | ||
|
|
ce703223b5 | ||
|
|
b1e594858a | ||
|
|
c09800fe5d | ||
|
|
add8771d1f | ||
|
|
40833ce35b | ||
|
|
de81199037 | ||
|
|
4af1728da4 | ||
|
|
11fed18c47 | ||
|
|
bdb781d680 | ||
|
|
e08dac2be9 | ||
|
|
7a04c81fe1 | ||
|
|
035b1a510d | ||
|
|
4a27df0166 | ||
|
|
478fa8e6de | ||
|
|
23abb6177d | ||
|
|
7b23729f94 | ||
|
|
cf86c7a8b5 | ||
|
|
e2830c9ad8 | ||
|
|
d95c1aef05 | ||
|
|
46ae16718e | ||
|
|
102c24d5d8 | ||
|
|
9dacf63e36 | ||
|
|
cec071f907 | ||
|
|
5e08553af6 | ||
|
|
540febc75b | ||
|
|
0f8231b069 | ||
|
|
7e912cf4ce | ||
|
|
467914e115 | ||
|
|
5d67041a0c | ||
|
|
52fc800398 | ||
|
|
3164b58300 | ||
|
|
1ca68a89e7 | ||
|
|
8ad25f2a9f | ||
|
|
6dc33e4fe0 | ||
|
|
0ebdd4ac1d | ||
|
|
24be915ef1 | ||
|
|
8b48b54575 | ||
|
|
d389e30c87 | ||
|
|
f8f14dc817 | ||
|
|
d9a5f9c4dd | ||
|
|
fbcbcecae6 | ||
|
|
768377f59b | ||
|
|
34adf454c5 | ||
|
|
9af00eea03 | ||
|
|
a6c487f8b7 | ||
|
|
5b7d686de9 | ||
|
|
3a42e8e07a | ||
|
|
115a3f12c7 | ||
|
|
ccec1cbdf3 | ||
|
|
36a124a3aa | ||
|
|
7836130bef | ||
|
|
06f5c78152 | ||
|
|
57ddf81fc4 | ||
|
|
0e7863650b | ||
|
|
a04ad5eeab | ||
|
|
435b252c86 | ||
|
|
4e72ddffbd | ||
|
|
c99059e8ba | ||
|
|
d8aa0b68c9 | ||
|
|
ebfa44cc2c | ||
|
|
a3ef7fa3c9 | ||
|
|
f398ad8a22 | ||
|
|
f06beeb6f6 | ||
|
|
3e2e764f89 | ||
|
|
a42d6f51a8 | ||
|
|
7cb01b8a85 | ||
|
|
022a668266 | ||
|
|
529b529073 | ||
|
|
46b4d57a8c | ||
|
|
369b2de052 | ||
|
|
c86ab63010 | ||
|
|
b692ed2437 | ||
|
|
c077bbc2fd | ||
|
|
7ccef97287 | ||
|
|
905ace3681 | ||
|
|
004b185e73 | ||
|
|
a1dd01a23c | ||
|
|
8d25792752 | ||
|
|
4335f00e28 | ||
|
|
0b14aec199 | ||
|
|
eceefa1308 | ||
|
|
109e685d55 | ||
|
|
b87d1a0a73 | ||
|
|
b422d8d3ca | ||
|
|
5f5a2bade0 | ||
|
|
45135eb416 | ||
|
|
eb7886bff4 | ||
|
|
f08344f60e | ||
|
|
b37f9e827c | ||
|
|
bbd8cd3cf9 | ||
|
|
ce086833b6 | ||
|
|
f7eebce14e | ||
|
|
394bfda92f | ||
|
|
c70a3d6f4b | ||
|
|
12237db4d7 | ||
|
|
9526bb773b | ||
|
|
ff35b921aa | ||
|
|
8511fe4d3b | ||
|
|
dea195be4a | ||
|
|
945c3dada9 | ||
|
|
3d3d6d38e0 | ||
|
|
7bc65a38fc | ||
|
|
2aaa41252a | ||
|
|
538535db9a | ||
|
|
c4c2e84936 | ||
|
|
1678a26d0c | ||
|
|
6afd67acea | ||
|
|
e2bac1b3df | ||
|
|
474f4f09b9 | ||
|
|
e979673d54 | ||
|
|
048e01e409 | ||
|
|
071e211cfe | ||
|
|
f0211ca445 | ||
|
|
5d9d47a2a5 | ||
|
|
25c1346dd9 | ||
|
|
af1fc3cf6b | ||
|
|
99cd739b39 | ||
|
|
ccffd44f40 | ||
|
|
7c4303c9a4 | ||
|
|
a52719b53f | ||
|
|
1449302701 | ||
|
|
d2b3fc737b | ||
|
|
dc5d9a881e | ||
|
|
ee8f95650b | ||
|
|
ebc12410d4 | ||
|
|
89b4062eb6 | ||
|
|
6846b71e43 | ||
|
|
5697800922 | ||
|
|
37b8c49a44 | ||
|
|
4577d5d3d5 | ||
|
|
4a65111dda | ||
|
|
b24e6d0673 | ||
|
|
1ce5be2774 | ||
|
|
6ea8e5fae1 | ||
|
|
45734a2bdd | ||
|
|
b5ebc11d26 | ||
|
|
b8081a335a | ||
|
|
381c856dd4 | ||
|
|
2efed72eba | ||
|
|
4d26202cf5 | ||
|
|
3aa4bd1448 | ||
|
|
179da34be8 | ||
|
|
0581dbe99f | ||
|
|
1b5c5befa9 | ||
|
|
c5ecc426d0 | ||
|
|
2618bc7a36 | ||
|
|
4c282b00d1 | ||
|
|
c5282afbb7 | ||
|
|
5e276d3112 | ||
|
|
d7d22d9cd4 | ||
|
|
adca56b111 | ||
|
|
69cdf6f727 | ||
|
|
a65ccd7c24 | ||
|
|
4f840da5d6 | ||
|
|
96ef013095 | ||
|
|
4efc04a167 | ||
|
|
7cdd45b4ff | ||
|
|
365fad488c | ||
|
|
c1e7aca3fe | ||
|
|
3f7fce2f02 | ||
|
|
105aa13eb1 | ||
|
|
7d8d6ebd20 | ||
|
|
1bb2d39d71 | ||
|
|
0a5375d1c7 | ||
|
|
c9042f188c | ||
|
|
cea74c63cd | ||
|
|
37420b253b | ||
|
|
c4dead2c99 | ||
|
|
f917748809 | ||
|
|
f25a728a36 | ||
|
|
be9f0a988a | ||
|
|
eae1e7c108 | ||
|
|
6f4e31d6c4 | ||
|
|
d52759f4fb | ||
|
|
4242591b56 | ||
|
|
276c1f6ff8 | ||
|
|
19df65188b | ||
|
|
9223c79ca3 | ||
|
|
693a73549d | ||
|
|
c7c21a34e7 | ||
|
|
d8ba637502 | ||
|
|
b638a784cf | ||
|
|
733c0e7a45 | ||
|
|
62aa9bfb49 | ||
|
|
fcb34ed8c6 | ||
|
|
e718e119f4 | ||
|
|
eb711e44b5 | ||
|
|
ac66038bb5 | ||
|
|
17ca016791 | ||
|
|
4c8be516cf | ||
|
|
2089e9b749 | ||
|
|
9f9900751d | ||
|
|
8a6cadd38f | ||
|
|
b582b317c1 | ||
|
|
e84523359a | ||
|
|
d646403068 | ||
|
|
6fb8df41e5 | ||
|
|
2a7980965e | ||
|
|
c458892467 | ||
|
|
c67151b9c9 | ||
|
|
ef64828613 | ||
|
|
c82364df7b | ||
|
|
f89d6fc73f | ||
|
|
9fb28b5f2a | ||
|
|
d6a3e1e286 | ||
|
|
8958b2123e | ||
|
|
6550ece258 | ||
|
|
fc9526092b | ||
|
|
bb26e243d6 | ||
|
|
ad66e31d6e | ||
|
|
e357330409 | ||
|
|
f3877f3d6a | ||
|
|
2705e2a56a | ||
|
|
4fd5f2bb64 | ||
|
|
1163a445cd | ||
|
|
56d15c8ef0 | ||
|
|
abd3a8b47e | ||
|
|
c21a062bcb | ||
|
|
0ff270c625 | ||
|
|
7a35a859d9 | ||
|
|
23f397fd6b | ||
|
|
2ac3c0824b | ||
|
|
c90907ba92 | ||
|
|
5032c883b8 | ||
|
|
951e01af5a | ||
|
|
42a4227efe | ||
|
|
ebb2a65e6d | ||
|
|
6cf0756f27 | ||
|
|
40c8a3ccf7 | ||
|
|
a9a3af320e | ||
|
|
410957be46 | ||
|
|
ff4bebd806 | ||
|
|
ff276a3c9f | ||
|
|
93dca22ee5 | ||
|
|
85547a9be7 | ||
|
|
b31876ff03 | ||
|
|
6092e39303 | ||
|
|
9ad9fccb22 | ||
|
|
f5cf39907a | ||
|
|
dae4761b72 | ||
|
|
2fdb4e30ac | ||
|
|
07192c4294 | ||
|
|
eb65a45ed4 | ||
|
|
68fe69ef24 | ||
|
|
42e50798b9 | ||
|
|
d570bcef9a | ||
|
|
7b60f5bd06 | ||
|
|
9b0139032b | ||
|
|
d4ccbfc72b | ||
|
|
fe8a1d2632 | ||
|
|
fde8a4a91a | ||
|
|
a7b41b3457 | ||
|
|
179845d9b1 | ||
|
|
de837b1b03 | ||
|
|
4f0daa8143 | ||
|
|
400bdb43aa | ||
|
|
69bb4f9237 | ||
|
|
dc5f9302cd | ||
|
|
58b8e27eea | ||
|
|
23d827e4ef | ||
|
|
2d51bf4d1b | ||
|
|
26efbb4b81 | ||
|
|
0ff5bb862a | ||
|
|
7d98e5cea5 | ||
|
|
0d04200c55 | ||
|
|
6983f3b1a1 | ||
|
|
a284159a18 | ||
|
|
521a97939d | ||
|
|
795dd6149c | ||
|
|
27877820ed | ||
|
|
2cbb1a17e7 | ||
|
|
605d9737f0 | ||
|
|
2662985ce2 | ||
|
|
b31836f674 | ||
|
|
231d08ae1a | ||
|
|
d8b97ee242 | ||
|
|
71e84d82f8 | ||
|
|
dc30e35615 | ||
|
|
eb3dfeaa7b | ||
|
|
124c627fe2 | ||
|
|
0b3a4821fa | ||
|
|
1aec754308 | ||
|
|
dbf01e6b75 | ||
|
|
c10b00fc1b | ||
|
|
e2b7c11750 | ||
|
|
105dc0ac1f | ||
|
|
69dc5aaad4 | ||
|
|
2e5ba99d5f | ||
|
|
014dd37594 | ||
|
|
14a9583a3e | ||
|
|
07ad8cf4ed | ||
|
|
bfa9965056 | ||
|
|
9a06a5727e | ||
|
|
4e02f12e19 | ||
|
|
471da9aace | ||
|
|
5924d9d017 | ||
|
|
443664fc3f | ||
|
|
4ecd58dbfe | ||
|
|
cfdd9862df | ||
|
|
302af71764 | ||
|
|
e851cea6b2 | ||
|
|
b4df9c49d8 | ||
|
|
477e7820cd | ||
|
|
80b6f5e01f | ||
|
|
0a4813225d | ||
|
|
d9280fdd1c | ||
|
|
52ba22f645 | ||
|
|
357755a65e | ||
|
|
466f565e44 | ||
|
|
ba97f9c116 | ||
|
|
4c81bf164c | ||
|
|
2e9f0275d7 | ||
|
|
f9042b3c9f | ||
|
|
1688b2d9c3 | ||
|
|
9b438620ee | ||
|
|
2f7a652e22 | ||
|
|
abecc554f4 | ||
|
|
8a827b37e6 | ||
|
|
460b7c9804 | ||
|
|
fb949730ba | ||
|
|
3bce8d82e5 | ||
|
|
bbe034173f | ||
|
|
9df78f5e11 | ||
|
|
b83aca188f | ||
|
|
220bc49488 | ||
|
|
4cbe50c33d | ||
|
|
d6f70857d1 | ||
|
|
23fd4e1843 | ||
|
|
fb3c783fc4 | ||
|
|
c713b6e062 | ||
|
|
4601f67ebc | ||
|
|
de00d2aca2 | ||
|
|
674fc0d1c0 | ||
|
|
7521ed060b | ||
|
|
e56501649b | ||
|
|
7d412985ec | ||
|
|
e711b71fe9 | ||
|
|
049efad065 | ||
|
|
6bdb7c040f | ||
|
|
48874b2773 | ||
|
|
d6cbdd221c | ||
|
|
59a8a98703 | ||
|
|
ecc673b972 | ||
|
|
06c64b3be8 | ||
|
|
211dfb4b11 | ||
|
|
357a6ba1ad | ||
|
|
00814e8608 | ||
|
|
fae26daf3b | ||
|
|
6e6c18e31c | ||
|
|
ae27023283 | ||
|
|
3fa7d25ecd | ||
|
|
69ccacbd50 | ||
|
|
82c31e5710 | ||
|
|
4c9f4a4a11 | ||
|
|
24cca86813 | ||
|
|
f00b568c9c | ||
|
|
ed6b73c413 | ||
|
|
7bf493f127 | ||
|
|
224a5e024d | ||
|
|
00a2916da5 | ||
|
|
1a8c5ed6d4 | ||
|
|
70d0d7d1a1 | ||
|
|
a2822f78ab | ||
|
|
82035524b9 | ||
|
|
efe7bd9dfd | ||
|
|
f29f57aa7c | ||
|
|
ef6a34e1a7 | ||
|
|
c7ef3dda4e | ||
|
|
5d5348abca | ||
|
|
6a9cbe1f1b | ||
|
|
f569f8e334 | ||
|
|
04590f8723 | ||
|
|
b95f37f93c | ||
|
|
c0472b8aff | ||
|
|
4980d00a67 | ||
|
|
c549ae34e8 | ||
|
|
84676a9346 | ||
|
|
37b4378a06 | ||
|
|
d7f239f40b | ||
|
|
d52cbca984 | ||
|
|
d165a93fbe | ||
|
|
d06832c473 | ||
|
|
af1b3c956b | ||
|
|
206a0d488f | ||
|
|
807bf3ff10 | ||
|
|
9aea0ed78c | ||
|
|
b587052396 | ||
|
|
2e7193b11e | ||
|
|
ce86891741 | ||
|
|
e99fadf501 | ||
|
|
41990fd4a9 | ||
|
|
10dea3f3a3 | ||
|
|
9ee754720e | ||
|
|
0dfa754c59 | ||
|
|
0dbd2e139c | ||
|
|
d80522633d | ||
|
|
024b6ef16d | ||
|
|
72b5735349 | ||
|
|
0d8032e3df | ||
|
|
de20ad8a8f | ||
|
|
49e4fe8c4c | ||
|
|
7040d75687 | ||
|
|
1aa9ced157 | ||
|
|
579e2bf3d7 | ||
|
|
a0a0601b2a | ||
|
|
bf18ee5ef0 | ||
|
|
2f6333ff89 | ||
|
|
87c9f77b5a | ||
|
|
46e6413b40 | ||
|
|
2bb6c826c9 | ||
|
|
1f1f58a182 | ||
|
|
f143ee6358 | ||
|
|
67a34a142d | ||
|
|
9a73e78540 | ||
|
|
dffd28a31c | ||
|
|
42c17faa98 | ||
|
|
d8d2993d7f | ||
|
|
0eba350eb1 | ||
|
|
a7d79eed2d | ||
|
|
a4a7e754af | ||
|
|
4c48a2b378 | ||
|
|
58c79b2c7b | ||
|
|
fa0d852286 | ||
|
|
9a1ad8ee5a | ||
|
|
7983a523de | ||
|
|
8223df3486 | ||
|
|
d33f26e503 | ||
|
|
4b6ad732b1 | ||
|
|
1ebc485e14 | ||
|
|
250ebd71f6 | ||
|
|
e939e5a18c | ||
|
|
a9e0d9d629 | ||
|
|
dd3c96b81c | ||
|
|
1bd1409192 | ||
|
|
12980a6437 | ||
|
|
2bfd149fd4 | ||
|
|
67eeb75231 | ||
|
|
b139394020 | ||
|
|
b1e184bfec | ||
|
|
77c3566d9b | ||
|
|
1e89ff95e4 | ||
|
|
893d6a7e94 | ||
|
|
ae2dfdffcb | ||
|
|
0cb1cd4896 | ||
|
|
b8a254db3b | ||
|
|
0a9c428ad2 | ||
|
|
35b2649c06 | ||
|
|
2440eb168b | ||
|
|
0ddcadbdbc | ||
|
|
fc985f07bf | ||
|
|
f8ba9fe517 | ||
|
|
9e40ec8869 | ||
|
|
b562fba520 | ||
|
|
06fba240b7 | ||
|
|
8495011f29 | ||
|
|
fc2931a1f6 | ||
|
|
e729f71aa9 | ||
|
|
f0e77b49f9 | ||
|
|
21873459fe | ||
|
|
e479b85dbb | ||
|
|
b969cee2a6 | ||
|
|
895cd6e03e | ||
|
|
3d039118c6 | ||
|
|
884ac75e42 | ||
|
|
5df840db2d | ||
|
|
9a89c3d352 | ||
|
|
d21883d6c3 | ||
|
|
b106672228 | ||
|
|
76e5269716 | ||
|
|
4a8be4fc9e | ||
|
|
d79ad9717b | ||
|
|
525e1d56c1 | ||
|
|
4daf199aa5 | ||
|
|
6cc8fb8aa5 | ||
|
|
ca3fdcb294 | ||
|
|
35d4a5c255 | ||
|
|
32c8a36164 | ||
|
|
1c36d919e4 | ||
|
|
cdefd3051b | ||
|
|
39920f5fd3 | ||
|
|
9528d6dc8f | ||
|
|
8400c827fd | ||
|
|
2a957f1423 | ||
|
|
87ce6dd4b5 | ||
|
|
f5c06a9c3b | ||
|
|
81e02c3baa | ||
|
|
f83153e0cc | ||
|
|
6f789ea870 | ||
|
|
91f1edfc81 | ||
|
|
fff8a7b9d8 | ||
|
|
da378ea15f | ||
|
|
0fd9b8236a |
21
.github/CODEOWNERS
vendored
21
.github/CODEOWNERS
vendored
@@ -1,20 +1,9 @@
|
||||
# Last match in file takes precedence.
|
||||
|
||||
# Ping for all PRs
|
||||
* @Acruid @PJB3005 @Silvertorch5
|
||||
* @PJB3005 @DrSmugleaf
|
||||
|
||||
/Robust.*/Audio/Midi/ @Zumorica
|
||||
|
||||
/Robust.Client.NameGenerator @PaulRitter
|
||||
/Robust.Client.Injectors @PaulRitter
|
||||
/Robust.Generators @PaulRitter
|
||||
/Robust.Analyzers @PaulRitter
|
||||
/Robust.*/GameStates @PaulRitter
|
||||
/Robust.Shared/Analyzers @PaulRitter
|
||||
/Robust.*/Serialization @PaulRitter @DrSmugleaf
|
||||
/Robust.*/Prototypes @PaulRitter
|
||||
/Robust.Shared/GameObjects/ComponentDependencies @PaulRitter
|
||||
/Robust.*/Containers @PaulRitter
|
||||
|
||||
# Be they Fluent translations or Freemarker templates, I know them both!
|
||||
*.ftl @RemieRichards
|
||||
# commands commands commands commands
|
||||
**/Toolshed/** @moonheart08
|
||||
*Command.cs @moonheart08
|
||||
*Commands.cs @moonheart08
|
||||
|
||||
4
.github/workflows/build-docfx.yml
vendored
4
.github/workflows/build-docfx.yml
vendored
@@ -7,12 +7,12 @@ jobs:
|
||||
docfx:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/build-test.yml
vendored
4
.github/workflows/build-test.yml
vendored
@@ -15,12 +15,12 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Install dependencies
|
||||
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -35,12 +35,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
4
.github/workflows/publish-client.yml
vendored
4
.github/workflows/publish-client.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
||||
$ver = [regex]::Match($env:GITHUB_REF, "refs/tags/v?(.+)").Groups[1].Value
|
||||
echo ("::set-output name=version::{0}" -f $ver)
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
|
||||
6
.github/workflows/test-content.yml
vendored
6
.github/workflows/test-content.yml
vendored
@@ -13,13 +13,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out content
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
repository: space-wizards/space-station-14
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Disable submodule autoupdate
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --configuration Release --no-restore
|
||||
run: dotnet build --configuration Tools --no-restore
|
||||
- name: Content.Tests
|
||||
run: dotnet test --no-build Content.Tests/Content.Tests.csproj -v n
|
||||
- name: Content.IntegrationTests
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
</Project>
|
||||
|
||||
Submodule Lidgren.Network/Lidgren.Network updated: 13bd6f1ad2...f19cea8010
@@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AssemblyName>SpaceWizards.Lidgren.Network</AssemblyName>
|
||||
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
@@ -9,6 +8,8 @@
|
||||
<DefaultItemExcludes>Lidgren.Network/**/*</DefaultItemExcludes>
|
||||
<DefineConstants>$(DefineConstants);USE_RELEASE_STATISTICS</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<SkipRobustAnalyzer>true</SkipRobustAnalyzer>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -18,4 +19,5 @@
|
||||
<Compile Remove="Lidgren.Network\Lidgren.Network\obj\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../MSBuild/Robust.Properties.targets" />
|
||||
</Project>
|
||||
|
||||
5
MSBuild/Robust.CompNetworkGenerator.targets
Normal file
5
MSBuild/Robust.CompNetworkGenerator.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Shared.CompNetworkGenerator\Robust.Shared.CompNetworkGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
37
MSBuild/Robust.Configurations.props
Normal file
37
MSBuild/Robust.Configurations.props
Normal file
@@ -0,0 +1,37 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- Build configurations -->
|
||||
<PropertyGroup>
|
||||
<!-- Avoid SDK defining DEBUG/RELEASE/DEBUGOPT/TOOLS itself. -->
|
||||
<DisableImplicitConfigurationDefines>true</DisableImplicitConfigurationDefines>
|
||||
|
||||
<!-- Project configurations -->
|
||||
<Configurations>Debug;Release;Tools;DebugOpt</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<!-- Debug configuration: asserts, tools, no optimizations -->
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<DebugSymbols Condition="'$(DebugSymbols)' == ''">true</DebugSymbols>
|
||||
<Optimize Condition="'$(Optimize)' == ''">false</Optimize>
|
||||
<RobustToolsBuild Condition="'$(RobustToolsBuild)' == ''">true</RobustToolsBuild>
|
||||
<DefineConstants>DEBUG;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<!-- DebugOpt configuration: asserts, tools, optimizations -->
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'DebugOpt'">
|
||||
<DebugSymbols Condition="'$(DebugSymbols)' == ''">true</DebugSymbols>
|
||||
<Optimize Condition="'$(Optimize)' == ''">true</Optimize>
|
||||
<RobustToolsBuild Condition="'$(RobustToolsBuild)' == ''">true</RobustToolsBuild>
|
||||
<DefineConstants>DEBUG;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<!-- Tools configuration: no asserts, tools, optimizations -->
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Tools'">
|
||||
<DebugSymbols Condition="'$(DebugSymbols)' == ''">true</DebugSymbols>
|
||||
<Optimize Condition="'$(Optimize)' == ''">true</Optimize>
|
||||
<RobustToolsBuild Condition="'$(RobustToolsBuild)' == ''">true</RobustToolsBuild>
|
||||
</PropertyGroup>
|
||||
<!-- Release configuration: no asserts, no tools, optimizations -->
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<Optimize Condition="'$(Optimize)' == ''">true</Optimize>
|
||||
<RobustToolsBuild Condition="'$(RobustToolsBuild)' == ''">false</RobustToolsBuild>
|
||||
<DefineConstants>RELEASE;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -23,7 +23,7 @@
|
||||
<PropertyGroup Condition="'$(FullRelease)' != 'True'">
|
||||
<DefineConstants>$(DefineConstants);DEVELOPMENT</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release' Or '$(Configuration)' == 'Tools'">
|
||||
<DefineConstants>$(DefineConstants);EXCEPTION_TOLERANCE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(EnableClientScripting)' == 'True'">
|
||||
@@ -32,4 +32,7 @@
|
||||
<PropertyGroup Condition="'$(UseSystemSqlite)' == 'True'">
|
||||
<DefineConstants>$(DefineConstants);USE_SYSTEM_SQLITE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RobustToolsBuild)' == 'true'">
|
||||
<DefineConstants>$(DefineConstants);TOOLS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
35
MSBuild/Robust.Platform.props
Normal file
35
MSBuild/Robust.Platform.props
Normal file
@@ -0,0 +1,35 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- MSBuild hurts and I can't find a foolproof way to detect platform. -->
|
||||
<PropertyGroup>
|
||||
<OS Condition="'$(OS)' == ''">Windows_NT</OS>
|
||||
</PropertyGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(OS)' != 'Unix'">
|
||||
<PropertyGroup>
|
||||
<ActualOS>Windows</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<!-- Folders that *probably* only exist on MacOS and not Linux. -->
|
||||
<When Condition="Exists('/Volumes') And Exists('/System') And Exists('/Library')" >
|
||||
<PropertyGroup>
|
||||
<ActualOS>MacOS</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$([MSBuild]::IsOSPlatform('FreeBSD'))">
|
||||
<PropertyGroup>
|
||||
<ActualOS>FreeBSD</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<ActualOS>Linux</ActualOS>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<PropertyGroup>
|
||||
<TargetOS Condition="'$(TargetOS)' == ''">$(ActualOS)</TargetOS>
|
||||
<Python>python3</Python>
|
||||
<Python Condition="'$(ActualOS)' == 'Windows'">py -3</Python>
|
||||
<UseSystemSqlite Condition="'$(TargetOS)' == 'FreeBSD'">True</UseSystemSqlite>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,40 +1,31 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<Import Project="Robust.Custom.targets" Condition="Exists('Robust.Custom.targets')"/>
|
||||
<!-- MSBuild hurts and I can't find a foolproof way to detect platform. -->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- Properties for both engine and content. -->
|
||||
<!-- Import this at the end of any project files in Robust and Content. -->
|
||||
|
||||
<PropertyGroup>
|
||||
<OS Condition="'$(OS)' == ''">Windows_NT</OS>
|
||||
</PropertyGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(OS)' != 'Unix'">
|
||||
<PropertyGroup>
|
||||
<ActualOS>Windows</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<!-- Folders that *probably* only exist on MacOS and not Linux. -->
|
||||
<When Condition="Exists('/Volumes') And Exists('/System') And Exists('/Library')" >
|
||||
<PropertyGroup>
|
||||
<ActualOS>MacOS</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="$([MSBuild]::IsOSPlatform('FreeBSD'))">
|
||||
<PropertyGroup>
|
||||
<ActualOS>FreeBSD</ActualOS>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<ActualOS>Linux</ActualOS>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<PropertyGroup>
|
||||
<TargetOS Condition="'$(TargetOS)' == ''">$(ActualOS)</TargetOS>
|
||||
<Python>python3</Python>
|
||||
<Python Condition="'$(ActualOS)' == 'Windows'">py -3</Python>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<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>
|
||||
|
||||
<Import Project="Robust.Custom.targets" Condition="Exists('Robust.Custom.targets')"/>
|
||||
|
||||
<!-- Configuration logic -->
|
||||
<Import Project="Robust.Configurations.props" />
|
||||
|
||||
<!-- Some platform management logic -->
|
||||
<Import Project="Robust.Platform.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableClientScripting>False</EnableClientScripting>
|
||||
<!-- Client scripting is only enabled on tools builds for security and size reasons. -->
|
||||
<EnableClientScripting Condition="'$(RobustToolsBuild)' == 'true'">True</EnableClientScripting>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- built-in define constants -->
|
||||
<Import Project="Robust.DefineConstants.targets" />
|
||||
|
||||
<!-- analyzer -->
|
||||
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- serialization generator -->
|
||||
<Import Project="Robust.Serialization.Generator.targets" />
|
||||
</Project>
|
||||
|
||||
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
5
MSBuild/Robust.Serialization.Generator.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
</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>false</RobustUseExternalMSBuild>
|
||||
<RobustUseExternalMSBuild>true</RobustUseExternalMSBuild>
|
||||
<_RobustUseExternalMSBuild>$(RobustUseExternalMSBuild)</_RobustUseExternalMSBuild>
|
||||
<_RobustUseExternalMSBuild Condition="'$(_RobustForceInternalMSBuild)' == 'true'">false</_RobustUseExternalMSBuild>
|
||||
</PropertyGroup>
|
||||
@@ -19,10 +19,17 @@
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- XamlIL does not make use of special Robust configurations like DebugOpt. Convert these down. -->
|
||||
<PropertyGroup>
|
||||
<RobustInjectorsConfiguration>$(Configuration)</RobustInjectorsConfiguration>
|
||||
<RobustInjectorsConfiguration Condition="'$(Configuration)' == 'DebugOpt'">Debug</RobustInjectorsConfiguration>
|
||||
<RobustInjectorsConfiguration Condition="'$(Configuration)' == 'Tools'">Release</RobustInjectorsConfiguration>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask
|
||||
Condition="'$(_RobustUseExternalMSBuild)' != 'true' And $(DesignTimeBuild) != true"
|
||||
TaskName="CompileRobustXamlTask"
|
||||
AssemblyFile="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(Configuration)\netstandard2.0\Robust.Client.Injectors.dll"/>
|
||||
AssemblyFile="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(RobustInjectorsConfiguration)\netstandard2.0\Robust.Client.Injectors.dll"/>
|
||||
<Target
|
||||
Name="CompileRobustXaml"
|
||||
Condition="Exists('@(IntermediateAssembly)')"
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<SkipRobustAnalyzer>true</SkipRobustAnalyzer>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.DefineConstants.targets" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="System.Memory" Version="4.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
</Project>
|
||||
|
||||
2260
RELEASE-NOTES.md
2260
RELEASE-NOTES.md
File diff suppressed because it is too large
Load Diff
@@ -1546,6 +1546,31 @@
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
- name: FastNoise
|
||||
license: |
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2020 Jordan Peck (jordan.me2@gmail.com)
|
||||
Copyright(c) 2020 Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: OpenAL soft
|
||||
license: |
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
|
||||
202
Resources/EngineFonts/NotoSans/LICENSE.txt
Normal file
202
Resources/EngineFonts/NotoSans/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
BIN
Resources/EngineFonts/NotoSans/NotoSans-Regular.ttf
Normal file
BIN
Resources/EngineFonts/NotoSans/NotoSans-Regular.ttf
Normal file
Binary file not shown.
BIN
Resources/EngineFonts/NotoSans/NotoSansMono-Regular.ttf
Normal file
BIN
Resources/EngineFonts/NotoSans/NotoSansMono-Regular.ttf
Normal file
Binary file not shown.
34
Resources/EnginePrototypes/Debug/rotation.yml
Normal file
34
Resources/EnginePrototypes/Debug/rotation.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
- type: entity
|
||||
id: debugRotation
|
||||
abstract: true
|
||||
categories: [ debug ]
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
visible: true
|
||||
sprite: debugRotation.rsi
|
||||
state: direction1
|
||||
|
||||
- type: entity
|
||||
id: debugRotation1
|
||||
parent: debugRotation
|
||||
name: dbg_rotation1
|
||||
components:
|
||||
- type: Sprite
|
||||
state: direction1
|
||||
|
||||
- type: entity
|
||||
id: debugRotation4
|
||||
parent: debugRotation
|
||||
name: dbg_rotation4
|
||||
components:
|
||||
- type: Sprite
|
||||
state: direction4
|
||||
|
||||
- type: entity
|
||||
id: debugRotationTex
|
||||
parent: debugRotation
|
||||
name: dbg_rotationTex
|
||||
components:
|
||||
- type: Sprite
|
||||
state: direction1
|
||||
@@ -1,3 +1,44 @@
|
||||
- type: uiTheme
|
||||
id: Default
|
||||
path: /textures/interface/Default
|
||||
path: /Textures/Interface/Default/
|
||||
colors:
|
||||
# Root
|
||||
rootBackground: "#000000"
|
||||
|
||||
# Windows
|
||||
windowBackground: "#111111"
|
||||
windowBorder: "#444444"
|
||||
windowHeader: "#001e3d"
|
||||
|
||||
windowCloseButton: "#FFFFFF"
|
||||
windowCloseButtonHover: "#FF7F7F"
|
||||
windowCloseButtonPressed: "#FF0000"
|
||||
|
||||
# Scrollbars
|
||||
scrollBarDefault: "#80808059"
|
||||
scrollBarHovered: "#8C8C8C59"
|
||||
scrollBarGrabbed: "#8C8C8C59"
|
||||
|
||||
# Buttons
|
||||
buttonBackground: "#171717"
|
||||
buttonBackgroundHovered: "#272727"
|
||||
buttonBackgroundPressed: "#173717"
|
||||
buttonBorder: "#444444"
|
||||
buttonBorderHovered: "#444444"
|
||||
buttonBorderPressed: "#447044"
|
||||
buttonBackgroundDisabled: "#333333"
|
||||
buttonBorderDisabled: "#222222"
|
||||
|
||||
# LineEdit
|
||||
lineEditUneditableText: "#444444"
|
||||
lineEditPlaceholderText: "#7d7d7d"
|
||||
lineEditBackground: "#000000"
|
||||
lineEditBorder: "#444444"
|
||||
|
||||
# TabContainer
|
||||
tabContainerBackground: "#000000"
|
||||
tabContainerBorder: "#444444"
|
||||
tabContainerActiveTabBackground: "#173717"
|
||||
tabContainerActiveTabBorder: "#447044"
|
||||
tabContainerInactiveTabBackground: "#171717"
|
||||
tabContainerInactiveTabBorder: "#444444"
|
||||
|
||||
17
Resources/EnginePrototypes/entityCategory.yml
Normal file
17
Resources/EnginePrototypes/entityCategory.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# debug related entities
|
||||
- type: entityCategory
|
||||
id: debug
|
||||
name: entity-category-name-debug
|
||||
description: entity-category-desc-debug
|
||||
|
||||
# entities that spawn other entities
|
||||
- type: entityCategory
|
||||
id: spawner
|
||||
name: entity-category-name-spawner
|
||||
description: entity-category-desc-spawner
|
||||
|
||||
# entities that should be hidden from the spawn menu
|
||||
- type: entityCategory
|
||||
id: hideSpawnMenu
|
||||
name: entity-category-name-hide
|
||||
description: entity-category-desc-hide
|
||||
@@ -11,17 +11,21 @@ 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.
|
||||
|
||||
cmd-error-file-not-found = Could not find file: {$file}.
|
||||
cmd-error-dir-not-found = Could not find directory: {$dir}.
|
||||
|
||||
cmd-failure-no-attached-entity = There is no entity attached to this shell.
|
||||
|
||||
## 'help' command
|
||||
cmd-help-desc = Display general help or help text for a specific command
|
||||
cmd-help-help = Usage: help [command name]
|
||||
cmd-oldhelp-desc = Display general help or help text for a specific command
|
||||
cmd-oldhelp-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]
|
||||
cmd-oldhelp-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-oldhelp-unknown = Unknown command: { $command }
|
||||
cmd-oldhelp-top = { $command } - { $description }
|
||||
cmd-oldhelp-invalid-args = Invalid amount of arguments.
|
||||
cmd-oldhelp-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Gets or sets a CVar.
|
||||
@@ -377,8 +381,11 @@ cmd-netaudit-help = netaudit
|
||||
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-desc = Teleports the current player or the specified players/entities to the location of the first player/entity.
|
||||
cmd-tpto-help = tpto <username|uid> [username|uid]...
|
||||
cmd-tpto-destination-hint = destination (uid or username)
|
||||
cmd-tpto-victim-hint = entity to teleport (uid or username)
|
||||
cmd-tpto-parse-error = Cant resolve entity or player: {$str}
|
||||
|
||||
cmd-listplayers-desc = Lists all players currently connected.
|
||||
cmd-listplayers-help = listplayers
|
||||
@@ -522,14 +529,35 @@ 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-vvwrite-help = Usage: vvwrite <path>
|
||||
|
||||
cmd-vv-desc = Opens View Variables (VV).
|
||||
cmd-vv-desc = Usage: vv <path|entity ID|guihover>
|
||||
cmd-vv-help = Usage: vv <path|entity ID|guihover>
|
||||
|
||||
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
|
||||
cmd-vvinvoke-desc = Usage: vvinvoke <path> [arguments...]
|
||||
cmd-vvinvoke-help = Usage: vvinvoke <path> [arguments...]
|
||||
|
||||
cmd-dump_dependency_injectors-desc = Dump IoCManager's dependency injector cache.
|
||||
cmd-dump_dependency_injectors-help = Usage: dump_dependency_injectors
|
||||
cmd-dump_dependency_injectors-total-count = Total count: { $total }
|
||||
|
||||
cmd-dump_netserializer_type_map-desc = Dump NetSerializer's type map and serializer hash.
|
||||
cmd-dump_netserializer_type_map-help = Usage: dump_netserializer_type_map
|
||||
|
||||
cmd-hub_advertise_now-desc = Immediately advertise to the master hub server
|
||||
cmd-hub_advertise_now-help = Usage: hub_advertise_now
|
||||
|
||||
cmd-echo-desc = Echo arguments back to the console
|
||||
cmd-echo-help = Usage: echo "<message>"
|
||||
|
||||
## 'vfs_ls' command
|
||||
cmd-vfs_ls-desc = List directory contents in the VFS.
|
||||
cmd-vfs_ls-help = Usage: vfs_list <path>
|
||||
Example:
|
||||
vfs_list /Assemblies
|
||||
|
||||
cmd-vfs_ls-err-args = Need exactly 1 argument.
|
||||
cmd-vfs_ls-hint-path = <path>
|
||||
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
|
||||
cmd-reloadtiletextures-help = Usage: reloadtiletextures
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
entity-spawn-window-title = Entity Spawn Panel
|
||||
entity-spawn-window-search-bar-placeholder = search
|
||||
entity-spawn-window-clear-button = Clear
|
||||
entity-spawn-window-replace-button-text = Replace
|
||||
entity-spawn-window-erase-button-text = Erase Mode
|
||||
entity-spawn-window-override-menu-tooltip = Override placement
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
debug-builtin-connection-screen-invalid-username-with-reason = The given username is invalid: {$invalidreason}
|
||||
debug-builtin-connection-screen-invalid-username = Invalid Username.
|
||||
debug-builtin-connection-screen-failed-to-connect = Failed to connect: {$reason}
|
||||
5
Resources/Locale/en-US/discordRPC.ftl
Normal file
5
Resources/Locale/en-US/discordRPC.ftl
Normal file
@@ -0,0 +1,5 @@
|
||||
discord-rpc-in-main-menu = In Main Menu
|
||||
discord-rpc-in-main-menu-logo-text = I think coolsville SUCKS
|
||||
discord-rpc-character = Username: {$username}
|
||||
discord-rpc-on-server = On Server: {$servername}
|
||||
discord-rpc-players = Players: {$players}/{$maxplayers}
|
||||
8
Resources/Locale/en-US/entity-category.ftl
Normal file
8
Resources/Locale/en-US/entity-category.ftl
Normal file
@@ -0,0 +1,8 @@
|
||||
entity-category-name-debug = Debug
|
||||
entity-category-desc-debug = Entity prototypes intended for debugging & testing.
|
||||
|
||||
entity-category-name-spawner = Spawner
|
||||
entity-category-desc-spawner = Entity prototypes that spawn other entities.
|
||||
|
||||
entity-category-name-hide = Hidden
|
||||
entity-category-desc-hide = Entity prototypes that should be hidden from the spawn menu
|
||||
@@ -18,6 +18,15 @@ input-key-F12 = F12
|
||||
input-key-F13 = F13
|
||||
input-key-F14 = F14
|
||||
input-key-F15 = F15
|
||||
input-key-F16 = F16
|
||||
input-key-F17 = F17
|
||||
input-key-F18 = F18
|
||||
input-key-F19 = F19
|
||||
input-key-F20 = F20
|
||||
input-key-F21 = F21
|
||||
input-key-F22 = F22
|
||||
input-key-F23 = F23
|
||||
input-key-F24 = F24
|
||||
input-key-Pause = Pause
|
||||
input-key-Left = Left
|
||||
input-key-Up = Up
|
||||
@@ -25,7 +34,22 @@ input-key-Down = Down
|
||||
input-key-Right = Right
|
||||
input-key-Space = Space
|
||||
input-key-Return = Return
|
||||
input-key-NumpadEnter = Num Enter
|
||||
input-key-NumpadEnter = Numpad Enter
|
||||
input-key-NumpadNum0 = Numpad 0
|
||||
input-key-NumpadNum1 = Numpad 1
|
||||
input-key-NumpadNum2 = Numpad 2
|
||||
input-key-NumpadNum3 = Numpad 3
|
||||
input-key-NumpadNum4 = Numpad 4
|
||||
input-key-NumpadNum5 = Numpad 5
|
||||
input-key-NumpadNum6 = Numpad 6
|
||||
input-key-NumpadNum7 = Numpad 7
|
||||
input-key-NumpadNum8 = Numpad 8
|
||||
input-key-NumpadNum9 = Numpad 9
|
||||
input-key-NumpadAdd = Numpad Add
|
||||
input-key-NumpadSubtract = Numpad Subtract
|
||||
input-key-NumpadDivide = Numpad Divide
|
||||
input-key-NumpadMultiply = Numpad Multiply
|
||||
input-key-NumpadDecimal = Numpad Decimal
|
||||
input-key-BackSpace = Backspace
|
||||
input-key-Tab = Tab
|
||||
input-key-PageUp = Page Up
|
||||
|
||||
59
Resources/Locale/en-US/replays.ftl
Normal file
59
Resources/Locale/en-US/replays.ftl
Normal file
@@ -0,0 +1,59 @@
|
||||
# Playback Commands
|
||||
|
||||
cmd-replay-play-desc = Resume replay playback.
|
||||
cmd-replay-play-help = replay_play
|
||||
|
||||
cmd-replay-pause-desc = Pause replay playback
|
||||
cmd-replay-pause-help = replay_pause
|
||||
|
||||
cmd-replay-toggle-desc = Resume or pause replay playback.
|
||||
cmd-replay-toggle-help = replay_toggle
|
||||
|
||||
cmd-replay-stop-desc = Stop and unload a replay.
|
||||
cmd-replay-stop-help = replay_stop
|
||||
|
||||
cmd-replay-load-desc = Load and start a replay.
|
||||
cmd-replay-load-help = replay_load <replay folder>
|
||||
cmd-replay-load-hint = Replay folder
|
||||
|
||||
cmd-replay-skip-desc = Skip forwards or backwards in time.
|
||||
cmd-replay-skip-help = replay_skip <tick or timespan>
|
||||
cmd-replay-skip-hint = Ticks or timespan (HH:MM:SS).
|
||||
|
||||
cmd-replay-set-time-desc = Jump forwards or backwards to some specific time.
|
||||
cmd-replay-set-time-help = replay_set <tick or time>
|
||||
cmd-replay-set-time-hint = Tick or timespan (HH:MM:SS), starting from
|
||||
|
||||
cmd-replay-error-time = "{$time}" is not an integer or timespan.
|
||||
cmd-replay-error-args = Wrong number of arguments.
|
||||
cmd-replay-error-no-replay = Not currently playing a replay.
|
||||
cmd-replay-error-already-loaded = A replay is already loaded.
|
||||
cmd-replay-error-run-level = You cannot load a replay while connected to a server.
|
||||
|
||||
# Recording commands
|
||||
|
||||
cmd-replay-recording-start-desc = Starts a replay recording, optionally with some time limit.
|
||||
cmd-replay-recording-start-help = Usage: replay_recording_start [name] [overwrite] [time limit]
|
||||
cmd-replay-recording-start-success = Started recording a replay.
|
||||
cmd-replay-recording-start-already-recording = Already recording a replay.
|
||||
cmd-replay-recording-start-error = An error occurred while trying to start the recording.
|
||||
cmd-replay-recording-start-hint-time = [time limit (minutes)]
|
||||
cmd-replay-recording-start-hint-name = [name]
|
||||
cmd-replay-recording-start-hint-overwrite = [overwrite (bool)]
|
||||
|
||||
cmd-replay-recording-stop-desc = Stops a replay recording.
|
||||
cmd-replay-recording-stop-help = Usage: replay_recording_stop
|
||||
cmd-replay-recording-stop-success = Stopped recording a replay.
|
||||
cmd-replay-recording-stop-not-recording = Not currently recording a replay.
|
||||
|
||||
cmd-replay-recording-stats-desc = Displays information about the current replay recording.
|
||||
cmd-replay-recording-stats-help = Usage: replay_recording_stats
|
||||
cmd-replay-recording-stats-result = Duration: {$time} min, Ticks: {$ticks}, Size: {$size} MB, rate: {$rate} MB/min.
|
||||
|
||||
|
||||
# Time Control UI
|
||||
replay-time-box-scrubbing-label = Dynamic Scrubbing
|
||||
replay-time-box-replay-time-label = Recording Time: {$current} / {$end} ({$percentage}%)
|
||||
replay-time-box-server-time-label = Server Time: {$current} / {$end}
|
||||
replay-time-box-index-label = Index: {$current} / {$total}
|
||||
replay-time-box-tick-label = Tick: {$current} / {$total}
|
||||
423
Resources/Locale/en-US/toolshed-commands.ftl
Normal file
423
Resources/Locale/en-US/toolshed-commands.ftl
Normal file
@@ -0,0 +1,423 @@
|
||||
command-description-tpto =
|
||||
Teleport the given entities to some target entity.
|
||||
command-description-player-list =
|
||||
Returns a list of all player sessions.
|
||||
command-description-player-self =
|
||||
Returns the current player session.
|
||||
command-description-player-imm =
|
||||
Returns the session associated with the player given as argument.
|
||||
command-description-player-entity =
|
||||
Returns the entities of the input sessions.
|
||||
command-description-self =
|
||||
Returns the current attached entity.
|
||||
command-description-physics-velocity =
|
||||
Returns the velocity of the input entities.
|
||||
command-description-physics-angular-velocity =
|
||||
Returns the angular velocity of the input entities.
|
||||
command-description-buildinfo =
|
||||
Provides information about the build of the game.
|
||||
command-description-cmd-list =
|
||||
Returns a list of all commands, for this side.
|
||||
command-description-explain =
|
||||
Explains the given expression, providing command descriptions and signatures.
|
||||
command-description-search =
|
||||
Searches through the input for the provided value.
|
||||
command-description-stopwatch =
|
||||
Measures the execution time of the given expression.
|
||||
command-description-types-consumers =
|
||||
Provides all commands that can consume the given type.
|
||||
command-description-types-tree =
|
||||
Debug tool to return all types the command interpreter can downcast the input to.
|
||||
command-description-types-gettype =
|
||||
Returns the type of the input.
|
||||
command-description-types-fullname =
|
||||
Returns the full name of the input type according to CoreCLR.
|
||||
command-description-as =
|
||||
Casts the input to the given type.
|
||||
Effectively a type hint if you know the type but the interpreter does not.
|
||||
command-description-count =
|
||||
Counts the amount of entries in it's input, returning an integer.
|
||||
command-description-map =
|
||||
Maps the input over the given block, with the provided expected return type.
|
||||
This command may be modified to not need an explicit return type in the future.
|
||||
command-description-select =
|
||||
Selects N objects or N% of objects from the input.
|
||||
One can additionally invert this command with not to make it select everything except N objects instead.
|
||||
command-description-comp =
|
||||
Returns the given component from the input entities, discarding entities without that component.
|
||||
command-description-delete =
|
||||
Deletes the input entities.
|
||||
command-description-ent =
|
||||
Returns the provided entity ID.
|
||||
command-description-entities =
|
||||
Returns all entities on the server.
|
||||
command-description-paused =
|
||||
Filters the input entities by whether or not they are paused.
|
||||
This command can be inverted with not.
|
||||
command-description-with =
|
||||
Filters the input entities by whether or not they have the given component.
|
||||
This command can be inverted with not.
|
||||
command-description-fuck =
|
||||
Throws an exception.
|
||||
command-description-ecscomp-listty =
|
||||
Lists every type of component registered.
|
||||
command-description-cd =
|
||||
Changes the session's current directory to the given relative or absolute path.
|
||||
command-description-ls-here =
|
||||
Lists the contents of the current directory.
|
||||
command-description-ls-in =
|
||||
Lists the contents of the given relative or absolute path.
|
||||
command-description-methods-get =
|
||||
Returns all methods associated with the input type.
|
||||
command-description-methods-overrides =
|
||||
Returns all methods overriden on the input type.
|
||||
command-description-methods-overridesfrom =
|
||||
Returns all methods overriden from the given type on the input type.
|
||||
command-description-cmd-moo =
|
||||
Asks the important questions.
|
||||
command-description-cmd-descloc =
|
||||
Returns the localization string for a command's description.
|
||||
command-description-cmd-getshim =
|
||||
Returns a command's execution shim.
|
||||
command-description-help =
|
||||
Provides a quick rundown of how to use toolshed.
|
||||
command-description-ioc-registered =
|
||||
Returns all the types registered with IoCManager on the current thread (usually the game thread)
|
||||
command-description-ioc-get =
|
||||
Gets an instance of an IoC registration.
|
||||
command-description-loc-tryloc =
|
||||
Tries to get a localization string, returning null if unable.
|
||||
command-description-loc-loc =
|
||||
Gets a localization string, returning the unlocalized string if unable.
|
||||
command-description-physics-angular_velocity =
|
||||
Returns the angular velocity of the given entities.
|
||||
command-description-vars =
|
||||
Provides a list of all variables set in this session.
|
||||
command-description-any =
|
||||
Returns true if there's any values in the input, otherwise false.
|
||||
command-description-ArrowCommand =
|
||||
Assigns the input to a variable.
|
||||
command-description-isempty =
|
||||
Returns true if the input is empty, otherwise false.
|
||||
command-description-isnull =
|
||||
Returns true if the input is null, otherwise false.
|
||||
command-description-unique =
|
||||
Filters the input sequence for uniqueness, removing duplicate values.
|
||||
command-description-where =
|
||||
Given some input sequence IEnumerable<T>, takes a block of signature T -> bool that decides if each input value should be included in the output sequence.
|
||||
command-description-do =
|
||||
Backwards compatibility with BQL, applies the given old commands over the input sequence.
|
||||
command-description-named =
|
||||
Filters the input entities by their name, with the regex ^selector$.
|
||||
command-description-prototyped =
|
||||
Filters the input entities by their prototype.
|
||||
command-description-nearby =
|
||||
Creates a new list of all entities nearby the inputs within the given range.
|
||||
command-description-first =
|
||||
Returns the first entry of the given enumerable.
|
||||
command-description-splat =
|
||||
"Splats" a block, value, or variable, creating N copies of it in a list.
|
||||
command-description-val =
|
||||
Casts the given value, block, or variable to the given type. This is mostly a workaround for current limitations of variables.
|
||||
command-description-actor-controlled =
|
||||
Filters entities by whether or not they're actively controlled.
|
||||
command-description-actor-session =
|
||||
Returns the sessions associated with the input entities.
|
||||
command-description-physics-parent =
|
||||
Returns the parent(s) of the input entities.
|
||||
command-description-emplace =
|
||||
Runs the given block over it's inputs, with the input value placed into the variable $value within the block.
|
||||
Additionally breaks out $wx, $wy, $proto, $desc, $name, and $paused for entities.
|
||||
Can also have breakout values for other types, consult the documentation for that type for further info.
|
||||
command-description-AddCommand =
|
||||
Performs numeric addition.
|
||||
command-description-SubtractCommand =
|
||||
Performs numeric subtraction.
|
||||
command-description-MultiplyCommand =
|
||||
Performs numeric multiplication.
|
||||
command-description-DivideCommand =
|
||||
Performs numeric division.
|
||||
command-description-min =
|
||||
Returns the minimum of two values.
|
||||
command-description-max =
|
||||
Returns the maximum of two values.
|
||||
command-description-BitAndCommand =
|
||||
Performs bitwise AND.
|
||||
command-description-BitOrCommand =
|
||||
Performs bitwise OR.
|
||||
command-description-BitXorCommand =
|
||||
Performs bitwise XOR.
|
||||
command-description-neg =
|
||||
Negates the input.
|
||||
command-description-GreaterThanCommand =
|
||||
Performs a greater-than comparison, x > y.
|
||||
command-description-LessThanCommand =
|
||||
Performs a less-than comparison, x < y.
|
||||
command-description-GreaterThanOrEqualCommand =
|
||||
Performs a greater-than-or-equal comparison, x >= y.
|
||||
command-description-LessThanOrEqualCommand =
|
||||
Performs a less-than-or-equal comparison, x <= y.
|
||||
command-description-EqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are equal.
|
||||
command-description-NotEqualCommand =
|
||||
Performs an equality comparison, returning true if the inputs are not equal.
|
||||
command-description-append =
|
||||
Appends a value to the input enumerable.
|
||||
command-description-DefaultIfNullCommand =
|
||||
Replaces the input with the type's default value if it is null, albeit only for value types (not objects).
|
||||
command-description-OrValueCommand =
|
||||
If the input is null, uses the provided alternate value.
|
||||
command-description-DebugPrintCommand =
|
||||
Prints the given value transparently, for debug prints in a command run.
|
||||
command-description-i =
|
||||
Integer constant.
|
||||
command-description-f =
|
||||
Float constant.
|
||||
command-description-s =
|
||||
String constant.
|
||||
command-description-b =
|
||||
Bool constant.
|
||||
command-description-join =
|
||||
Joins two sequences together into one sequence.
|
||||
command-description-reduce =
|
||||
Given a block to use as a reducer, turns a sequence into a single value.
|
||||
The left hand side of the block is implied, and the right hand is stored in $value.
|
||||
command-description-rep =
|
||||
Repeats the input value N times to form a sequence.
|
||||
command-description-take =
|
||||
Takes N values from the input sequence
|
||||
command-description-spawn-at =
|
||||
Spawns an entity at the given coordinates.
|
||||
command-description-spawn-on =
|
||||
Spawns an entity on the given entity, at it's coordinates.
|
||||
command-description-spawn-attached =
|
||||
Spawns an entity attached to the given entity, at (0 0) relative to it.
|
||||
command-description-mappos =
|
||||
Returns an entity's coordinates relative to it's current map.
|
||||
command-description-pos =
|
||||
Returns an entity's coordinates.
|
||||
command-description-tp-coords =
|
||||
Teleports the target to the given coordinates.
|
||||
command-description-tp-to =
|
||||
Teleports the target to the given other entity.
|
||||
command-description-tp-into =
|
||||
Teleports the target "into" the given other entity, attaching it at (0 0) relative to it.
|
||||
command-description-comp-get =
|
||||
Gets the given component from the given entity.
|
||||
command-description-comp-add =
|
||||
Adds the given component to the given entity.
|
||||
command-description-comp-ensure =
|
||||
Ensures the given entity has the given component.
|
||||
command-description-comp-has =
|
||||
Check if the given entity has the given component.
|
||||
command-description-AddVecCommand =
|
||||
Adds a scalar (single value) to every element in the input.
|
||||
command-description-SubVecCommand =
|
||||
Subtracts a scalar (single value) from every element in the input.
|
||||
command-description-MulVecCommand =
|
||||
Multiplies a scalar (single value) by every element in the input.
|
||||
command-description-DivVecCommand =
|
||||
Divides every element in the input by a scalar (single value).
|
||||
command-description-rng-to =
|
||||
Returns a number from its input to its argument (i.e. n..m inclusive)
|
||||
command-description-rng-from =
|
||||
Returns a number to its input from its argument (i.e. m..n inclusive)
|
||||
command-description-rng-prob =
|
||||
Returns a boolean based on the input probability/chance (from 0 to 1)
|
||||
command-description-sum =
|
||||
Computes the sum of the input.
|
||||
command-description-bin =
|
||||
"Bins" the input, counting up how many times each unique element occurs.
|
||||
command-description-extremes =
|
||||
Returns the two extreme ends of a list, interwoven.
|
||||
command-description-sortby =
|
||||
Sorts the input least to greatest by the computed key.
|
||||
command-description-sortmapby =
|
||||
Sorts the input least to greatest by the computed key, replacing the value with it's computed key afterward.
|
||||
command-description-sort =
|
||||
Sorts the input least to greatest.
|
||||
command-description-sortdownby =
|
||||
Sorts the input greatest to least by the computed key.
|
||||
command-description-sortmapdownby =
|
||||
Sorts the input greatest to least by the computed key, replacing the value with it's computed key afterward.
|
||||
command-description-sortdown =
|
||||
Sorts the input greatest to least.
|
||||
command-description-iota =
|
||||
Returns a list of numbers 1 to N.
|
||||
command-description-to =
|
||||
Returns a list of numbers N to M.
|
||||
command-description-curtick =
|
||||
The current game tick.
|
||||
command-description-curtime =
|
||||
The current game time (a TimeSpan)
|
||||
command-description-realtime =
|
||||
The current realtime since startup (a TimeSpan)
|
||||
command-description-servertime =
|
||||
The current server game time, or zero if we are the server (a TimeSpan)
|
||||
command-description-replace =
|
||||
Replaces the input entities with the given prototype, preserving position and rotation (but nothing else)
|
||||
command-description-allcomps =
|
||||
Returns all components on the given entity.
|
||||
command-description-entitysystemupdateorder-tick =
|
||||
Lists the tick update order of entity systems.
|
||||
command-description-entitysystemupdateorder-frame =
|
||||
Lists the frame update order of entity systems.
|
||||
command-description-more =
|
||||
Prints the contents of $more, i.e. any extras that Toolshed didn't print from the last command.
|
||||
command-description-ModulusCommand =
|
||||
Computes the modulus of two values.
|
||||
This is usually remainder, check C#'s documentation for the type.
|
||||
command-description-ModVecCommand =
|
||||
Performs the modulus operation over the input with the given constant right-hand value.
|
||||
command-description-BitAndNotCommand =
|
||||
Performs bitwise AND-NOT over the input.
|
||||
command-description-BitOrNotCommand =
|
||||
Performs bitwise OR-NOT over the input.
|
||||
command-description-BitXnorCommand =
|
||||
Performs bitwise XNOR over the input.
|
||||
command-description-BitNotCommand =
|
||||
Performs bitwise NOT on the input.
|
||||
command-description-abs =
|
||||
Computes the absolute value of the input (removing the sign)
|
||||
command-description-average =
|
||||
Computes the average (arithmetic mean) of the input.
|
||||
command-description-bibytecount =
|
||||
Returns the size of the input in bytes, given that the input implements IBinaryInteger.
|
||||
This is NOT sizeof.
|
||||
command-description-shortestbitlength =
|
||||
Returns the minimum number of bits needed to represent the input value.
|
||||
command-description-countleadzeros =
|
||||
Counts the number of leading binary zeros in the input value.
|
||||
command-description-counttrailingzeros =
|
||||
Counts the number of trailing binary zeros in the input value.
|
||||
command-description-fpi =
|
||||
pi (3.14159...) as a float.
|
||||
command-description-fe =
|
||||
e (2.71828...) as a float.
|
||||
command-description-ftau =
|
||||
tau (6.28318...) as a float.
|
||||
command-description-fepsilon =
|
||||
The epsilon value for a float, exactly 1.4e-45.
|
||||
command-description-dpi =
|
||||
pi (3.14159...) as a double.
|
||||
command-description-de =
|
||||
e (2.71828...) as a double.
|
||||
command-description-dtau =
|
||||
tau (6.28318...) as a double.
|
||||
command-description-depsilon =
|
||||
The epsilon value for a double, exactly 4.9406564584124654E-324.
|
||||
command-description-hpi =
|
||||
pi (3.14...) as a half.
|
||||
command-description-he =
|
||||
e (2.71...) as a half.
|
||||
command-description-htau =
|
||||
tau (6.28...) as a half.
|
||||
command-description-hepsilon =
|
||||
The epsilon value for a half, exactly 5.9604645E-08.
|
||||
command-description-floor =
|
||||
Returns the floor of the input value (rounding toward zero).
|
||||
command-description-ceil =
|
||||
Returns the ceil of the input value (rounding away from zero).
|
||||
command-description-round =
|
||||
Rounds the input value.
|
||||
command-description-trunc =
|
||||
Truncates the input value.
|
||||
command-description-round2frac =
|
||||
Rounds the input value to the specified number of fractional digits.
|
||||
command-description-exponentbytecount =
|
||||
Returns the number of bytes required to store the exponent.
|
||||
command-description-significandbytecount =
|
||||
Returns the number of bytes required to store the significand.
|
||||
command-description-significandbitcount =
|
||||
Returns the exact bit length of the significand.
|
||||
command-description-exponentshortestbitcount =
|
||||
Returns the minimum number of bits to store the exponent.
|
||||
command-description-stepnext =
|
||||
Steps to the next float value, adding one to the significand with carry.
|
||||
command-description-stepprev =
|
||||
Steps to the previous float value, subtracting one from the significand with carry.
|
||||
command-description-checkedto =
|
||||
Converts from the input numeric type to the target, erroring if not possible.
|
||||
command-description-saturateto =
|
||||
Converts from the input numeric type to the target, saturating if the value is out of range.
|
||||
For example, converting 382 to a byte would saturate to 255 (the maximum value of a byte).
|
||||
command-description-truncto =
|
||||
Converts from the input numeric type to the target, with truncation.
|
||||
In the case of integers, this is a bit cast with sign extension.
|
||||
command-description-iscanonical =
|
||||
Returns whether the input is in canonical form.
|
||||
command-description-iscomplex =
|
||||
Returns whether the input is a complex number (by value, not by type)
|
||||
command-description-iseven =
|
||||
Returns whether the input is even.
|
||||
Not a javascript package.
|
||||
command-description-isodd =
|
||||
Returns whether the input is odd.
|
||||
command-description-isfinite =
|
||||
Returns whether the input is finite.
|
||||
command-description-isimaginary =
|
||||
Returns whether the input is purely imaginary (no real part).
|
||||
command-description-isinfinite =
|
||||
Returns whether the input is infinite.
|
||||
command-description-isinteger =
|
||||
Returns whether the input is an integer (by value, not by type)
|
||||
command-description-isnan =
|
||||
Returns whether the input is Not a Number (NaN).
|
||||
This is a special floating point value, so this is by value, not by type.
|
||||
command-description-isnegative =
|
||||
Returns whether the input is negative.
|
||||
command-description-ispositive =
|
||||
Returns whether the input is positive.
|
||||
command-description-isreal =
|
||||
Returns whether the input is purely real (no imaginary part).
|
||||
command-description-issubnormal =
|
||||
Returns whether the input is in sub-normal form.
|
||||
command-description-iszero =
|
||||
Returns whether the input is zero.
|
||||
command-description-pow =
|
||||
Computes the power of its lefthand to its righthand. x^y.
|
||||
command-description-sqrt =
|
||||
Computes the square root of its input.
|
||||
command-description-cbrt =
|
||||
Computes the cube root of its input.
|
||||
command-description-root =
|
||||
Computes the Nth root of its input.
|
||||
command-description-hypot =
|
||||
Computes the hypotenuse of a triangle with the given sides A and B.
|
||||
command-description-sin =
|
||||
Computes the sine of the input.
|
||||
command-description-sinpi =
|
||||
Computes the sine of the input multiplied by pi.
|
||||
command-description-asin =
|
||||
Computes the arcsine of the input.
|
||||
command-description-asinpi =
|
||||
Computes the arcsine of the input multiplied by pi.
|
||||
command-description-cos =
|
||||
Computes the cosine of the input.
|
||||
command-description-cospi =
|
||||
Computes the cosine of the input multiplied by pi.
|
||||
command-description-acos =
|
||||
Computes the arcosine of the input.
|
||||
command-description-acospi =
|
||||
Computes the arcosine of the input multiplied by pi.
|
||||
command-description-tan =
|
||||
Computes the tangent of the input.
|
||||
command-description-tanpi =
|
||||
Computes the tangent of the input multiplied by pi.
|
||||
command-description-atan =
|
||||
Computes the arctangent of the input.
|
||||
command-description-atanpi =
|
||||
Computes the arctangent of the input multiplied by pi.
|
||||
command-description-iterate =
|
||||
Iterates the given function over the input N times, returning a list of results.
|
||||
Think of this like successively applying the function to a value, tracking all the intermediate values.
|
||||
command-description-pick =
|
||||
Picks a random value from the input.
|
||||
command-description-tee =
|
||||
Tees the input into the given block, ignoring the block's result.
|
||||
This essentially lets you have a branch in your code to do multiple operations on one value.
|
||||
command-description-cmd-info =
|
||||
Returns a CommandSpec for the given command.
|
||||
On it's own, this means it'll print the comamnd's help message.
|
||||
command-description-comp-rm =
|
||||
Removes the given component from the entity.
|
||||
7
Resources/Locale/en-US/uploadfolder.ftl
Normal file
7
Resources/Locale/en-US/uploadfolder.ftl
Normal file
@@ -0,0 +1,7 @@
|
||||
uploadfolder-command-description = Uploads a folder from your UserData folder recursively to the server contentDB.
|
||||
uploadfolder-command-help = uploadfolder [folder you want to upload in userdata/UploadFolder]
|
||||
uploadfolder-command-wrong-args = Wrong number of arguments!
|
||||
uploadfolder-command-folder-not-found = Folder {$folder} not found!
|
||||
uploadfolder-command-resource-upload-disabled = Network Resource Uploading is currently disabled. check Server CVars.
|
||||
uploadfolder-command-file-too-big = File {$filename} above the current size limit! It must be smaller than {$sizeLimit} MB. skipping.
|
||||
uploadfolder-command-success = Uploaded {$fileCount} files
|
||||
@@ -1,5 +1,6 @@
|
||||
## ViewVariablesInstanceEntity
|
||||
|
||||
view-variables = View Variables
|
||||
view-variable-instance-entity-server-components-add-component-button-placeholder = Add Component
|
||||
view-variable-instance-entity-client-variables-tab-title = Client Variables
|
||||
view-variable-instance-entity-client-components-tab-title = Client Components
|
||||
@@ -8,4 +9,4 @@ view-variable-instance-entity-server-components-tab-title = Server Components
|
||||
view-variable-instance-entity-client-components-search-bar-placeholder = Search
|
||||
view-variable-instance-entity-server-components-search-bar-placeholder = Search
|
||||
view-variable-instance-entity-add-window-server-components = Add Component [S]
|
||||
view-variable-instance-entity-add-window-client-components = Add Component [C]
|
||||
view-variable-instance-entity-add-window-client-components = Add Component [C]
|
||||
|
||||
@@ -5,6 +5,7 @@ preset raw;
|
||||
#include "/Shaders/Internal/fov_shared.swsl"
|
||||
|
||||
const highp float g_MinVariance = 0.0;
|
||||
uniform highp vec4 occludeColor;
|
||||
|
||||
void fragment()
|
||||
{
|
||||
@@ -19,5 +20,5 @@ void fragment()
|
||||
discard;
|
||||
}
|
||||
|
||||
COLOR = vec4(0.0, 0.0, 0.0, 1.0 - occlusion);
|
||||
COLOR = vec4(occludeColor.rgb, 1.0 - occlusion);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ preset raw;
|
||||
#include "/Shaders/Internal/fov_shared.swsl"
|
||||
|
||||
const highp float g_MinVariance = 0.0;
|
||||
uniform highp vec4 occludeColor;
|
||||
|
||||
void fragment()
|
||||
{
|
||||
@@ -18,5 +19,5 @@ void fragment()
|
||||
discard;
|
||||
}
|
||||
|
||||
COLOR = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
COLOR = vec4(occludeColor.rgb, 1.0);
|
||||
}
|
||||
|
||||
BIN
Resources/Textures/Interface/Default/cross.png
Normal file
BIN
Resources/Textures/Interface/Default/cross.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 B |
|
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 637 B |
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
@@ -12,6 +13,7 @@ namespace Robust.Analyzers
|
||||
public class AccessAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string AccessAttributeType = "Robust.Shared.Analyzers.AccessAttribute";
|
||||
private const string RobustAutoGeneratedAttributeType = "Robust.Shared.Analyzers.RobustAutoGeneratedAttribute";
|
||||
private const string PureAttributeType = "System.Diagnostics.Contracts.PureAttribute";
|
||||
|
||||
[SuppressMessage("ReSharper", "RS2008")]
|
||||
@@ -73,11 +75,20 @@ namespace Robust.Analyzers
|
||||
|
||||
// Get the attributes
|
||||
var friendAttribute = context.Compilation.GetTypeByMetadataName(AccessAttributeType);
|
||||
var autoGenAttribute = context.Compilation.GetTypeByMetadataName(RobustAutoGeneratedAttributeType);
|
||||
|
||||
// Get the type that is containing this expression, or, the type where this is happening.
|
||||
if (context.ContainingSymbol?.ContainingType is not {} accessingType)
|
||||
return;
|
||||
|
||||
// Should we ignore the access attempt due to the accessing type being auto-generated?
|
||||
if (accessingType.GetAttributes().FirstOrDefault(a =>
|
||||
a.AttributeClass != null &&
|
||||
a.AttributeClass.Equals(autoGenAttribute, SymbolEqualityComparer.Default)) is { } attr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine which type of access is happening here... Read, write or execute?
|
||||
var accessAttempt = DetermineAccess(context, targetAccess, operation);
|
||||
|
||||
|
||||
206
Robust.Analyzers/ByRefEventAnalyzer.cs
Normal file
206
Robust.Analyzers/ByRefEventAnalyzer.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using static Microsoft.CodeAnalysis.SymbolEqualityComparer;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string ByRefAttribute = "Robust.Shared.GameObjects.ByRefEventAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor ByRefEventSubscribedByValueRule = new(
|
||||
Diagnostics.IdByRefEventSubscribedByValue,
|
||||
"By-ref event subscribed to by value",
|
||||
"Tried to subscribe to a by-ref event '{0}' by value.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure that methods subscribing to a ref event have the ref keyword for the event argument."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor ByValueEventSubscribedByRefRule = new(
|
||||
Diagnostics.IdValueEventRaisedByRef,
|
||||
"Value event subscribed to by-ref",
|
||||
"Tried to subscribe to a value event '{0}' by-ref.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure that methods subscribing to value events do not have the ref keyword for the event argument."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor ByRefEventRaisedByValueRule = new(
|
||||
Diagnostics.IdByRefEventRaisedByValue,
|
||||
"By-ref event raised by value",
|
||||
"Tried to raise a by-ref event '{0}' by value.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to use the ref keyword when raising ref events."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor ByValueEventRaisedByRefRule = new(
|
||||
Diagnostics.IdValueEventRaisedByRef,
|
||||
"Value event raised by-ref",
|
||||
"Tried to raise a value event '{0}' by-ref.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to not use the ref keyword when raising value events."
|
||||
);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
ByRefEventSubscribedByValueRule,
|
||||
ByValueEventSubscribedByRefRule,
|
||||
ByRefEventRaisedByValueRule,
|
||||
ByValueEventRaisedByRefRule
|
||||
);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterOperationAction(CheckEventSubscription, OperationKind.Invocation);
|
||||
context.RegisterOperationAction(CheckEventRaise, OperationKind.Invocation);
|
||||
}
|
||||
|
||||
private void CheckEventSubscription(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation operation)
|
||||
return;
|
||||
|
||||
var subscribeMethods = context.Compilation
|
||||
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
|
||||
.GetMembers()
|
||||
.Where(m => m.Name.Contains("SubscribeLocalEvent"))
|
||||
.Cast<IMethodSymbol>();
|
||||
|
||||
if (subscribeMethods == null)
|
||||
return;
|
||||
|
||||
if (!subscribeMethods.Any(m => m.Equals(operation.TargetMethod.OriginalDefinition, Default)))
|
||||
return;
|
||||
|
||||
var typeArguments = operation.TargetMethod.TypeArguments;
|
||||
if (typeArguments.Length < 1 || typeArguments.Length > 2)
|
||||
return;
|
||||
|
||||
if (operation.Arguments.First().Value is not IDelegateCreationOperation delegateCreation)
|
||||
return;
|
||||
|
||||
if (delegateCreation.Target is not IMethodReferenceOperation methodReference)
|
||||
return;
|
||||
|
||||
var eventParameter = methodReference.Method.Parameters.LastOrDefault();
|
||||
if (eventParameter == null)
|
||||
return;
|
||||
|
||||
ITypeSymbol eventArgument;
|
||||
switch (typeArguments.Length)
|
||||
{
|
||||
case 1:
|
||||
eventArgument = typeArguments[0];
|
||||
break;
|
||||
case 2:
|
||||
eventArgument = typeArguments[1];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var byRefAttribute = context.Compilation.GetTypeByMetadataName(ByRefAttribute);
|
||||
if (byRefAttribute == null)
|
||||
return;
|
||||
|
||||
var isByRefEventType = eventArgument
|
||||
.GetAttributes()
|
||||
.Any(attribute => attribute.AttributeClass?.Equals(byRefAttribute, Default) ?? false);
|
||||
var parameterIsRef = eventParameter.RefKind == RefKind.Ref;
|
||||
|
||||
if (isByRefEventType != parameterIsRef)
|
||||
{
|
||||
var descriptor = isByRefEventType ? ByRefEventSubscribedByValueRule : ByValueEventSubscribedByRefRule;
|
||||
var diagnostic = Diagnostic.Create(descriptor, operation.Syntax.GetLocation(), eventArgument);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckEventRaise(OperationAnalysisContext context)
|
||||
{
|
||||
if (context.Operation is not IInvocationOperation operation)
|
||||
return;
|
||||
|
||||
var raiseMethods = context.Compilation
|
||||
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
|
||||
.GetMembers()
|
||||
.Where(m => m.Name.Contains("RaiseLocalEvent") && m.Kind == SymbolKind.Method)
|
||||
.Cast<IMethodSymbol>();
|
||||
|
||||
var busRaiseMethods = context.Compilation
|
||||
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntityEventBus")?
|
||||
.GetMembers()
|
||||
.Where(m => m.Name.Contains("RaiseLocalEvent") && m.Kind == SymbolKind.Method)
|
||||
.Cast<IMethodSymbol>();
|
||||
|
||||
if (raiseMethods == null)
|
||||
return;
|
||||
|
||||
if (busRaiseMethods != null)
|
||||
raiseMethods = raiseMethods.Concat(busRaiseMethods);
|
||||
|
||||
if (!raiseMethods.Any(m => m.Equals(operation.TargetMethod.OriginalDefinition, Default)))
|
||||
{
|
||||
// If you try to do this normally by concatenating like busRaiseMethods above
|
||||
// the analyzer does not run without any errors
|
||||
// I don't know man
|
||||
const string directedBusMethod = "Robust.Shared.GameObjects.IDirectedEventBus.RaiseLocalEvent";
|
||||
if (!operation.TargetMethod.ToString().StartsWith(directedBusMethod))
|
||||
return;
|
||||
}
|
||||
|
||||
var arguments = operation.Arguments;
|
||||
IArgumentOperation eventArgument;
|
||||
switch (arguments.Length)
|
||||
{
|
||||
case 1:
|
||||
eventArgument = arguments[0];
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
eventArgument = arguments[1];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var eventParameter = eventArgument.Parameter;
|
||||
// TODO have a way to check generic type parameters
|
||||
if (eventParameter == null ||
|
||||
eventParameter.Type.SpecialType == SpecialType.System_Object ||
|
||||
eventParameter.Type.TypeKind == TypeKind.TypeParameter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var byRefAttribute = context.Compilation.GetTypeByMetadataName(ByRefAttribute);
|
||||
if (byRefAttribute == null)
|
||||
return;
|
||||
|
||||
var isByRefEventType = eventParameter.Type
|
||||
.GetAttributes()
|
||||
.Any(attribute => attribute.AttributeClass?.Equals(byRefAttribute, Default) ?? false);
|
||||
|
||||
var parameterIsRef = eventParameter.RefKind == RefKind.Ref;
|
||||
|
||||
if (isByRefEventType != parameterIsRef)
|
||||
{
|
||||
var descriptor = isByRefEventType ? ByRefEventRaisedByValueRule : ByValueEventRaisedByRefRule;
|
||||
var diagnostic = Diagnostic.Create(descriptor, eventArgument.Syntax.GetLocation(), eventParameter.Type);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
293
Robust.Analyzers/DataDefinitionAnalyzer.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
||||
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
||||
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
|
||||
Diagnostics.IdDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} is a DataDefinition but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type that is a data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
|
||||
Diagnostics.IdNestedDataDefinitionPartial,
|
||||
"Type must be partial",
|
||||
"Type {0} contains nested data definition {1} but is not partial.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to mark any type containing a nested data definition as partial."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
|
||||
Diagnostics.IdDataFieldWritable,
|
||||
"Data field must not be readonly",
|
||||
"Data field {0} in data definition {1} is readonly.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to remove the readonly modifier."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
|
||||
Diagnostics.IdDataFieldPropertyWritable,
|
||||
"Data field property must have a setter",
|
||||
"Data field property {0} in data definition {1} does not have a setter.",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Make sure to add a setter."
|
||||
);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
|
||||
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
|
||||
);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.ClassDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.StructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordStructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.InterfaceDeclaration);
|
||||
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataField, SyntaxKind.FieldDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeDataFieldProperty, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
private void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not TypeDeclarationSyntax declaration)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(declaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
if (!IsPartial(declaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataDefinitionPartialRule, declaration.Keyword.GetLocation(), type.Name));
|
||||
}
|
||||
|
||||
var containingType = type.ContainingType;
|
||||
while (containingType != null)
|
||||
{
|
||||
var containingTypeDeclaration = (TypeDeclarationSyntax) containingType.DeclaringSyntaxReferences[0].GetSyntax();
|
||||
if (!IsPartial(containingTypeDeclaration))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(NestedDataDefinitionPartialRule, containingTypeDeclaration.Keyword.GetLocation(), containingType.Name, type.Name));
|
||||
}
|
||||
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataField(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not FieldDeclarationSyntax field)
|
||||
return;
|
||||
|
||||
var typeDeclaration = field.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type))
|
||||
return;
|
||||
|
||||
foreach (var variable in field.Declaration.Variables)
|
||||
{
|
||||
var fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
|
||||
if (fieldSymbol == null)
|
||||
continue;
|
||||
|
||||
if (IsReadOnlyDataField(type, fieldSymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not PropertyDeclarationSyntax property)
|
||||
return;
|
||||
|
||||
var typeDeclaration = property.FirstAncestorOrSelf<TypeDeclarationSyntax>();
|
||||
if (typeDeclaration == null)
|
||||
return;
|
||||
|
||||
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
|
||||
if (!IsDataDefinition(type) || type.IsRecord || type.IsValueType)
|
||||
return;
|
||||
|
||||
var propertySymbol = context.SemanticModel.GetDeclaredSymbol(property);
|
||||
if (propertySymbol == null)
|
||||
return;
|
||||
|
||||
if (IsReadOnlyDataField(type, propertySymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
|
||||
{
|
||||
if (!IsDataField(field, out _, out _))
|
||||
return false;
|
||||
|
||||
return IsReadOnlyMember(type, field);
|
||||
}
|
||||
|
||||
private static bool IsPartial(TypeDeclarationSyntax type)
|
||||
{
|
||||
return type.Modifiers.IndexOf(SyntaxKind.PartialKeyword) != -1;
|
||||
}
|
||||
|
||||
private static bool IsDataDefinition(ITypeSymbol? type)
|
||||
{
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
return HasAttribute(type, DataDefinitionNamespace) ||
|
||||
IsImplicitDataDefinition(type);
|
||||
}
|
||||
|
||||
private static bool IsDataField(ISymbol member, out ITypeSymbol type, out AttributeData attribute)
|
||||
{
|
||||
// TODO data records and other attributes
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
foreach (var attr in field.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = field.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
foreach (var attr in property.GetAttributes())
|
||||
{
|
||||
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
||||
{
|
||||
type = property.Type;
|
||||
attribute = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = null!;
|
||||
attribute = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool Inherits(ITypeSymbol type, string parent)
|
||||
{
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (baseType.ToDisplayString() == parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
|
||||
{
|
||||
if (member is IFieldSymbol field)
|
||||
{
|
||||
return field.IsReadOnly;
|
||||
}
|
||||
else if (member is IPropertySymbol property)
|
||||
{
|
||||
if (property.SetMethod == null)
|
||||
return true;
|
||||
|
||||
if (property.SetMethod.IsInitOnly)
|
||||
return type.IsReferenceType;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasAttribute(ITypeSymbol type, string attributeName)
|
||||
{
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var baseType in GetBaseTypes(type))
|
||||
{
|
||||
if (HasAttribute(baseType, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var @interface in type.AllInterfaces)
|
||||
{
|
||||
if (IsImplicitDataDefinitionInterface(@interface))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsImplicitDataDefinitionInterface(ITypeSymbol @interface)
|
||||
{
|
||||
if (HasAttribute(@interface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
|
||||
foreach (var subInterface in @interface.AllInterfaces)
|
||||
{
|
||||
if (HasAttribute(subInterface, ImplicitDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
yield return baseType;
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
168
Robust.Analyzers/DataDefinitionFixer.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
|
||||
using static Robust.Analyzers.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public sealed class DefinitionFixer : CodeFixProvider
|
||||
{
|
||||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
|
||||
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
|
||||
);
|
||||
|
||||
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
foreach (var diagnostic in context.Diagnostics)
|
||||
{
|
||||
switch (diagnostic.Id)
|
||||
{
|
||||
case IdDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdNestedDataDefinitionPartial:
|
||||
return RegisterPartialTypeFix(context, diagnostic);
|
||||
case IdDataFieldWritable:
|
||||
return RegisterDataFieldFix(context, diagnostic);
|
||||
case IdDataFieldPropertyWritable:
|
||||
return RegisterDataFieldPropertyFix(context, diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
private static async Task RegisterPartialTypeFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<TypeDeclarationSyntax>().First();
|
||||
|
||||
if (token == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make type partial",
|
||||
c => MakeDataDefinitionPartial(context.Document, token, c),
|
||||
"Make type partial"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeDataDefinitionPartial(Document document, TypeDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = SyntaxFactory.Token(PartialKeyword);
|
||||
var newDeclaration = declaration.AddModifiers(token);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var field = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (field == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakeFieldWritable(context.Document, field, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task RegisterDataFieldPropertyFix(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var property = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<PropertyDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Make data field writable",
|
||||
c => MakePropertyWritable(context.Document, property, c),
|
||||
"Make data field writable"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakeFieldWritable(Document document, FieldDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var token = declaration.Modifiers.First(t => t.IsKind(ReadOnlyKeyword));
|
||||
var newDeclaration = declaration.WithModifiers(declaration.Modifiers.Remove(token));
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
|
||||
private static async Task<Document> MakePropertyWritable(Document document, PropertyDeclarationSyntax declaration, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
var newDeclaration = declaration;
|
||||
var privateSet = newDeclaration
|
||||
.AccessorList?
|
||||
.Accessors
|
||||
.FirstOrDefault(s => s.IsKind(SetAccessorDeclaration) || s.IsKind(InitAccessorDeclaration));
|
||||
|
||||
if (newDeclaration.AccessorList != null && privateSet != null)
|
||||
{
|
||||
newDeclaration = newDeclaration.WithAccessorList(
|
||||
newDeclaration.AccessorList.WithAccessors(
|
||||
newDeclaration.AccessorList.Accessors.Remove(privateSet)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
AccessorDeclarationSyntax setter;
|
||||
if (declaration.Modifiers.Any(m => m.IsKind(PrivateKeyword)))
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
setter = SyntaxFactory.AccessorDeclaration(
|
||||
SetAccessorDeclaration,
|
||||
default,
|
||||
SyntaxFactory.TokenList(SyntaxFactory.Token(PrivateKeyword)),
|
||||
SyntaxFactory.Token(SetKeyword),
|
||||
default,
|
||||
default,
|
||||
SyntaxFactory.Token(SemicolonToken)
|
||||
);
|
||||
}
|
||||
|
||||
newDeclaration = newDeclaration.AddAccessorListAccessors(setter);
|
||||
|
||||
root = root!.ReplaceNode(declaration, newDeclaration);
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,14 @@ public static class Diagnostics
|
||||
public const string IdInvalidNotNullableFlagImplementation = "RA0010";
|
||||
public const string IdInvalidNotNullableFlagType = "RA0011";
|
||||
public const string IdNotNullableFlagValueType = "RA0012";
|
||||
public const string IdByRefEventSubscribedByValue = "RA0013";
|
||||
public const string IdValueEventSubscribedByRef = "RA0014";
|
||||
public const string IdByRefEventRaisedByValue = "RA0015";
|
||||
public const string IdValueEventRaisedByRef = "RA0016";
|
||||
public const string IdDataDefinitionPartial = "RA0017";
|
||||
public const string IdNestedDataDefinitionPartial = "RA0018";
|
||||
public const string IdDataFieldWritable = "RA0019";
|
||||
public const string IdDataFieldPropertyWritable = "RA0020";
|
||||
|
||||
public static SuppressionDescriptor MeansImplicitAssignment =>
|
||||
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class AddRemoveComponentBenchmark
|
||||
public partial class AddRemoveComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -48,7 +48,7 @@ public class AddRemoveComponentBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ public class ArchetypeComponentAccessBenchmark
|
||||
public struct Struct9{}
|
||||
public struct Struct10{}
|
||||
|
||||
private sealed class Archetype<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
|
||||
public sealed class Archetype<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
|
||||
{
|
||||
private int _nextId;
|
||||
private readonly Dictionary<int, int> _ids;
|
||||
@@ -905,7 +905,7 @@ public class ArchetypeComponentAccessBenchmark
|
||||
var curLength = array.Length;
|
||||
if (curLength <= idx.Value)
|
||||
{
|
||||
var newLength = MathHelper.NextPowerOfTwo(Math.Max(8, idx.Value));
|
||||
var newLength = MathHelper.NextPowerOfTwo(System.Math.Max(8, idx.Value));
|
||||
Array.Resize(ref array, newLength);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
public partial class ComponentIteratorBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
|
||||
[UsedImplicitly]
|
||||
[Params(1, 10, 100, 1000)]
|
||||
public int N;
|
||||
|
||||
public A[] Comps = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.RegisterComponents(f => f.RegisterClass<A>())
|
||||
.InitializeInstance();
|
||||
|
||||
_entityManager = _simulation.Resolve<IEntityManager>();
|
||||
|
||||
Comps = new A[N+2];
|
||||
|
||||
var coords = new MapCoordinates(0, 0, new MapId(1));
|
||||
_simulation.AddMap(coords.MapId);
|
||||
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
var uid = _entityManager.SpawnEntity(null, coords);
|
||||
_entityManager.AddComponent<A>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public A[] ComponentStructEnumerator()
|
||||
{
|
||||
var query = _entityManager.EntityQueryEnumerator<A>();
|
||||
var i = 0;
|
||||
|
||||
while (query.MoveNext(out var comp))
|
||||
{
|
||||
Comps[i] = comp;
|
||||
i++;
|
||||
}
|
||||
|
||||
return Comps;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public A[] ComponentIEnumerable()
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
foreach (var comp in _entityManager.EntityQuery<A>())
|
||||
{
|
||||
Comps[i] = comp;
|
||||
i++;
|
||||
}
|
||||
|
||||
return Comps;
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
53
Robust.Benchmarks/EntityManager/ComponentPassingBenchmark.cs
Normal file
53
Robust.Benchmarks/EntityManager/ComponentPassingBenchmark.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using static Robust.Benchmarks.EntityManager.ArchetypeComponentAccessBenchmark;
|
||||
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[MemoryDiagnoser]
|
||||
[Virtual]
|
||||
public class ArrayAccessBenchmark
|
||||
{
|
||||
[Params(new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})]
|
||||
public int[] Array = default!;
|
||||
|
||||
[Params(5)]
|
||||
public int Element;
|
||||
|
||||
[Params(500)]
|
||||
public int Handle;
|
||||
|
||||
public Archetype<int, int, int, int, int, int, int, int, int, int> Archetype = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
Archetype = new Archetype<int, int, int, int, int, int, int, int, int, int>(1000);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int? GetArrayElement()
|
||||
{
|
||||
return Consume();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int? GetExisting()
|
||||
{
|
||||
return Consume(Element);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int? GetArchetype()
|
||||
{
|
||||
return Consume(Archetype.GetComponentUnsafeHandle<int>(Handle));
|
||||
}
|
||||
|
||||
private int? Consume(int? value = null)
|
||||
{
|
||||
if (value == null)
|
||||
value = Array[5];
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Analyzers;
|
||||
@@ -9,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class GetComponentBenchmark
|
||||
public partial class GetComponentBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -55,7 +54,7 @@ public class GetComponentBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.UnitTesting.Server;
|
||||
namespace Robust.Benchmarks.EntityManager;
|
||||
|
||||
[Virtual]
|
||||
public class SpawnDeleteEntityBenchmark
|
||||
public partial class SpawnDeleteEntityBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entityManager = default!;
|
||||
@@ -56,7 +56,7 @@ public class SpawnDeleteEntityBenchmark
|
||||
}
|
||||
|
||||
[ComponentProtoName("A")]
|
||||
public sealed class A : Component
|
||||
public sealed partial class A : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
[DbContext(typeof(BenchmarkContext))]
|
||||
[Migration("20220510131430_fix-pk")]
|
||||
partial class fixpk
|
||||
partial class FixPK
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
public partial class fixpk : Migration
|
||||
public partial class FixPK : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
[DbContext(typeof(BenchmarkContext))]
|
||||
[Migration("20221009235705_db")]
|
||||
partial class db
|
||||
partial class DB
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
public partial class db : Migration
|
||||
public partial class DB : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
[DbContext(typeof(BenchmarkContext))]
|
||||
[Migration("20221010144620_param_work")]
|
||||
partial class param_work
|
||||
partial class ParamWork
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Benchmarks.Exporters;
|
||||
|
||||
namespace Robust.Benchmarks.Migrations
|
||||
{
|
||||
public partial class param_work : Migration
|
||||
public partial class ParamWork : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Benchmarks.NumericsHelpers
|
||||
namespace Robust.Benchmarks.NumericsHelpers;
|
||||
|
||||
[Virtual]
|
||||
public class AddBenchmark
|
||||
{
|
||||
[Virtual]
|
||||
public class AddBenchmark
|
||||
[Params(32, 128)]
|
||||
public int N { get; set; }
|
||||
|
||||
[Params(1,2)]
|
||||
public int T { get; set; }
|
||||
|
||||
private float[] _inputA = default!;
|
||||
private float[] _inputB = default!;
|
||||
private float[] _output = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
[Params(32, 128)]
|
||||
public int N { get; set; }
|
||||
_inputA = new float[N];
|
||||
_inputB = new float[N];
|
||||
_output = new float[N];
|
||||
}
|
||||
|
||||
[Params(1,2)]
|
||||
public int T { get; set; }
|
||||
|
||||
private float[] _inputA = default!;
|
||||
private float[] _inputB = default!;
|
||||
private float[] _output = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_inputA = new float[N];
|
||||
_inputB = new float[N];
|
||||
_output = new float[N];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Bench()
|
||||
{
|
||||
Shared.Maths.NumericsHelpers.Add(_inputA, _inputB, _output);
|
||||
}
|
||||
[Benchmark]
|
||||
public void Bench()
|
||||
{
|
||||
Shared.Maths.NumericsHelpers.Add(_inputA, _inputB, _output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>../bin/Benchmarks</OutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
<NoWarn>RA0003</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>../bin/Benchmarks</OutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
<NoWarn>RA0003</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -33,8 +33,8 @@ namespace Robust.Benchmarks.Serialization
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public int CreateCopy(ISerializationManager serializationManager, int source, SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null)
|
||||
public int CreateCopy(ISerializationManager serializationManager, int source,
|
||||
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -108,9 +108,7 @@ namespace Robust.Benchmarks.Serialization.Copy
|
||||
copy.Potency = Seed.Potency;
|
||||
copy.Ligneous = Seed.Ligneous;
|
||||
|
||||
copy.PlantRsi = Seed.PlantRsi == null
|
||||
? null!
|
||||
: new ResourcePath(Seed.PlantRsi.ToString(), Seed.PlantRsi.Separator);
|
||||
copy.PlantRsi = new ResPath(Seed.PlantRsi.ToString());
|
||||
copy.PlantIconState = Seed.PlantIconState;
|
||||
copy.Bioluminescent = Seed.Bioluminescent;
|
||||
copy.BioluminescentColor = Seed.BioluminescentColor;
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
[Virtual]
|
||||
public class DataDefinitionWithString
|
||||
public partial class DataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace Robust.Benchmarks.Serialization.Definitions
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed class SealedDataDefinitionWithString
|
||||
public sealed partial class SealedDataDefinitionWithString
|
||||
{
|
||||
[DataField("string")]
|
||||
public string StringField { get; init; } = default!;
|
||||
public string StringField { get; private set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -10,8 +11,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
/// Arbitrarily large data definition for benchmarks.
|
||||
/// Taken from content.
|
||||
/// </summary>
|
||||
[Prototype("seed")]
|
||||
public sealed class SeedDataDefinition : IPrototype
|
||||
public sealed partial class SeedDataDefinition : Component
|
||||
{
|
||||
public const string Prototype = @"
|
||||
- type: seed
|
||||
@@ -87,7 +87,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
#endregion
|
||||
|
||||
#region Cosmetics
|
||||
[DataField("plantRsi", required: true)] public ResourcePath PlantRsi { get; set; } = default!;
|
||||
[DataField("plantRsi", required: true)] public ResPath PlantRsi { get; set; } = default!;
|
||||
[DataField("plantIconState")] public string PlantIconState { get; set; } = "produce";
|
||||
[DataField("bioluminescent")] public bool Bioluminescent { get; set; }
|
||||
[DataField("bioluminescentColor")] public Color BioluminescentColor { get; set; } = Color.White;
|
||||
@@ -106,7 +106,7 @@ namespace Robust.Benchmarks.Serialization.Definitions
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public struct SeedChemQuantity
|
||||
public partial struct SeedChemQuantity
|
||||
{
|
||||
[DataField("Min")]
|
||||
public int Min;
|
||||
|
||||
171
Robust.Benchmarks/Transform/RecursiveMoveBenchmark.cs
Normal file
171
Robust.Benchmarks/Transform/RecursiveMoveBenchmark.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Transform;
|
||||
|
||||
/// <summary>
|
||||
/// This benchmark tests various transform/move related functions with an entity that has many children.
|
||||
/// </summary>
|
||||
[Virtual, MemoryDiagnoser]
|
||||
public class RecursiveMoveBenchmark
|
||||
{
|
||||
private ISimulation _simulation = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private SharedTransformSystem _transform = default!;
|
||||
private ContainerSystem _container = default!;
|
||||
private PvsSystem _pvs = default!;
|
||||
private EntityCoordinates _mapCoords;
|
||||
private EntityCoordinates _gridCoords;
|
||||
private EntityUid _ent;
|
||||
private EntityUid _child;
|
||||
private TransformComponent _childXform = default!;
|
||||
private EntityQuery<TransformComponent> _query;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_simulation = RobustServerSimulation
|
||||
.NewSimulation()
|
||||
.InitializeInstance();
|
||||
|
||||
if (!_simulation.Resolve<IConfigurationManager>().GetCVar(CVars.NetPVS))
|
||||
throw new InvalidOperationException("PVS must be enabled");
|
||||
|
||||
_entMan = _simulation.Resolve<IEntityManager>();
|
||||
_transform = _entMan.System<SharedTransformSystem>();
|
||||
_container = _entMan.System<ContainerSystem>();
|
||||
_pvs = _entMan.System<PvsSystem>();
|
||||
_query = _entMan.GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Create map & grid
|
||||
var mapMan = _simulation.Resolve<IMapManager>();
|
||||
var mapSys = _entMan.System<SharedMapSystem>();
|
||||
var mapId = mapMan.CreateMap();
|
||||
var map = mapMan.GetMapEntityId(mapId);
|
||||
var gridComp = mapMan.CreateGrid(mapId);
|
||||
var grid = gridComp.Owner;
|
||||
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
||||
_mapCoords = new EntityCoordinates(map, 100, 100);
|
||||
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
||||
|
||||
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
|
||||
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
|
||||
_ent = _entMan.Spawn();
|
||||
|
||||
// Quick check that SetCoordinates actually changes the parent as expected
|
||||
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
|
||||
throw new Exception("Grid traversal error.");
|
||||
|
||||
// Add 5 direct children in slots to represent clothing.
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var id = $"inventory{i}";
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, id);
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// body parts
|
||||
_container.EnsureContainer<Container>(_ent, "body");
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
// Simple organ
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// body part that has another body part / limb
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
|
||||
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Backpack
|
||||
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
|
||||
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
|
||||
// Misc backpack contents.
|
||||
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Emergency box inside of the backpack
|
||||
var box = backpackStorage.ContainedEntities.First();
|
||||
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
|
||||
throw new Exception($"Failed to setup entity");
|
||||
}
|
||||
|
||||
// Deepest child.
|
||||
_child = boxContainer.ContainedEntities.First();
|
||||
_childXform = _query.GetComponent(_child);
|
||||
|
||||
_pvs.ProcessCollections();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This implicitly measures move events, including PVS and entity lookups. Though given that most of the entities
|
||||
/// are in containers, this will bias the entity lookup aspect.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void MoveEntity()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Like <see cref="MoveEntity"/>, but also processes queued PVS chunk updates.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void MoveAndUpdateChunks()
|
||||
{
|
||||
_transform.SetCoordinates(_ent, _gridCoords);
|
||||
_pvs.ProcessCollections();
|
||||
_transform.SetCoordinates(_ent, _mapCoords);
|
||||
_pvs.ProcessCollections();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public Vector2 GetWorldPos()
|
||||
{
|
||||
return _transform.GetWorldPosition(_childXform);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public EntityUid GetRootUid()
|
||||
{
|
||||
var xform = _childXform;
|
||||
while (xform.ParentUid.IsValid())
|
||||
{
|
||||
xform = _query.GetComponent(xform.ParentUid);
|
||||
}
|
||||
return xform.ParentUid;
|
||||
}
|
||||
}
|
||||
36
Robust.Benchmarks/Utility/ResourcePathBench.cs
Normal file
36
Robust.Benchmarks/Utility/ResourcePathBench.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
#pragma warning disable CS0612
|
||||
namespace Robust.Benchmarks.Utility;
|
||||
|
||||
[Virtual]
|
||||
public class ResourcePathBench
|
||||
{
|
||||
private string _path = default!;
|
||||
|
||||
[UsedImplicitly]
|
||||
[Params(10, 100, 1000)]
|
||||
public int N;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_path = "/a/b/c/../test";
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public ResPath CreateWithSeparatorResPath()
|
||||
{
|
||||
ResPath res = default;
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
res = new ResPath(_path);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
#pragma warning restore CS0612
|
||||
@@ -25,7 +25,7 @@ namespace Robust.Build.Tasks
|
||||
Single = ts.GetType("System.Single");
|
||||
Int32 = ts.GetType("System.Int32");
|
||||
|
||||
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2", Single, 2);
|
||||
(Vector2, Vector2ConstructorFull) = GetNumericTypeInfo("System.Numerics.Vector2", Single, 2);
|
||||
(Vector2i, Vector2iConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Vector2i", Int32, 2);
|
||||
(Thickness, ThicknessConstructorFull) = GetNumericTypeInfo("Robust.Shared.Maths.Thickness", Single, 4);
|
||||
|
||||
|
||||
@@ -110,25 +110,6 @@ namespace Robust.Build.Tasks
|
||||
var compiler =
|
||||
new RobustXamlILCompiler(transformerconfig, emitConfig, true);
|
||||
|
||||
var loaderDispatcherDef = new TypeDefinition("CompiledRobustXaml", "!XamlLoader",
|
||||
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
|
||||
|
||||
var loaderDispatcherMethod = new MethodDefinition("TryLoad",
|
||||
MethodAttributes.Static | MethodAttributes.Public,
|
||||
asm.MainModule.TypeSystem.Object)
|
||||
{
|
||||
Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)}
|
||||
};
|
||||
loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
|
||||
asm.MainModule.Types.Add(loaderDispatcherDef);
|
||||
|
||||
var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
|
||||
m =>
|
||||
m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 2 &&
|
||||
m.ReturnType.FullName == "System.Boolean"
|
||||
&& m.Parameters[0].ParameterType.FullName == "System.String"
|
||||
&& m.Parameters[1].ParameterType.FullName == "System.String"));
|
||||
|
||||
bool CompileGroup(IResourceGroup group)
|
||||
{
|
||||
var typeDef = new TypeDefinition("CompiledRobustXaml", "!" + group.Name, TypeAttributes.Class,
|
||||
@@ -252,31 +233,6 @@ namespace Robust.Build.Tasks
|
||||
$"No call to RobustXamlLoader.Load(this) call found anywhere in the type {classType.FullName} and type seems to have custom constructors.");
|
||||
}
|
||||
}
|
||||
|
||||
//add compiled build method
|
||||
var compiledBuildMethod = typeSystem.GetTypeReference(builder).Resolve().Methods
|
||||
.First(m => m.Name == buildName);
|
||||
var parameterlessCtor = classTypeDefinition.GetConstructors()
|
||||
.FirstOrDefault(c => c.IsPublic && !c.IsStatic && !c.HasParameters);
|
||||
|
||||
if (compiledBuildMethod != null && parameterlessCtor != null)
|
||||
{
|
||||
var i = loaderDispatcherMethod.Body.Instructions;
|
||||
var nop = Instruction.Create(OpCodes.Nop);
|
||||
i.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
|
||||
i.Add(Instruction.Create(OpCodes.Call, stringEquals));
|
||||
i.Add(Instruction.Create(OpCodes.Brfalse, nop));
|
||||
if (parameterlessCtor != null)
|
||||
i.Add(Instruction.Create(OpCodes.Newobj, parameterlessCtor));
|
||||
else
|
||||
{
|
||||
i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
|
||||
}
|
||||
|
||||
i.Add(Instruction.Create(OpCodes.Ret));
|
||||
i.Add(nop);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -293,8 +249,6 @@ namespace Robust.Build.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
||||
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Client.WebView.Cef
|
||||
var mainArgs = new CefMainArgs(argv);
|
||||
|
||||
// This will block executing until the subprocess is shut down.
|
||||
var code = CefRuntime.ExecuteProcess(mainArgs, new RobustCefApp(), IntPtr.Zero);
|
||||
var code = CefRuntime.ExecuteProcess(mainArgs, new RobustCefApp(null), IntPtr.Zero);
|
||||
|
||||
if (code != 0)
|
||||
{
|
||||
|
||||
@@ -7,9 +7,15 @@ namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
internal sealed class RobustCefApp : CefApp
|
||||
{
|
||||
private readonly ISawmill? _sawmill;
|
||||
private readonly BrowserProcessHandler _browserProcessHandler = new();
|
||||
private readonly RenderProcessHandler _renderProcessHandler = new();
|
||||
|
||||
public RobustCefApp(ISawmill? sawmill)
|
||||
{
|
||||
_sawmill = sawmill;
|
||||
}
|
||||
|
||||
protected override CefBrowserProcessHandler GetBrowserProcessHandler()
|
||||
{
|
||||
return _browserProcessHandler;
|
||||
@@ -39,8 +45,7 @@ namespace Robust.Client.WebView.Cef
|
||||
commandLine.AppendSwitch("disable-threaded-scrolling", "1");
|
||||
commandLine.AppendSwitch("disable-features", "TouchpadAndWheelScrollLatching,AsyncWheelEvents");
|
||||
|
||||
if(IoCManager.Instance != null)
|
||||
Logger.Debug($"{commandLine}");
|
||||
_sawmill?.Debug($"CEF command line: {commandLine}");
|
||||
}
|
||||
|
||||
protected override void OnRegisterCustomSchemes(CefSchemeRegistrar registrar)
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Client.WebView.Cef
|
||||
var impl = new WebViewWindowImpl(this);
|
||||
|
||||
var lifeSpanHandler = new WindowLifeSpanHandler(impl);
|
||||
var reqHandler = new RobustRequestHandler(Logger.GetSawmill("root"));
|
||||
var reqHandler = new RobustRequestHandler(_sawmill);
|
||||
var client = new WindowCefClient(lifeSpanHandler, reqHandler);
|
||||
var settings = new CefBrowserSettings();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
@@ -26,15 +26,20 @@ namespace Robust.Client.WebView.Cef
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly ILocalizationManager _localization = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
IoCManager.Instance!.InjectDependencies(this, oneOff: true);
|
||||
_sawmill = _logManager.GetSawmill("web.cef");
|
||||
|
||||
_consoleHost.RegisterCommand("flushcookies", Loc.GetString("cmd-flushcookies-desc"), Loc.GetString("cmd-flushcookies-help"), (_, _, _) =>
|
||||
{
|
||||
CefCookieManager.GetGlobal(null).FlushStore(null);
|
||||
});
|
||||
_consoleHost.RegisterCommand(
|
||||
"flushcookies",
|
||||
_localization.GetString("cmd-flushcookies-desc"),
|
||||
_localization.GetString("cmd-flushcookies-help"),
|
||||
(_, _, _) => CefCookieManager.GetGlobal(null).FlushStore(null));
|
||||
|
||||
string subProcessName;
|
||||
if (OperatingSystem.IsWindows())
|
||||
@@ -46,6 +51,7 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
var subProcessPath = Path.Combine(BasePath, subProcessName);
|
||||
var cefResourcesPath = LocateCefResources();
|
||||
_sawmill.Debug($"Subprocess path: {subProcessPath}, resources: {cefResourcesPath}");
|
||||
|
||||
// System.Console.WriteLine(AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES"));
|
||||
|
||||
@@ -54,7 +60,7 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
var cachePath = "";
|
||||
if (_resourceManager.UserData is WritableDirProvider userData)
|
||||
cachePath = userData.GetFullPath(new ResourcePath("/cef_cache"));
|
||||
cachePath = userData.GetFullPath(new ResPath("/cef_cache"));
|
||||
|
||||
var settings = new CefSettings()
|
||||
{
|
||||
@@ -69,26 +75,28 @@ namespace Robust.Client.WebView.Cef
|
||||
CachePath = cachePath,
|
||||
};
|
||||
|
||||
var userAgentOverride = _cfg.GetCVar(WCVars.UserAgentOverride);
|
||||
var userAgentOverride = _cfg.GetCVar(WCVars.WebUserAgentOverride);
|
||||
if (!string.IsNullOrEmpty(userAgentOverride))
|
||||
{
|
||||
settings.UserAgent = userAgentOverride;
|
||||
}
|
||||
|
||||
Logger.Info($"CEF Version: {CefRuntime.ChromeVersion}");
|
||||
_sawmill.Info($"CEF Version: {CefRuntime.ChromeVersion}");
|
||||
|
||||
_app = new RobustCefApp();
|
||||
_app = new RobustCefApp(_sawmill);
|
||||
|
||||
// We pass no main arguments...
|
||||
CefRuntime.Initialize(new CefMainArgs(null), settings, _app, IntPtr.Zero);
|
||||
|
||||
// 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.
|
||||
// So these arguments look like nonsense, but it turns out CEF is just *like that*.
|
||||
// The first argument is literally nonsense, but it needs to be there as otherwise the second argument doesn't apply
|
||||
// The second argument turns off CEF's bullshit error handling, which breaks dotnet's error handling.
|
||||
CefRuntime.Initialize(new CefMainArgs(new string[]{"binary","--disable-in-process-stack-traces"}), settings, _app, IntPtr.Zero);
|
||||
|
||||
if (_cfg.GetCVar(WCVars.WebResProtocol))
|
||||
{
|
||||
var handler = new ResourceSchemeFactoryHandler(this, _resourceManager, Logger.GetSawmill("web.res"));
|
||||
var handler = new ResourceSchemeFactoryHandler(
|
||||
this,
|
||||
_resourceManager,
|
||||
_logManager.GetSawmill("web.res"));
|
||||
|
||||
CefRuntime.RegisterSchemeHandlerFactory("res", "", handler);
|
||||
}
|
||||
}
|
||||
@@ -161,10 +169,10 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
_sawmill.Debug($"HANDLING: {request.Url}");
|
||||
|
||||
var resourcePath = new ResourcePath(uri.AbsolutePath);
|
||||
if (_resourceManager.TryContentFileRead(resourcePath, out var stream))
|
||||
var resPath = new ResPath(uri.AbsolutePath);
|
||||
if (_resourceManager.TryContentFileRead(resPath, out var stream))
|
||||
{
|
||||
if (!_parent.TryGetResourceMimeType(resourcePath.Extension, out var mime))
|
||||
if (!_parent.TryGetResourceMimeType(resPath.Extension, out var mime))
|
||||
mime = "application/octet-stream";
|
||||
|
||||
return new RequestResultStream(stream, mime, HttpStatusCode.OK).MakeHandler();
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<ValidateExecutableReferencesMatchSelfContained>false</ValidateExecutableReferencesMatchSelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.DefineConstants.targets" />
|
||||
<Target Name="RobustAfterBuild" AfterTargets="Build" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
||||
<PackageReference Include="Robust.Natives.Cef" Version="102.0.9" />
|
||||
@@ -20,4 +16,6 @@
|
||||
<ProjectReference Include="..\cefglue\CefGlue\CefGlue.csproj" />
|
||||
<ProjectReference Include="..\Robust.Client\Robust.Client.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -18,6 +18,12 @@ public static class WCVars
|
||||
/// <summary>
|
||||
/// Overrides the default CEF user-agent when set to a non-empty string.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> UserAgentOverride =
|
||||
public static readonly CVarDef<string> WebUserAgentOverride =
|
||||
CVarDef.Create("web.user_agent", "", CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// If true, use headless WebView implementation even with graphical client (turn off CEF).
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> WebHeadless =
|
||||
CVarDef.Create("web.headless", false, CVar.CLIENTONLY);
|
||||
}
|
||||
|
||||
@@ -15,22 +15,29 @@ namespace Robust.Client.WebView
|
||||
{
|
||||
private IWebViewManagerImpl? _impl;
|
||||
|
||||
public void Initialize(GameController.DisplayMode mode)
|
||||
public void PreInitialize(IDependencyCollection dependencies, GameController.DisplayMode mode)
|
||||
{
|
||||
DebugTools.Assert(_impl == null, "WebViewManager has already been initialized!");
|
||||
|
||||
var cfg = IoCManager.Resolve<IConfigurationManagerInternal>();
|
||||
var cfg = dependencies.Resolve<IConfigurationManagerInternal>();
|
||||
cfg.LoadCVarsFromAssembly(typeof(WebViewManager).Assembly);
|
||||
|
||||
IoCManager.RegisterInstance<IWebViewManager>(this);
|
||||
IoCManager.RegisterInstance<IWebViewManagerInternal>(this);
|
||||
dependencies.RegisterInstance<IWebViewManager>(this);
|
||||
dependencies.RegisterInstance<IWebViewManagerInternal>(this);
|
||||
|
||||
if (mode == GameController.DisplayMode.Headless)
|
||||
if (mode == GameController.DisplayMode.Headless || cfg.GetCVar(WCVars.WebHeadless))
|
||||
_impl = new WebViewManagerHeadless();
|
||||
else
|
||||
_impl = new WebViewManagerCef();
|
||||
|
||||
_impl.Initialize();
|
||||
dependencies.InjectDependencies(_impl, oneOff: true);
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
|
||||
|
||||
_impl!.Initialize();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Maths;
|
||||
using Vector3 = Robust.Shared.Maths.Vector3;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Animations
|
||||
{
|
||||
@@ -117,7 +120,7 @@ namespace Robust.Client.Animations
|
||||
switch (a)
|
||||
{
|
||||
case Vector2 vector2:
|
||||
return Vector2.InterpolateCubic((Vector2) preA, vector2, (Vector2) b, (Vector2) postB, t);
|
||||
return Vector2Helpers.InterpolateCubic((Vector2) preA, vector2, (Vector2) b, (Vector2) postB, t);
|
||||
case Vector3 vector3:
|
||||
return Vector3.InterpolateCubic((Vector3) preA, vector3, (Vector3) b, (Vector3) postB, t);
|
||||
case Vector4 vector4:
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Client.Animations
|
||||
DebugTools.AssertNotNull(LayerKey);
|
||||
|
||||
var entity = (EntityUid) context;
|
||||
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<ISpriteComponent>(entity);
|
||||
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<SpriteComponent>(entity);
|
||||
|
||||
var playingTime = prevPlayingTime + frameTime;
|
||||
var keyFrameIndex = prevKeyFrameIndex;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Robust.Client.Audio;
|
||||
@@ -8,13 +8,17 @@ public sealed class AudioStream
|
||||
public TimeSpan Length { get; }
|
||||
internal ClydeHandle? ClydeHandle { get; }
|
||||
public string? Name { get; }
|
||||
public string? Title { get; }
|
||||
public string? Artist { get; }
|
||||
public int ChannelCount { get; }
|
||||
|
||||
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null)
|
||||
internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null, string? title = null, string? artist = null)
|
||||
{
|
||||
ClydeHandle = handle;
|
||||
Length = length;
|
||||
ChannelCount = channelCount;
|
||||
Name = name;
|
||||
Title = title;
|
||||
Artist = artist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -15,7 +17,6 @@ public enum MidiRendererStatus : byte
|
||||
|
||||
public interface IMidiRenderer : IDisposable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The buffered audio source of this renderer.
|
||||
/// </summary>
|
||||
@@ -34,6 +35,7 @@ public interface IMidiRenderer : IDisposable
|
||||
/// <summary>
|
||||
/// This increases all note on velocities to 127.
|
||||
/// </summary>
|
||||
[Obsolete($"Use {nameof(VelocityOverride)} instead, you can set it to 127 to achieve the same effect.")]
|
||||
bool VolumeBoost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -94,6 +96,27 @@ public interface IMidiRenderer : IDisposable
|
||||
/// </summary>
|
||||
double SequencerTimeScale { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this renderer will subscribe to another and copy its events.
|
||||
/// See <see cref="FilteredChannels"/> to filter specific channels.
|
||||
/// </summary>
|
||||
IMidiRenderer? Master { get; set; }
|
||||
|
||||
// NOTE: Why is the properties below BitArray, you ask?
|
||||
// Well see, MIDI 2.0 supports up to 256(!) channels as opposed to MIDI 1.0's meekly 16 channels...
|
||||
// I'd like us to support MIDI 2.0 one day so I'm just future-proofing here. Also BitArray is cool!
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to filter out note events from certain channels.
|
||||
/// Only NoteOn will be filtered.
|
||||
/// </summary>
|
||||
BitArray FilteredChannels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to override all NoteOn velocities. Set to null to disable.
|
||||
/// </summary>
|
||||
byte? VelocityOverride { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start listening for midi input.
|
||||
/// </summary>
|
||||
@@ -120,6 +143,16 @@ public interface IMidiRenderer : IDisposable
|
||||
/// </summary>
|
||||
void StopAllNotes();
|
||||
|
||||
/// <summary>
|
||||
/// Reset renderer back to a clean state.
|
||||
/// </summary>
|
||||
void SystemReset();
|
||||
|
||||
/// <summary>
|
||||
/// Clears all scheduled events.
|
||||
/// </summary>
|
||||
void ClearAllEvents();
|
||||
|
||||
/// <summary>
|
||||
/// Render and play MIDI to the audio source.
|
||||
/// </summary>
|
||||
@@ -151,7 +184,7 @@ public interface IMidiRenderer : IDisposable
|
||||
/// This is only used if <see cref="Mono"/> is set to True
|
||||
/// and <see cref="TrackingEntity"/> is null.
|
||||
/// </summary>
|
||||
EntityCoordinates? TrackingCoordinates { get; set; }
|
||||
MapCoordinates? TrackingCoordinates { get; set; }
|
||||
|
||||
MidiRendererState RendererState { get; }
|
||||
|
||||
@@ -159,7 +192,8 @@ public interface IMidiRenderer : IDisposable
|
||||
/// Send a midi event for the renderer to play.
|
||||
/// </summary>
|
||||
/// <param name="midiEvent">The midi event to be played</param>
|
||||
void SendMidiEvent(RobustMidiEvent midiEvent);
|
||||
/// <param name="raiseEvent">Whether to raise an event for this event.</param>
|
||||
void SendMidiEvent(RobustMidiEvent midiEvent, bool raiseEvent = true);
|
||||
|
||||
/// <summary>
|
||||
/// Schedule a MIDI event to be played at a later time.
|
||||
@@ -172,7 +206,7 @@ public interface IMidiRenderer : IDisposable
|
||||
/// <summary>
|
||||
/// Apply a certain state to the renderer.
|
||||
/// </summary>
|
||||
void ApplyState(MidiRendererState state);
|
||||
void ApplyState(MidiRendererState state, bool filterChannels = false);
|
||||
|
||||
/// <summary>
|
||||
/// Actually disposes of this renderer. Do NOT use outside the MIDI thread.
|
||||
|
||||
@@ -3,20 +3,27 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Threading;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -24,6 +31,15 @@ namespace Robust.Client.Audio.Midi;
|
||||
|
||||
internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
public const string SoundfontEnvironmentVariable = "ROBUST_SOUNDFONT_OVERRIDE";
|
||||
|
||||
private int _minRendererParallel;
|
||||
private float _occlusionUpdateDelay;
|
||||
private float _positionUpdateDelay;
|
||||
|
||||
[ViewVariables] private TimeSpan _nextOcclusionUpdate = TimeSpan.Zero;
|
||||
[ViewVariables] private TimeSpan _nextPositionUpdate = TimeSpan.Zero;
|
||||
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceCacheInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
@@ -31,6 +47,9 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
[Dependency] private readonly IClydeAudio _clydeAudio = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
[Dependency] private readonly ILogManager _logger = default!;
|
||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||
[Dependency] private readonly IRuntimeLog _runtime = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private SharedPhysicsSystem _broadPhaseSystem = default!;
|
||||
|
||||
@@ -57,11 +76,10 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<IMidiRenderer> _renderers = new();
|
||||
[ViewVariables] private readonly List<IMidiRenderer> _renderers = new();
|
||||
|
||||
private bool _alive = true;
|
||||
private Settings? _settings;
|
||||
[ViewVariables] private Settings? _settings;
|
||||
private Thread? _midiThread;
|
||||
private ISawmill _midiSawmill = default!;
|
||||
private float _volume = 0f;
|
||||
@@ -87,10 +105,12 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
"/usr/share/soundfonts/default.sf2",
|
||||
"/usr/share/soundfonts/default.dls",
|
||||
"/usr/share/soundfonts/FluidR3_GM.sf2",
|
||||
"/usr/share/soundfonts/FluidR3_GM2-2.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/FluidR3_GM2-2.sf2",
|
||||
"/usr/share/sounds/sf2/TimGM6mb.sf2",
|
||||
};
|
||||
|
||||
@@ -103,7 +123,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
private const string ContentCustomSoundfontDirectory = "/Audio/MidiCustom/";
|
||||
|
||||
private static ResourcePath CustomSoundfontDirectory = new ResourcePath("/soundfonts/");
|
||||
private static ResPath CustomSoundfontDirectory = new("/soundfonts/");
|
||||
|
||||
private readonly ResourceLoaderCallbacks _soundfontLoaderCallbacks;
|
||||
|
||||
@@ -111,7 +131,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
private bool _failedInitialize;
|
||||
|
||||
private NFluidsynth.Logger.LoggerDelegate _loggerDelegate = default!;
|
||||
private ISawmill _sawmill = default!;
|
||||
private ISawmill _fluidsynthSawmill = default!;
|
||||
private float _maxCastLength;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -126,20 +146,28 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (FluidsynthInitialized || _failedInitialize) return;
|
||||
|
||||
_volume = _cfgMan.GetCVar(CVars.MidiVolume);
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
|
||||
{
|
||||
_volume = value;
|
||||
_volumeDirty = true;
|
||||
}, true);
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiMinRendererParallel,
|
||||
value => _minRendererParallel = value, true);
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiOcclusionUpdateDelay,
|
||||
value => _occlusionUpdateDelay = value, true);
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiPositionUpdateDelay,
|
||||
value => _positionUpdateDelay = value, true);
|
||||
|
||||
_midiSawmill = _logger.GetSawmill("midi");
|
||||
#if DEBUG
|
||||
_midiSawmill.Level = LogLevel.Debug;
|
||||
#else
|
||||
_midiSawmill.Level = LogLevel.Error;
|
||||
#endif
|
||||
_sawmill = _logger.GetSawmill("midi.fluidsynth");
|
||||
_fluidsynthSawmill = _logger.GetSawmill("midi.fluidsynth");
|
||||
_loggerDelegate = LoggerDelegate;
|
||||
|
||||
if (!_resourceManager.UserData.Exists(CustomSoundfontDirectory))
|
||||
@@ -163,8 +191,6 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
_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";
|
||||
@@ -172,8 +198,11 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
_settings["audio.period-size"].IntValue = 4096;
|
||||
_settings["midi.autoconnect"].IntValue = 1;
|
||||
_settings["player.reset-synth"].IntValue = 0;
|
||||
_settings["synth.midi-channels"].IntValue = Math.Clamp(RobustMidiEvent.MaxChannels, 16, 256);
|
||||
_settings["synth.midi-bank-select"].StringValue = "gm";
|
||||
//_settings["synth.verbose"].IntValue = 1; // Useful for debugging.
|
||||
|
||||
_parallel.AddAndInvokeParallelCountChanged(UpdateParallelCount);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -191,6 +220,18 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
FluidsynthInitialized = true;
|
||||
}
|
||||
|
||||
private void UpdateParallelCount()
|
||||
{
|
||||
if (_settings == null)
|
||||
return;
|
||||
|
||||
_settings["synth.polyphony"].IntValue = Math.Clamp(1024 + (int)(Math.Log2(_parallel.ParallelProcessCount) * 2048), 1, 65535);
|
||||
_settings["synth.cpu-cores"].IntValue = Math.Clamp(_parallel.ParallelProcessCount, 1, 256);
|
||||
|
||||
_midiSawmill.Debug($"Synth Cores: {_settings["synth.cpu-cores"].IntValue}");
|
||||
_midiSawmill.Debug($"Synth Polyphony: {_settings["synth.polyphony"].IntValue}");
|
||||
}
|
||||
|
||||
private void OnRaycastLengthChanged(float value)
|
||||
{
|
||||
_maxCastLength = value;
|
||||
@@ -207,7 +248,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
NFluidsynth.Logger.LogLevel.Debug => LogLevel.Debug,
|
||||
_ => LogLevel.Debug
|
||||
};
|
||||
_sawmill.Log(rLevel, message);
|
||||
_fluidsynthSawmill.Log(rLevel, message);
|
||||
}
|
||||
|
||||
public IMidiRenderer? GetNewRenderer(bool mono = true)
|
||||
@@ -234,10 +275,11 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
var renderer = new MidiRenderer(_settings!, soundfontLoader, mono, this, _clydeAudio, _taskManager, _midiSawmill);
|
||||
|
||||
_midiSawmill.Debug($"Loading soundfont {FallbackSoundfont}");
|
||||
_midiSawmill.Debug($"Loading fallback soundfont {FallbackSoundfont}");
|
||||
// Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont.
|
||||
renderer.LoadSoundfont(FallbackSoundfont);
|
||||
|
||||
// Load system-specific soundfonts.
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
foreach (var filepath in LinuxSoundfonts)
|
||||
@@ -247,8 +289,8 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
try
|
||||
{
|
||||
_midiSawmill.Debug($"Loading OS soundfont {filepath}");
|
||||
renderer.LoadSoundfont(filepath);
|
||||
_midiSawmill.Debug($"Loaded Linux soundfont {filepath}");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -262,7 +304,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (File.Exists(OsxSoundfont) && SoundFont.IsSoundFont(OsxSoundfont))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading soundfont {OsxSoundfont}");
|
||||
_midiSawmill.Debug($"Loading OS soundfont {OsxSoundfont}");
|
||||
renderer.LoadSoundfont(OsxSoundfont);
|
||||
}
|
||||
}
|
||||
@@ -270,27 +312,42 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
if (File.Exists(WindowsSoundfont) && SoundFont.IsSoundFont(WindowsSoundfont))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading soundfont {WindowsSoundfont}");
|
||||
_midiSawmill.Debug($"Loading OS soundfont {WindowsSoundfont}");
|
||||
renderer.LoadSoundfont(WindowsSoundfont);
|
||||
}
|
||||
}
|
||||
|
||||
// Load content-specific custom soundfonts, which could override the system/fallback soundfont.
|
||||
_midiSawmill.Debug($"Loading soundfonts from {ContentCustomSoundfontDirectory}");
|
||||
// Maybe load soundfont specified in environment variable.
|
||||
// Load it here so it can override system soundfonts but not content or user data soundfonts.
|
||||
if (Environment.GetEnvironmentVariable(SoundfontEnvironmentVariable) is {} soundfontOverride)
|
||||
{
|
||||
if (File.Exists(soundfontOverride) && SoundFont.IsSoundFont(soundfontOverride))
|
||||
{
|
||||
_midiSawmill.Debug($"Loading environment variable soundfont {soundfontOverride}");
|
||||
renderer.LoadSoundfont(soundfontOverride);
|
||||
}
|
||||
}
|
||||
|
||||
// Load content-specific custom soundfonts, which should override the system/fallback soundfont.
|
||||
_midiSawmill.Debug($"Loading soundfonts from content directory {ContentCustomSoundfontDirectory}");
|
||||
foreach (var file in _resourceManager.ContentFindFiles(ContentCustomSoundfontDirectory))
|
||||
{
|
||||
if (file.Extension != "sf2" && file.Extension != "dls") continue;
|
||||
_midiSawmill.Debug($"Loading soundfont {file}");
|
||||
if (file.Extension != "sf2" && file.Extension != "dls" && file.Extension != "sf3") continue;
|
||||
_midiSawmill.Debug($"Loading content soundfont {file}");
|
||||
renderer.LoadSoundfont(file.ToString());
|
||||
}
|
||||
|
||||
var userDataPath = _resourceManager.UserData.RootDir == null
|
||||
? CustomSoundfontDirectory
|
||||
: new ResPath(_resourceManager.UserData.RootDir) / CustomSoundfontDirectory.ToRelativePath();
|
||||
|
||||
// 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;
|
||||
_midiSawmill.Debug($"Loading soundfonts from user data directory {userDataPath}");
|
||||
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}");
|
||||
if (file.Extension != "sf2" && file.Extension != "dls" && file.Extension != "sf3") continue;
|
||||
_midiSawmill.Debug($"Loading user soundfont {file}");
|
||||
renderer.LoadSoundfont(file.ToString());
|
||||
}
|
||||
|
||||
@@ -320,72 +377,108 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
lock (_renderers)
|
||||
{
|
||||
foreach (var renderer in _renderers)
|
||||
if (_renderers.Count == 0)
|
||||
return;
|
||||
|
||||
var transQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
|
||||
var opts = new ParallelOptions { MaxDegreeOfParallelism = _parallel.ParallelProcessCount };
|
||||
|
||||
if (_renderers.Count > _minRendererParallel)
|
||||
{
|
||||
if (renderer.Disposed)
|
||||
continue;
|
||||
|
||||
if(_volumeDirty)
|
||||
renderer.Source.SetVolume(Volume);
|
||||
|
||||
if (!renderer.Mono)
|
||||
Parallel.ForEach(_renderers, opts, renderer => UpdateRenderer(renderer, transQuery, physicsQuery));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.Source.SetGlobal();
|
||||
continue;
|
||||
}
|
||||
|
||||
MapCoordinates? mapPos = null;
|
||||
var trackingEntity = renderer.TrackingEntity != null && !_entityManager.Deleted(renderer.TrackingEntity);
|
||||
if (trackingEntity)
|
||||
{
|
||||
renderer.TrackingCoordinates = _entityManager.GetComponent<TransformComponent>(renderer.TrackingEntity!.Value).Coordinates;
|
||||
}
|
||||
|
||||
if (renderer.TrackingCoordinates != null)
|
||||
{
|
||||
mapPos = renderer.TrackingCoordinates.Value.ToMap(_entityManager);
|
||||
}
|
||||
|
||||
if (mapPos != null && mapPos.Value.MapId == _eyeManager.CurrentMap)
|
||||
{
|
||||
var pos = mapPos.Value;
|
||||
|
||||
var sourceRelative = pos.Position - _eyeManager.CurrentEye.Position.Position;
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length > 0)
|
||||
{
|
||||
occlusion = _broadPhaseSystem.IntersectRayPenetration(
|
||||
pos.MapId,
|
||||
new CollisionRay(
|
||||
_eyeManager.CurrentEye.Position.Position,
|
||||
sourceRelative.Normalized,
|
||||
OcclusionCollisionMask),
|
||||
MathF.Min(sourceRelative.Length, _maxCastLength),
|
||||
renderer.TrackingEntity);
|
||||
}
|
||||
|
||||
renderer.Source.SetOcclusion(occlusion);
|
||||
|
||||
if (!renderer.Source.SetPosition(pos.Position))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (trackingEntity)
|
||||
{
|
||||
var vel = _broadPhaseSystem.GetMapLinearVelocity(renderer.TrackingEntity!.Value);
|
||||
renderer.Source.SetVelocity(vel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Source.SetOcclusion(float.MaxValue);
|
||||
UpdateRenderer(renderer, transQuery, physicsQuery);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_nextOcclusionUpdate < _timing.RealTime)
|
||||
_nextOcclusionUpdate = _timing.RealTime.Add(TimeSpan.FromSeconds(_occlusionUpdateDelay));
|
||||
|
||||
if (_nextPositionUpdate < _timing.RealTime)
|
||||
_nextPositionUpdate = _timing.RealTime.Add(TimeSpan.FromSeconds(_positionUpdateDelay));
|
||||
|
||||
_volumeDirty = false;
|
||||
}
|
||||
private void UpdateRenderer(IMidiRenderer renderer, EntityQuery<TransformComponent> transQuery,
|
||||
EntityQuery<PhysicsComponent> physicsQuery)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (renderer.Disposed)
|
||||
return;
|
||||
|
||||
if (_volumeDirty)
|
||||
renderer.Source.SetVolume(Volume);
|
||||
|
||||
if (!renderer.Mono)
|
||||
{
|
||||
renderer.Source.SetGlobal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nextPositionUpdate < _timing.RealTime)
|
||||
{
|
||||
if (renderer.TrackingEntity is {} trackedEntity && !_entityManager.Deleted(trackedEntity))
|
||||
{
|
||||
renderer.TrackingCoordinates = transQuery.GetComponent(renderer.TrackingEntity!.Value).MapPosition;
|
||||
}
|
||||
else if (renderer.TrackingCoordinates == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!renderer.Source.SetPosition(renderer.TrackingCoordinates.Value.Position))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vel = _broadPhaseSystem.GetMapLinearVelocity(renderer.TrackingEntity!.Value,
|
||||
xformQuery: transQuery, physicsQuery: physicsQuery);
|
||||
renderer.Source.SetVelocity(vel);
|
||||
}
|
||||
|
||||
if (renderer.TrackingCoordinates != null && renderer.TrackingCoordinates.Value.MapId == _eyeManager.CurrentMap)
|
||||
{
|
||||
if (_nextOcclusionUpdate >= _timing.RealTime)
|
||||
return;
|
||||
|
||||
var pos = renderer.TrackingCoordinates.Value;
|
||||
|
||||
var sourceRelative = pos.Position - _eyeManager.CurrentEye.Position.Position;
|
||||
var occlusion = 0f;
|
||||
if (sourceRelative.Length() > 0)
|
||||
{
|
||||
occlusion = _broadPhaseSystem.IntersectRayPenetration(
|
||||
pos.MapId,
|
||||
new CollisionRay(
|
||||
_eyeManager.CurrentEye.Position.Position,
|
||||
sourceRelative.Normalized(),
|
||||
OcclusionCollisionMask),
|
||||
MathF.Min(sourceRelative.Length(), _maxCastLength),
|
||||
renderer.TrackingEntity);
|
||||
}
|
||||
|
||||
renderer.Source.SetOcclusion(occlusion);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Source.SetOcclusion(float.MaxValue);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_runtime.LogException(ex, _midiSawmill.Name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main method for the thread rendering the midi audio.
|
||||
@@ -400,7 +493,12 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
{
|
||||
var renderer = _renderers[i];
|
||||
if (!renderer.Disposed)
|
||||
{
|
||||
if (renderer.Master is { Disposed: true })
|
||||
renderer.Master = null;
|
||||
|
||||
renderer.Render();
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.InternalDispose();
|
||||
@@ -476,7 +574,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
|
||||
Stream? stream;
|
||||
var resourceCache = _parent._resourceManager;
|
||||
var resourcePath = new ResourcePath(filename);
|
||||
var resourcePath = new ResPath(filename);
|
||||
|
||||
if (resourcePath.IsRooted)
|
||||
{
|
||||
@@ -550,7 +648,7 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int Seek(IntPtr sfHandle, int offset, SeekOrigin origin)
|
||||
public override int Seek(IntPtr sfHandle, long offset, SeekOrigin origin)
|
||||
{
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
|
||||
@@ -559,11 +657,11 @@ internal sealed partial class MidiManager : IMidiManager
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int Tell(IntPtr sfHandle)
|
||||
public override long Tell(IntPtr sfHandle)
|
||||
{
|
||||
var stream = _openStreams[(int) sfHandle];
|
||||
|
||||
return (int) stream.Position;
|
||||
return (long) stream.Position;
|
||||
}
|
||||
|
||||
public override int Close(IntPtr sfHandle)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using JetBrains.Annotations;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Asynchronous;
|
||||
@@ -9,7 +11,6 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
@@ -21,14 +22,13 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
// TODO: Make this a replicated CVar in MidiManager
|
||||
private const int MidiSizeLimit = 2000000;
|
||||
private const double BytesToMegabytes = 0.000001d;
|
||||
private const int ChannelCount = 16;
|
||||
private const int ChannelCount = RobustMidiEvent.MaxChannels;
|
||||
|
||||
private readonly ISawmill _midiSawmill;
|
||||
|
||||
private readonly Settings _settings;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private bool _debugEvents = false;
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _debugEvents = false;
|
||||
|
||||
// Kept around to avoid the loader callbacks getting GC'd
|
||||
// ReSharper disable once NotAccessedField.Local
|
||||
@@ -48,8 +48,9 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
private readonly SequencerClientId _robustRegister;
|
||||
private readonly SequencerClientId _debugRegister;
|
||||
|
||||
[ViewVariables]
|
||||
private MidiRendererState _rendererState = new();
|
||||
[ViewVariables] private MidiRendererState _rendererState = new();
|
||||
|
||||
private IMidiRenderer? _master;
|
||||
public MidiRendererState RendererState => _rendererState;
|
||||
public IClydeBufferedAudioSource Source { get; set; }
|
||||
IClydeBufferedAudioSource IMidiRenderer.Source => Source;
|
||||
@@ -70,8 +71,8 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
{
|
||||
for (byte i = 0; i < ChannelCount; i++)
|
||||
{
|
||||
// Channel 9 is the percussion channel. Let's not change its instrument...
|
||||
if (i == 9)
|
||||
// Don't change percussion channel instrument.
|
||||
if (i == RobustMidiEvent.PercussionChannel)
|
||||
continue;
|
||||
|
||||
SendMidiEvent(RobustMidiEvent.ProgramChange(i, value, SequencerTick));
|
||||
@@ -96,11 +97,14 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
{
|
||||
for (byte i = 0; i < ChannelCount; i++)
|
||||
{
|
||||
// Channel 9 is the percussion channel. Let's not change its bank...
|
||||
if (i == 9)
|
||||
// Don't change percussion channel bank.
|
||||
if (i == RobustMidiEvent.PercussionChannel)
|
||||
continue;
|
||||
|
||||
SendMidiEvent(RobustMidiEvent.BankSelect(i, value, SequencerTick));
|
||||
|
||||
// Re-select program.
|
||||
SendMidiEvent(RobustMidiEvent.ProgramChange(i, _midiProgram, SequencerTick));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +132,11 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool DisablePercussionChannel { get; set; } = true;
|
||||
public bool DisablePercussionChannel
|
||||
{
|
||||
get => FilteredChannels[RobustMidiEvent.PercussionChannel];
|
||||
set => FilteredChannels[RobustMidiEvent.PercussionChannel] = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool DisableProgramChangeEvent { get; set; } = true;
|
||||
@@ -181,13 +189,62 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool VolumeBoost { get; set; }
|
||||
[Obsolete($"Use {nameof(VelocityOverride)} instead, you can set it to 127 to achieve the same effect.")]
|
||||
public bool VolumeBoost
|
||||
{
|
||||
get => VelocityOverride == 127;
|
||||
set => VelocityOverride = value ? 127 : null;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityUid? TrackingEntity { get; set; } = null;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityCoordinates? TrackingCoordinates { get; set; } = null;
|
||||
public MapCoordinates? TrackingCoordinates { get; set; } = null;
|
||||
|
||||
[ViewVariables]
|
||||
public BitArray FilteredChannels { get; } = new(RobustMidiEvent.MaxChannels);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public byte? VelocityOverride { get; set; } = null;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public IMidiRenderer? Master
|
||||
{
|
||||
get => _master;
|
||||
set
|
||||
{
|
||||
if (value == _master)
|
||||
return;
|
||||
|
||||
if (_master is { Disposed: false })
|
||||
{
|
||||
try
|
||||
{
|
||||
_master.OnMidiEvent -= SendMidiEvent;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
_master = value;
|
||||
|
||||
if (_master == null)
|
||||
return;
|
||||
|
||||
_master.OnMidiEvent += SendMidiEvent;
|
||||
ApplyState(_master.RendererState, true);
|
||||
MidiBank = _midiBank;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables, UsedImplicitly]
|
||||
private double CpuLoad => !_synth.Disposed ? _synth.CpuLoad : 0;
|
||||
|
||||
public event Action<RobustMidiEvent>? OnMidiEvent;
|
||||
public event Action? OnMidiPlayerFinished;
|
||||
|
||||
internal MidiRenderer(Settings settings, SoundFontLoader soundFontLoader, bool mono,
|
||||
IMidiManager midiManager, IClydeAudio clydeAudio, ITaskManager taskManager, ISawmill midiSawmill)
|
||||
@@ -354,6 +411,16 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
public void SystemReset()
|
||||
{
|
||||
SendMidiEvent(RobustMidiEvent.SystemReset(SequencerTick));
|
||||
}
|
||||
|
||||
public void ClearAllEvents()
|
||||
{
|
||||
_sequencer.RemoveEvents(SequencerClientId.Wildcard, SequencerClientId.Wildcard, -1);
|
||||
}
|
||||
|
||||
public void LoadSoundfont(string filename, bool resetPresets = true)
|
||||
{
|
||||
lock (_playerStateLock)
|
||||
@@ -363,9 +430,6 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<RobustMidiEvent>? OnMidiEvent;
|
||||
public event Action? OnMidiPlayerFinished;
|
||||
|
||||
void IMidiRenderer.Render()
|
||||
{
|
||||
Render();
|
||||
@@ -427,7 +491,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
if (!Source.IsPlaying) Source.StartPlaying();
|
||||
}
|
||||
|
||||
public void ApplyState(MidiRendererState state)
|
||||
public void ApplyState(MidiRendererState state, bool filterChannels = false)
|
||||
{
|
||||
lock (_playerStateLock)
|
||||
{
|
||||
@@ -435,6 +499,9 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
|
||||
for (var channel = 0; channel < ChannelCount; channel++)
|
||||
{
|
||||
if (filterChannels && !FilteredChannels[channel])
|
||||
continue;
|
||||
|
||||
_synth.AllNotesOff(channel);
|
||||
|
||||
_synth.PitchBend(channel, state.PitchBend.AsSpan[channel]);
|
||||
@@ -457,7 +524,8 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
_synth.ProgramChange(channel, state.Program.AsSpan[channel]);
|
||||
var program = DisableProgramChangeEvent ? MidiProgram : state.Program.AsSpan[channel];
|
||||
_synth.ProgramChange(channel, program);
|
||||
|
||||
for (var key = 0; key < state.NoteVelocities.AsSpan[channel].AsSpan.Length; key++)
|
||||
{
|
||||
@@ -482,7 +550,12 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
}
|
||||
}
|
||||
|
||||
public void SendMidiEvent(RobustMidiEvent midiEvent)
|
||||
private void SendMidiEvent(RobustMidiEvent midiEvent)
|
||||
{
|
||||
SendMidiEvent(midiEvent, true);
|
||||
}
|
||||
|
||||
public void SendMidiEvent(RobustMidiEvent midiEvent, bool raiseEvent)
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
@@ -500,11 +573,10 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
break;
|
||||
|
||||
case RobustMidiCommand.NoteOn:
|
||||
// Channel 9 is the percussion channel. We only block NoteOn events to it.
|
||||
if (DisablePercussionChannel && midiEvent.Channel == 9)
|
||||
return;
|
||||
if (FilteredChannels[midiEvent.Channel])
|
||||
break;
|
||||
|
||||
var velocity = (byte)(VolumeBoost ? 127 : midiEvent.Velocity);
|
||||
var velocity = VelocityOverride ?? midiEvent.Velocity;
|
||||
|
||||
_rendererState.NoteVelocities.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Key] = velocity;
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, velocity);
|
||||
@@ -518,7 +590,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
case RobustMidiCommand.ControlChange:
|
||||
// CC0 is bank selection
|
||||
if (midiEvent.Control == 0x0 && DisableProgramChangeEvent)
|
||||
return;
|
||||
break;
|
||||
|
||||
_rendererState.Controllers.AsSpan[midiEvent.Channel].AsSpan[midiEvent.Control] = midiEvent.Value;
|
||||
if(midiEvent.Control != 0x0)
|
||||
@@ -529,7 +601,7 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
|
||||
case RobustMidiCommand.ProgramChange:
|
||||
if (DisableProgramChangeEvent)
|
||||
return;
|
||||
break;
|
||||
|
||||
_rendererState.Program.AsSpan[midiEvent.Channel] = midiEvent.Program;
|
||||
_synth.ProgramChange(midiEvent.Channel, midiEvent.Program);
|
||||
@@ -556,14 +628,14 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
switch (midiEvent.Control)
|
||||
{
|
||||
case 0x0 when midiEvent.Status == 0xFF:
|
||||
_rendererState = new ();
|
||||
_rendererState = new MidiRendererState();
|
||||
_synth.SystemReset();
|
||||
|
||||
// Reset the instrument to the one we were using.
|
||||
if (DisableProgramChangeEvent)
|
||||
{
|
||||
MidiProgram = _midiProgram;
|
||||
MidiBank = _midiBank;
|
||||
MidiProgram = _midiProgram;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -592,7 +664,10 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
//_midiSawmill.Error("Exception while sending midi event of type {0}: {1}", midiEvent.Type, e, midiEvent);
|
||||
}
|
||||
|
||||
_taskManager.RunOnMainThread(() => OnMidiEvent?.Invoke(midiEvent));
|
||||
if (raiseEvent)
|
||||
{
|
||||
_taskManager.RunOnMainThread(() => OnMidiEvent?.Invoke(midiEvent));
|
||||
}
|
||||
}
|
||||
|
||||
public void ScheduleMidiEvent(RobustMidiEvent midiEvent, uint time, bool absolute = false)
|
||||
@@ -628,6 +703,9 @@ internal sealed class MidiRenderer : IMidiRenderer
|
||||
/// <inheritdoc />
|
||||
void IMidiRenderer.InternalDispose()
|
||||
{
|
||||
OnMidiEvent = null;
|
||||
OnMidiPlayerFinished = null;
|
||||
|
||||
Source?.Dispose();
|
||||
_driver?.Dispose();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Audio.Midi;
|
||||
|
||||
@@ -12,7 +13,7 @@ public struct MidiRendererState
|
||||
internal FixedArray16<byte> ChannelPressure;
|
||||
internal FixedArray16<ushort> PitchBend;
|
||||
|
||||
internal Span<byte> AsSpan => MemoryMarshal.CreateSpan(ref NoteVelocities._00._00, 4160);
|
||||
[ViewVariables] internal Span<byte> AsSpan => MemoryMarshal.CreateSpan(ref NoteVelocities._00._00, 4160);
|
||||
|
||||
static unsafe MidiRendererState()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Robust.Client.Configuration;
|
||||
using Robust.Client.Debugging;
|
||||
@@ -22,7 +23,7 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Client
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class BaseClient : IBaseClient
|
||||
public sealed class BaseClient : IBaseClient, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _net = default!;
|
||||
[Dependency] private readonly IPlayerManager _playMan = default!;
|
||||
@@ -32,6 +33,7 @@ namespace Robust.Client
|
||||
[Dependency] private readonly IDiscordRichPresence _discord = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStates = default!;
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ushort DefaultPort { get; } = 1212;
|
||||
@@ -48,6 +50,7 @@ namespace Robust.Client
|
||||
public string? LastDisconnectReason { get; private set; }
|
||||
|
||||
private (TimeSpan, GameTick) _timeBase;
|
||||
private ISawmill _logger = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
@@ -63,12 +66,20 @@ namespace Robust.Client
|
||||
_configManager.OnValueChanged(CVars.NetTickrate, TickRateChanged, invokeImmediately: true);
|
||||
|
||||
_playMan.Initialize();
|
||||
_playMan.PlayerListUpdated += OnPlayerListUpdated;
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void OnPlayerListUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
var serverPlayers = _playMan.PlayerCount;
|
||||
if (_net.ServerChannel != null && GameInfo != null && _net.IsConnected)
|
||||
_discord.Update(GameInfo.ServerName, _net.ServerChannel.UserName, GameInfo.ServerMaxPlayers.ToString(), serverPlayers.ToString());
|
||||
}
|
||||
|
||||
private void SyncTimeBase(MsgSyncTimeBase message)
|
||||
{
|
||||
Logger.DebugS("client", $"Synchronized time base: {message.Tick}: {message.Time}");
|
||||
_logger.Debug($"Synchronized time base: {message.Tick}: {message.Time}");
|
||||
|
||||
if (RunLevel >= ClientRunLevel.Connected)
|
||||
_timing.TimeBase = (message.Time, message.Tick);
|
||||
@@ -84,7 +95,7 @@ namespace Robust.Client
|
||||
}
|
||||
|
||||
_timing.SetTickRateAt((byte) tickrate, info.TickChanged);
|
||||
Logger.InfoS("client", $"Tickrate changed to: {tickrate} on tick {_timing.CurTick}");
|
||||
_logger.Info($"Tickrate changed to: {tickrate} on tick {_timing.CurTick}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -167,7 +178,7 @@ namespace Robust.Client
|
||||
|
||||
var userName = _net.ServerChannel!.UserName;
|
||||
var userId = _net.ServerChannel.UserId;
|
||||
_discord.Update(info.ServerName, userName, info.ServerMaxPlayers.ToString());
|
||||
|
||||
// start up player management
|
||||
_playMan.Startup();
|
||||
|
||||
@@ -175,6 +186,10 @@ namespace Robust.Client
|
||||
_playMan.LocalPlayer.Name = userName;
|
||||
|
||||
_playMan.LocalPlayer.StatusChanged += OnLocalStatusChanged;
|
||||
|
||||
var serverPlayers = _playMan.PlayerCount;
|
||||
_discord.Update(info.ServerName, userName, info.ServerMaxPlayers.ToString(), serverPlayers.ToString());
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -265,11 +280,16 @@ namespace Robust.Client
|
||||
|
||||
private void OnRunLevelChanged(ClientRunLevel newRunLevel)
|
||||
{
|
||||
Logger.DebugS("client", $"Runlevel changed to: {newRunLevel}");
|
||||
_logger.Debug($"Runlevel changed to: {newRunLevel}");
|
||||
var args = new RunLevelChangedEventArgs(RunLevel, newRunLevel);
|
||||
RunLevel = newRunLevel;
|
||||
RunLevelChanged?.Invoke(this, args);
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_logger = _logMan.GetSawmill("client");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -16,11 +16,15 @@ using Robust.Client.Profiling;
|
||||
using Robust.Client.Prototypes;
|
||||
using Robust.Client.Reflection;
|
||||
using Robust.Client.Replays;
|
||||
using Robust.Client.Replays.Loading;
|
||||
using Robust.Client.Replays.Playback;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Serialization;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Client.Upload;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Client.UserInterface.Themes;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Client.ViewVariables;
|
||||
@@ -39,6 +43,7 @@ using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Upload;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client
|
||||
@@ -63,6 +68,7 @@ namespace Robust.Client
|
||||
deps.Register<IComponentFactory, ComponentFactory>();
|
||||
deps.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<ClydeTileDefinitionManager, ClydeTileDefinitionManager>();
|
||||
deps.Register<GameController, GameController>();
|
||||
deps.Register<IGameController, GameController>();
|
||||
deps.Register<IGameControllerInternal, GameController>();
|
||||
@@ -76,7 +82,10 @@ namespace Robust.Client
|
||||
deps.Register<IClientEntityManager, ClientEntityManager>();
|
||||
deps.Register<IClientEntityManagerInternal, ClientEntityManager>();
|
||||
deps.Register<IEntityNetworkManager, ClientEntityManager>();
|
||||
deps.Register<IReplayLoadManager, ReplayLoadManager>();
|
||||
deps.Register<IReplayPlaybackManager, ReplayPlaybackManager>();
|
||||
deps.Register<IReplayRecordingManager, ReplayRecordingManager>();
|
||||
deps.Register<IReplayRecordingManagerInternal, ReplayRecordingManager>();
|
||||
deps.Register<IClientGameStateManager, ClientGameStateManager>();
|
||||
deps.Register<IBaseClient, BaseClient>();
|
||||
deps.Register<IPlayerManager, PlayerManager>();
|
||||
@@ -89,6 +98,8 @@ namespace Robust.Client
|
||||
deps.Register<IMidiManager, MidiManager>();
|
||||
deps.Register<IAuthManager, AuthManager>();
|
||||
deps.Register<ProfViewManager>();
|
||||
deps.Register<IGamePrototypeLoadManager, GamePrototypeLoadManager>();
|
||||
deps.Register<NetworkResourceManager>();
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
@@ -128,12 +139,14 @@ namespace Robust.Client
|
||||
deps.Register<IClientConGroupController, ClientConGroupController>();
|
||||
deps.Register<IScriptClient, ScriptClient>();
|
||||
deps.Register<IRobustSerializer, ClientRobustSerializer>();
|
||||
deps.Register<IRobustSerializerInternal, ClientRobustSerializer>();
|
||||
deps.Register<IClientRobustSerializer, ClientRobustSerializer>();
|
||||
deps.Register<IConfigurationManager, ClientNetConfigurationManager>();
|
||||
deps.Register<INetConfigurationManager, ClientNetConfigurationManager>();
|
||||
deps.Register<IConfigurationManagerInternal, ClientNetConfigurationManager>();
|
||||
deps.Register<IClientNetConfigurationManager, ClientNetConfigurationManager>();
|
||||
deps.Register<INetConfigurationManagerInternal, ClientNetConfigurationManager>();
|
||||
deps.Register<MarkupTagManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
public sealed partial class LightTreeComponent: Component, IComponentTreeComponent<PointLightComponent>
|
||||
{
|
||||
[ViewVariables]
|
||||
public DynamicTree<ComponentTreeEntry<PointLightComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -18,8 +19,9 @@ public sealed class LightTreeSystem : ComponentTreeSystem<LightTreeComponent, Po
|
||||
{
|
||||
// Really we should be rotating the light offset by the relative rotation. But I assume the light offset will
|
||||
// always be relatively small, so fuck it, this is probably faster than having to compute the angle every time.
|
||||
var radius = entry.Component.Radius + entry.Component.Offset.Length;
|
||||
return new Box2(pos - radius, pos + radius);
|
||||
var radius = entry.Component.Radius + entry.Component.Offset.Length();
|
||||
var radiusVec = new Vector2(radius, radius);
|
||||
return new Box2(pos - radiusVec, pos + radiusVec);
|
||||
}
|
||||
|
||||
protected override Box2 ExtractAabb(in ComponentTreeEntry<PointLightComponent> entry)
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Physics;
|
||||
namespace Robust.Client.ComponentTrees;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
public sealed partial class SpriteTreeComponent: Component, IComponentTreeComponent<SpriteComponent>
|
||||
{
|
||||
public DynamicTree<ComponentTreeEntry<SpriteComponent>> Tree { get; set; } = default!;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Configuration;
|
||||
@@ -12,6 +13,8 @@ namespace Robust.Client.Configuration;
|
||||
internal sealed class ClientNetConfigurationManager : NetConfigurationManager, IClientNetConfigurationManager
|
||||
{
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IReplayRecordingManager _replay = default!;
|
||||
|
||||
private bool _receivedInitialNwVars = false;
|
||||
|
||||
@@ -86,6 +89,12 @@ internal sealed class ClientNetConfigurationManager : NetConfigurationManager, I
|
||||
ApplyClientNetVarChange(message.NetworkedVars, message.Tick);
|
||||
else
|
||||
base.HandleNetVarMessage(message);
|
||||
|
||||
_replay.RecordClientMessage(new ReplayMessage.CvarChangeMsg()
|
||||
{
|
||||
ReplicatedCvars = message.NetworkedVars,
|
||||
TimeBase = _timing.TimeBase
|
||||
});
|
||||
}
|
||||
|
||||
protected override void ApplyNetVarChange(
|
||||
|
||||
@@ -14,26 +14,26 @@ internal sealed partial class ClientConsoleHost
|
||||
private int _completionSeq;
|
||||
|
||||
|
||||
public async Task<CompletionResult> GetCompletions(List<string> args, CancellationToken cancel)
|
||||
public async Task<CompletionResult> GetCompletions(List<string> args, string argStr, CancellationToken cancel)
|
||||
{
|
||||
// Last element is the command currently being typed. May be empty.
|
||||
|
||||
// Logger.Debug($"Running completions: {string.Join(", ", args)}");
|
||||
// _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);
|
||||
return await CalcCompletions(args, argStr, cancel);
|
||||
}
|
||||
|
||||
private Task<CompletionResult> CalcCompletions(List<string> args, CancellationToken cancel)
|
||||
private Task<CompletionResult> CalcCompletions(List<string> args, string argStr, CancellationToken cancel)
|
||||
{
|
||||
if (args.Count == 1)
|
||||
{
|
||||
// Typing out command name, handle this ourselves.
|
||||
var cmdOptions = CompletionResult.FromOptions(
|
||||
RegisteredCommands.Values
|
||||
AvailableCommands.Values
|
||||
.Where(c => CanExecute(c.Command))
|
||||
.OrderBy(c => c.Command)
|
||||
.Select(c => new CompletionOption(c.Command, c.Description)));
|
||||
@@ -41,13 +41,13 @@ internal sealed partial class ClientConsoleHost
|
||||
return Task.FromResult(cmdOptions);
|
||||
}
|
||||
|
||||
if (!RegisteredCommands.TryGetValue(args[0], out var cmd))
|
||||
if (!AvailableCommands.TryGetValue(args[0], out var cmd))
|
||||
return Task.FromResult(CompletionResult.Empty);
|
||||
|
||||
return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], cancel).AsTask();
|
||||
return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], argStr, cancel).AsTask();
|
||||
}
|
||||
|
||||
private Task<CompletionResult> DoServerCompletions(List<string> args, CancellationToken cancel)
|
||||
private Task<CompletionResult> DoServerCompletions(List<string> args, string argStr, CancellationToken cancel)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<CompletionResult>();
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel);
|
||||
@@ -62,6 +62,7 @@ internal sealed partial class ClientConsoleHost
|
||||
var msg = new MsgConCompletion
|
||||
{
|
||||
Args = args.ToArray(),
|
||||
ArgString = argStr,
|
||||
Seq = seq
|
||||
};
|
||||
|
||||
|
||||
@@ -9,25 +9,26 @@ 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.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.Console
|
||||
{
|
||||
public sealed class AddStringArgs : EventArgs
|
||||
{
|
||||
public string Text { get; }
|
||||
public FormattedMessage Text { get; }
|
||||
|
||||
public bool Local { get; }
|
||||
|
||||
public bool Error { get; }
|
||||
|
||||
public AddStringArgs(string text, bool local, bool error)
|
||||
public AddStringArgs(FormattedMessage text, bool local, bool error)
|
||||
{
|
||||
Text = text;
|
||||
Local = local;
|
||||
@@ -46,13 +47,19 @@ namespace Robust.Client.Console
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IClientConsoleHost" />
|
||||
internal sealed partial class ClientConsoleHost : ConsoleHost, IClientConsoleHost, IConsoleHostInternal
|
||||
internal sealed partial class ClientConsoleHost : ConsoleHost, IClientConsoleHost, IConsoleHostInternal, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _conGroup = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
|
||||
[ViewVariables] private readonly Dictionary<string, IConsoleCommand> _availableServerCommands = new();
|
||||
|
||||
private bool _requestedCommands;
|
||||
private ISawmill _logger = default!;
|
||||
private ISawmill _conLogger = default!;
|
||||
|
||||
public ClientConsoleHost() : base(isServer: false) {}
|
||||
|
||||
@@ -67,12 +74,45 @@ namespace Robust.Client.Console
|
||||
|
||||
_requestedCommands = false;
|
||||
NetManager.Connected += OnNetworkConnected;
|
||||
NetManager.Disconnect += OnNetworkDisconnected;
|
||||
_client.RunLevelChanged += OnLevelChanged;
|
||||
|
||||
LoadConsoleCommands();
|
||||
SendServerCommandRequest();
|
||||
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, IConsoleCommand> _availableCommands = new();
|
||||
public override IReadOnlyDictionary<string, IConsoleCommand> AvailableCommands => _availableCommands;
|
||||
|
||||
private void OnLevelChanged(object? sender, RunLevelChangedEventArgs e)
|
||||
{
|
||||
UpdateAvailableCommands();
|
||||
}
|
||||
|
||||
private void OnNetworkDisconnected(object? sender, NetDisconnectedArgs e)
|
||||
{
|
||||
_availableServerCommands.Clear();
|
||||
_requestedCommands = false;
|
||||
UpdateAvailableCommands();
|
||||
}
|
||||
|
||||
protected override void UpdateAvailableCommands()
|
||||
{
|
||||
_availableCommands.Clear();
|
||||
|
||||
foreach (var (name, cmd) in RegisteredCommands)
|
||||
{
|
||||
if (!cmd.RequireServerOrSingleplayer || (_client.RunLevel is ClientRunLevel.Initialize or ClientRunLevel.SinglePlayerGame))
|
||||
_availableCommands.Add(name, cmd);
|
||||
}
|
||||
|
||||
foreach (var (name, cmd) in _availableServerCommands)
|
||||
{
|
||||
_availableCommands.TryAdd(name, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessCommand(MsgConCmd message)
|
||||
{
|
||||
string? text = message.Text;
|
||||
@@ -93,10 +133,17 @@ namespace Robust.Client.Console
|
||||
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
|
||||
}
|
||||
|
||||
public override void WriteLine(ICommonSession? session, FormattedMessage msg)
|
||||
{
|
||||
AddFormattedLine(msg);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteError(ICommonSession? session, string text)
|
||||
{
|
||||
OutputText(text, true, true);
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(text);
|
||||
OutputText(msg, true, true);
|
||||
}
|
||||
|
||||
public bool IsCmdServer(IConsoleCommand cmd)
|
||||
@@ -112,8 +159,13 @@ namespace Robust.Client.Console
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
return;
|
||||
|
||||
WriteLine(null, "");
|
||||
var msg = new FormattedMessage();
|
||||
msg.PushColor(Color.Gold);
|
||||
msg.AddText("> " + command);
|
||||
msg.Pop();
|
||||
// echo the command locally
|
||||
WriteLine(null, "> " + command);
|
||||
OutputText(msg, true, false);
|
||||
|
||||
//Commands are processed locally and then sent to the server to be processed there again.
|
||||
var args = new List<string>();
|
||||
@@ -122,24 +174,24 @@ namespace Robust.Client.Console
|
||||
|
||||
var commandName = args[0];
|
||||
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
if (!AvailableCommands.TryGetValue(commandName, out var cmd))
|
||||
{
|
||||
if (!CanExecute(commandName))
|
||||
{
|
||||
WriteError(null, $"Insufficient perms for command: {commandName}");
|
||||
return;
|
||||
}
|
||||
|
||||
var command1 = AvailableCommands[commandName];
|
||||
args.RemoveAt(0);
|
||||
var shell = new ConsoleShell(this, null);
|
||||
var cmdArgs = args.ToArray();
|
||||
|
||||
AnyCommandExecuted?.Invoke(shell, commandName, command, cmdArgs);
|
||||
command1.Execute(shell, command, cmdArgs);
|
||||
}
|
||||
else
|
||||
WriteError(null, "Unknown command: " + commandName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanExecute(commandName))
|
||||
{
|
||||
WriteError(null, $"Insufficient perms for command: {commandName}");
|
||||
return;
|
||||
}
|
||||
|
||||
args.RemoveAt(0);
|
||||
var shell = new ConsoleShell(this, session ?? _player.LocalPlayer?.Session, session == null);
|
||||
var cmdArgs = args.ToArray();
|
||||
|
||||
AnyCommandExecuted?.Invoke(shell, commandName, command, cmdArgs);
|
||||
cmd.Execute(shell, command, cmdArgs);
|
||||
}
|
||||
|
||||
private bool CanExecute(string cmdName)
|
||||
@@ -166,7 +218,9 @@ namespace Robust.Client.Console
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine(ICommonSession? session, string text)
|
||||
{
|
||||
OutputText(text, true, false);
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(text);
|
||||
OutputText(msg, true, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -175,12 +229,12 @@ namespace Robust.Client.Console
|
||||
// We don't have anything to dispose.
|
||||
}
|
||||
|
||||
private void OutputText(string text, bool local, bool error)
|
||||
private void OutputText(FormattedMessage text, bool local, bool error)
|
||||
{
|
||||
AddString?.Invoke(this, new AddStringArgs(text, local, error));
|
||||
|
||||
var level = error ? LogLevel.Warning : LogLevel.Info;
|
||||
Logger.LogS(level, "CON", text);
|
||||
_conLogger.Log(level, text.ToString());
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
|
||||
@@ -190,7 +244,7 @@ namespace Robust.Client.Console
|
||||
|
||||
private void HandleConCmdAck(MsgConCmdAck msg)
|
||||
{
|
||||
OutputText("< " + msg.Text, false, msg.Error);
|
||||
OutputText(msg.Text, false, msg.Error);
|
||||
}
|
||||
|
||||
private void HandleConCmdReg(MsgConCmdReg msg)
|
||||
@@ -200,15 +254,17 @@ namespace Robust.Client.Console
|
||||
string? commandName = cmd.Name;
|
||||
|
||||
// Do not do duplicate commands.
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
if (_availableServerCommands.ContainsKey(commandName))
|
||||
{
|
||||
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
|
||||
_logger.Error($"Server sent duplicate console command {commandName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
|
||||
AvailableCommands[commandName] = command;
|
||||
_availableServerCommands[commandName] = command;
|
||||
}
|
||||
|
||||
UpdateAvailableCommands();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -228,6 +284,12 @@ namespace Robust.Client.Console
|
||||
_requestedCommands = true;
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_logger = _logMan.GetSawmill("console");
|
||||
_conLogger = _logMan.GetSawmill("CON");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These dummies are made purely so list and help can list server-side commands.
|
||||
/// </summary>
|
||||
@@ -256,13 +318,14 @@ namespace Robust.Client.Console
|
||||
public async ValueTask<CompletionResult> GetCompletionAsync(
|
||||
IConsoleShell shell,
|
||||
string[] args,
|
||||
string argStr,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var host = (ClientConsoleHost)shell.ConsoleHost;
|
||||
var argsList = args.ToList();
|
||||
argsList.Insert(0, Command);
|
||||
|
||||
return await host.DoServerCompletions(argsList, cancel);
|
||||
return await host.DoServerCompletions(argsList, argStr, cancel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,16 +337,17 @@ namespace Robust.Client.Console
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
shell.RemoteExecuteCommand(argStr["> ".Length..]);
|
||||
shell.RemoteExecuteCommand(argStr[">".Length..]);
|
||||
}
|
||||
|
||||
public override async ValueTask<CompletionResult> GetCompletionAsync(
|
||||
IConsoleShell shell,
|
||||
string[] args,
|
||||
string argStr,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var host = (ClientConsoleHost)shell.ConsoleHost;
|
||||
return await host.DoServerCompletions(args.ToList(), cancel);
|
||||
return await host.DoServerCompletions(args.ToList(), argStr[">".Length..], cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = EntityUid.Parse(args[0]);
|
||||
var netEntity = NetEntity.Parse(args[0]);
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
var componentName = args[1];
|
||||
|
||||
var component = (Component) _componentFactory.GetComponent(componentName);
|
||||
@@ -49,7 +50,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var entityUid = EntityUid.Parse(args[0]);
|
||||
var netEntity = NetEntity.Parse(args[0]);
|
||||
var entityUid = _entityManager.GetEntity(netEntity);
|
||||
var componentName = args[1];
|
||||
|
||||
var registration = _componentFactory.GetRegistration(componentName);
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class SaveConfig : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
|
||||
public override string Command => "saveconfig";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_cfg.SaveToFile();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -78,14 +78,7 @@ namespace Robust.Client.Console.Commands
|
||||
message.Append($"net ID: {registration.NetID}");
|
||||
}
|
||||
|
||||
message.Append($", References:");
|
||||
|
||||
shell.WriteLine(message.ToString());
|
||||
|
||||
foreach (var type in registration.References)
|
||||
{
|
||||
shell.WriteLine($" {type}");
|
||||
}
|
||||
}
|
||||
catch (UnknownComponentException)
|
||||
{
|
||||
@@ -191,6 +184,19 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowRotationsCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
|
||||
|
||||
public override string Command => "showrot";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = _entitySystems.GetEntitySystem<DebugDrawingSystem>();
|
||||
mgr.DebugRotations = !mgr.DebugRotations;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowRayCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
|
||||
@@ -283,6 +289,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class SnapGridGetCell : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public override string Command => "sggcell";
|
||||
@@ -297,7 +304,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
string indices = args[1];
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
if (!NetEntity.TryParse(args[0], out var gridNet))
|
||||
{
|
||||
shell.WriteError($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
@@ -309,7 +316,7 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (_map.TryGetGrid(gridUid, out var grid))
|
||||
if (_map.TryGetGrid(_entManager.GetEntity(gridNet), out var grid))
|
||||
{
|
||||
foreach (var entity in grid.GetAnchoredEntities(new Vector2i(
|
||||
int.Parse(indices.Split(',')[0], CultureInfo.InvariantCulture),
|
||||
@@ -417,6 +424,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class GridTileCount : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
public override string Command => "gridtc";
|
||||
@@ -429,7 +437,8 @@ namespace Robust.Client.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var gridUid))
|
||||
if (!NetEntity.TryParse(args[0], out var gridUidNet) ||
|
||||
!_entManager.TryGetEntity(gridUidNet, out var gridUid))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid entity UID.");
|
||||
return;
|
||||
@@ -455,7 +464,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
using var writer = _res.UserData.OpenWriteText(new ResourcePath("/guidump.txt"));
|
||||
using var writer = _res.UserData.OpenWriteText(new ResPath("/guidump.txt"));
|
||||
|
||||
foreach (var root in _ui.AllRoots)
|
||||
{
|
||||
@@ -608,6 +617,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class ChunkInfoCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
[Dependency] private readonly IInputManager _input = default!;
|
||||
@@ -616,18 +626,19 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mousePos = _eye.ScreenToMap(_input.MouseScreenPosition);
|
||||
var mousePos = _eye.PixelToMap(_input.MouseScreenPosition);
|
||||
|
||||
if (!_map.TryFindGridAt(mousePos, out var grid))
|
||||
if (!_map.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||
{
|
||||
shell.WriteLine("No grid under your mouse cursor.");
|
||||
return;
|
||||
}
|
||||
|
||||
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
|
||||
var chunk = grid.GetOrAddChunk(chunkIndex);
|
||||
var mapSystem = _entManager.System<SharedMapSystem>();
|
||||
var chunkIndex = mapSystem.LocalToChunkIndices(gridUid, grid, grid.MapToGrid(mousePos));
|
||||
var chunk = mapSystem.GetOrAddChunk(gridUid, grid, chunkIndex);
|
||||
|
||||
shell.WriteLine($"worldBounds: {grid.CalcWorldAABB(chunk)} localBounds: {chunk.CachedBounds}");
|
||||
shell.WriteLine($"worldBounds: {mapSystem.CalcWorldAABB(gridUid, grid, chunk)} localBounds: {chunk.CachedBounds}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,7 +672,7 @@ namespace Robust.Client.Console.Commands
|
||||
? StringComparer.Ordinal
|
||||
: StringComparer.OrdinalIgnoreCase;
|
||||
|
||||
var reversePathResolution = new ConcurrentDictionary<string, HashSet<ResourcePath>>(stringComparer);
|
||||
var reversePathResolution = new ConcurrentDictionary<string, HashSet<ResPath>>(stringComparer);
|
||||
|
||||
var taskManager = _taskManager;
|
||||
|
||||
@@ -675,7 +686,7 @@ namespace Robust.Client.Console.Commands
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
reversePathResolution.GetOrAdd(fullPath, _ => new HashSet<ResourcePath>()).Add(path);
|
||||
reversePathResolution.GetOrAdd(fullPath, _ => new HashSet<ResPath>()).Add(path);
|
||||
|
||||
var dir = Path.GetDirectoryName(fullPath)!;
|
||||
var fileName = Path.GetFileName(fullPath);
|
||||
@@ -689,7 +700,7 @@ namespace Robust.Client.Console.Commands
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
reversePathResolution.GetOrAdd(incFullPath, _ => new HashSet<ResourcePath>()).Add(path);
|
||||
reversePathResolution.GetOrAdd(incFullPath, _ => new HashSet<ResPath>()).Add(path);
|
||||
|
||||
var incDir = Path.GetDirectoryName(incFullPath)!;
|
||||
var incFileName = Path.GetFileName(incFullPath);
|
||||
@@ -842,5 +853,15 @@ namespace Robust.Client.Console.Commands
|
||||
shell.WriteLine($"name: '{name}' ");
|
||||
}
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
return CompletionResult.FromOptions(Enum.GetNames<Keyboard.Key>());
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if !FULL_RELEASE
|
||||
#if TOOLS
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if !FULL_RELEASE
|
||||
#if TOOLS
|
||||
using System;
|
||||
using JetBrains.Profiler.Api;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
280
Robust.Client/Console/Commands/UITestCommand.TabSpriteView.cs
Normal file
280
Robust.Client/Console/Commands/UITestCommand.TabSpriteView.cs
Normal file
@@ -0,0 +1,280 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Console.Commands;
|
||||
|
||||
internal sealed partial class UITestControl
|
||||
{
|
||||
private sealed class TabSpriteView : Control
|
||||
{
|
||||
private const string EntityId = "debugRotation4";
|
||||
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly IGameTiming _timing;
|
||||
private readonly BoxContainer _box;
|
||||
|
||||
private record Entry(EntityUid Uid, SpriteComponent Sprite, TransformComponent Transform,
|
||||
SpriteView View, Action<Entry, float>? Update);
|
||||
|
||||
private List<Entry> _entries = new();
|
||||
|
||||
private float _degreesPerSecond = 45;
|
||||
|
||||
public TabSpriteView()
|
||||
{
|
||||
IoCManager.Resolve(ref _entMan, ref _timing);
|
||||
SetValue(TabContainer.TabTitleProperty, nameof(SpriteView));
|
||||
_box = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(10),
|
||||
};
|
||||
AddChild(new ScrollContainer() { Children = { _box }});
|
||||
|
||||
var level = IoCManager.Resolve<IBaseClient>().RunLevel;
|
||||
if (level != ClientRunLevel.InGame && level != ClientRunLevel.SinglePlayerGame)
|
||||
{
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "You need to be in-game for this UI to work."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "This control contains a bunch of sprite views. They are grouped based on SpriteView properties.\n" +
|
||||
"Each group has views with different rotation and sprite transformations applied.\n" +
|
||||
"Except for the fixed-size no-stretch view, the sprites should never leave the boxes.\n"
|
||||
});
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "=============================\n" +
|
||||
"Default SpriteView. Ignore sprite offset and world rotation\n" +
|
||||
"============================="
|
||||
});
|
||||
AddEntries();
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "============================\n" +
|
||||
"No overrides (Show entity rotation & offset)\n" +
|
||||
"============================="
|
||||
});
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
}
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "=============================\n" +
|
||||
"No override + Transformed view\n" +
|
||||
"============================="
|
||||
});
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
}
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "============================\n" +
|
||||
"No override + Transform + Fixed Size + No Stretch (sprites can leave).\n" +
|
||||
"============================"
|
||||
});
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = new(64, 64);
|
||||
e.View.Stretch = SpriteView.StretchMode.None;
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
}
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "============================\n" +
|
||||
"No override + Transform + Fixed Size + Shrink to view\n" +
|
||||
"============================"
|
||||
});
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = new(64, 64);
|
||||
e.View.Stretch = SpriteView.StretchMode.Fit;
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
}
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "============================\n" +
|
||||
"No override + Transform + Fixed Size + Scale to fill view\n" +
|
||||
"============================"
|
||||
});
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = new(300, 300);
|
||||
e.View.Stretch = SpriteView.StretchMode.Fill;
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
e.View.SpriteOffset = true;
|
||||
e.View.WorldRotation = null;
|
||||
}
|
||||
|
||||
_box.AddChild(new Label
|
||||
{
|
||||
Text = "============================\n" +
|
||||
"With override + Transform + Fixed Size + Scale to fill view\n" +
|
||||
"============================"
|
||||
});
|
||||
|
||||
foreach (var e in AddEntries())
|
||||
{
|
||||
e.View.SetSize = new(300, 300);
|
||||
e.View.Stretch = SpriteView.StretchMode.Fill;
|
||||
e.View.Scale = new(1, 0.75f);
|
||||
e.View.EyeRotation = Angle.FromDegrees(45);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClosed()
|
||||
{
|
||||
foreach (var e in _entries)
|
||||
{
|
||||
_entMan.DeleteEntity(e.Uid);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Entry> AddEntries()
|
||||
{
|
||||
var added = new List<Entry>();
|
||||
|
||||
var entry = AddEntry("Default", null);
|
||||
added.Add(entry);
|
||||
|
||||
entry = AddEntry("Local Rotation", (e, time) =>
|
||||
{
|
||||
e.Transform.LocalRotation = Angle.FromDegrees(time * _degreesPerSecond);
|
||||
e.View.InvalidateMeasure();
|
||||
});
|
||||
added.Add(entry);
|
||||
|
||||
entry = AddEntry("Local Rotation (NoRot)", (e, time) =>
|
||||
{
|
||||
e.Transform.LocalRotation = Angle.FromDegrees(time * _degreesPerSecond);
|
||||
e.View.InvalidateMeasure();
|
||||
});
|
||||
entry.Sprite.NoRotation = true;
|
||||
added.Add(entry);
|
||||
|
||||
entry = AddEntry("Offset", (e, time) =>
|
||||
{
|
||||
e.Sprite.Offset = new Vector2(MathF.Sin((float) Angle.FromDegrees(time * _degreesPerSecond)), 0);
|
||||
e.View.InvalidateMeasure();
|
||||
});
|
||||
added.Add(entry);
|
||||
|
||||
entry = AddEntry("Scaled", (e, time) =>
|
||||
{
|
||||
var theta = (float) Angle.FromDegrees(_degreesPerSecond * time).Theta;
|
||||
e.Sprite.Scale = Vector2.One + new Vector2(0.5f * MathF.Sin(theta), 0.5f * MathF.Cos(theta));
|
||||
e.View.InvalidateMeasure();
|
||||
});
|
||||
added.Add(entry);
|
||||
|
||||
entry = AddEntry("Sprite Rotation", (e, time) =>
|
||||
{
|
||||
e.Sprite.Rotation = Angle.FromDegrees(time * _degreesPerSecond);
|
||||
});
|
||||
added.Add(entry);
|
||||
|
||||
entry = AddEntry("Combination", (e, time) =>
|
||||
{
|
||||
var theta = (float) Angle.FromDegrees(_degreesPerSecond * time * 2).Theta;
|
||||
e.Sprite.Scale = Vector2.One + new Vector2(0.5f * MathF.Sin(theta), 0.5f * MathF.Cos(theta));
|
||||
e.Sprite.Offset = new(MathF.Sin((float) Angle.FromDegrees(time * _degreesPerSecond)), 0);
|
||||
e.Sprite.Rotation = Angle.FromDegrees(0.5 * time * _degreesPerSecond);
|
||||
e.Transform.LocalRotation = Angle.FromDegrees(0.25 * time * _degreesPerSecond);
|
||||
e.View.InvalidateMeasure();
|
||||
});
|
||||
added.Add(entry);
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
var time = (float) _timing.CurTime.TotalSeconds;
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
entry.Update?.Invoke(entry, time);
|
||||
}
|
||||
}
|
||||
|
||||
private Entry AddEntry(string text, Action<Entry, float>? onUpdate)
|
||||
{
|
||||
var label = new Label
|
||||
{
|
||||
MinWidth = 200,
|
||||
Text = text,
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
var ent = _entMan.SpawnEntity(EntityId, MapCoordinates.Nullspace);
|
||||
var view = new SpriteView();
|
||||
view.SetEntity(ent);
|
||||
|
||||
var viewBox = new PanelContainer()
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Children = { view },
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = Color.White,
|
||||
BorderColor = Color.Black,
|
||||
BorderThickness = new(4),
|
||||
}
|
||||
};
|
||||
|
||||
_box.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(10),
|
||||
MinHeight = 150,
|
||||
Children = { label, viewBox},
|
||||
Name = text,
|
||||
});
|
||||
|
||||
var sprite = _entMan.GetComponent<SpriteComponent>(ent);
|
||||
var xform = _entMan.GetComponent<TransformComponent>(ent);
|
||||
var entry = new Entry(ent, sprite, xform, view, onUpdate);
|
||||
_entries.Add(entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Console.Commands;
|
||||
|
||||
internal sealed class UITestControl : Control
|
||||
internal sealed partial class UITestControl : Control
|
||||
{
|
||||
private const string Lipsum = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer sed interdum diam. Duis erat risus, tincidunt at pulvinar non, accumsan non dui. Morbi feugiat nisi in odio consectetur, ac suscipit nulla mollis. Nulla consequat neque sit amet neque venenatis feugiat. Proin placerat eget mauris sit amet tincidunt. Sed pulvinar purus sed ex varius, et lobortis risus efficitur. Integer blandit eu neque quis elementum. Vivamus lacinia sem non lacinia eleifend. Integer sit amet est ac risus tempus iaculis sed quis leo. Proin eu dui tincidunt orci ornare elementum. Curabitur molestie enim scelerisque, porttitor ipsum vitae, posuere libero. Donec finibus placerat accumsan. Nam et arcu lacus.
|
||||
|
||||
@@ -26,6 +26,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
|
||||
|
||||
private readonly TabContainer _tabContainer;
|
||||
private readonly TabSpriteView _sprite;
|
||||
|
||||
public UITestControl()
|
||||
{
|
||||
@@ -88,7 +89,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
{
|
||||
grid.AddChild(new Button
|
||||
{
|
||||
MinSize = (50, 50),
|
||||
MinSize = new(50, 50),
|
||||
Text = $"{x}, {y}"
|
||||
});
|
||||
}
|
||||
@@ -150,6 +151,14 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
|
||||
_tabContainer.AddChild(TabTextEdit());
|
||||
_tabContainer.AddChild(TabRichText());
|
||||
|
||||
_sprite = new TabSpriteView();
|
||||
_tabContainer.AddChild(_sprite);
|
||||
}
|
||||
|
||||
public void OnClosed()
|
||||
{
|
||||
_sprite.OnClosed();
|
||||
}
|
||||
|
||||
private Control TabTextEdit()
|
||||
@@ -216,6 +225,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
Untitled4 = 5,
|
||||
TextEdit = 6,
|
||||
RichText = 7,
|
||||
SpriteView = 8,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,8 +235,10 @@ internal sealed class UITestCommand : LocalizedCommands
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var window = new DefaultWindow { MinSize = (500, 400) };
|
||||
window.Contents.AddChild(new UITestControl());
|
||||
var window = new DefaultWindow { MinSize = new(800, 600) };
|
||||
var control = new UITestControl();
|
||||
window.OnClose += control.OnClosed;
|
||||
window.Contents.AddChild(control);
|
||||
|
||||
window.OpenCentered();
|
||||
}
|
||||
@@ -267,7 +279,7 @@ internal sealed class UITest2Command : LocalizedCommands
|
||||
|
||||
var root = _uiMgr.CreateWindowRoot(window);
|
||||
window.DisposeOnClose = true;
|
||||
|
||||
window.RequestClosed += _ => control.OnClosed();
|
||||
root.AddChild(control);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Input;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user