Compare commits
1357 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4e2f5aefc | ||
|
|
8ceb40a24e | ||
|
|
3e53d67412 | ||
|
|
844f51d916 | ||
|
|
26974ffd4c | ||
|
|
91f5b4a675 | ||
|
|
d179240139 | ||
|
|
67b6a7e198 | ||
|
|
3b721242c1 | ||
|
|
c6ceaa5c87 | ||
|
|
af9629c53f | ||
|
|
9509909f46 | ||
|
|
88efbd433f | ||
|
|
f716928683 | ||
|
|
e23d024ba3 | ||
|
|
591419611e | ||
|
|
f68beab1df | ||
|
|
2798610afd | ||
|
|
c0a96e3811 | ||
|
|
9d0d76f0a5 | ||
|
|
c7173ac832 | ||
|
|
b12ce47e7e | ||
|
|
269272983b | ||
|
|
6adc5eb9fb | ||
|
|
fd8bc8ae3f | ||
|
|
3369b0d429 | ||
|
|
097d39e8ce | ||
|
|
11fa08e6d6 | ||
|
|
96987af71d | ||
|
|
23327cb3ef | ||
|
|
b43b45be8f | ||
|
|
41994ec82e | ||
|
|
cd14ea7c62 | ||
|
|
9f13bca6b8 | ||
|
|
f4252c3e97 | ||
|
|
6598867d3d | ||
|
|
f39e040a0d | ||
|
|
a3d140269b | ||
|
|
feac29ea40 | ||
|
|
834c8cdbca | ||
|
|
d441fbdf91 | ||
|
|
e897f524dc | ||
|
|
1caf9446d8 | ||
|
|
bfd2c885db | ||
|
|
f467fa4e59 | ||
|
|
41fca4a2bb | ||
|
|
d38f955144 | ||
|
|
010e50a25d | ||
|
|
eab7c607cd | ||
|
|
3cafbf7f1a | ||
|
|
d7277b5a5b | ||
|
|
bb8dd1402d | ||
|
|
539cd0d418 | ||
|
|
0f8e1625d5 | ||
|
|
840e9bedb2 | ||
|
|
e11ad476fc | ||
|
|
c43726bc75 | ||
|
|
c9ab933a23 | ||
|
|
066a0b1668 | ||
|
|
aa68fe412e | ||
|
|
d748ea7e40 | ||
|
|
a7a282078f | ||
|
|
2fad3b588a | ||
|
|
4cb5d4a9c5 | ||
|
|
5b22ef192e | ||
|
|
34024d8524 | ||
|
|
ecc31dea15 | ||
|
|
d0e48d9bdc | ||
|
|
f6fc5599c0 | ||
|
|
89b6666436 | ||
|
|
94d45a2262 | ||
|
|
dc17a185ca | ||
|
|
4915477823 | ||
|
|
8662d2227c | ||
|
|
dd01a9bef3 | ||
|
|
6e320b1fd5 | ||
|
|
6e40993199 | ||
|
|
9d48af2b50 | ||
|
|
28d48a76e3 | ||
|
|
80eb14f9da | ||
|
|
623fa06d52 | ||
|
|
7154113c13 | ||
|
|
0a932ff880 | ||
|
|
7ef4a1ff12 | ||
|
|
ccc52b69d2 | ||
|
|
8139fdc738 | ||
|
|
a4fd53d516 | ||
|
|
27357110b5 | ||
|
|
fde78738b6 | ||
|
|
ce530e795a | ||
|
|
658d211779 | ||
|
|
66c1cd0d93 | ||
|
|
55ac231cec | ||
|
|
67674d53a2 | ||
|
|
8646b2dfce | ||
|
|
a2f68c2e3c | ||
|
|
5b1a090c5e | ||
|
|
e9efa238b0 | ||
|
|
0dabd8a1f6 | ||
|
|
b7a1f82f56 | ||
|
|
2507e096b7 | ||
|
|
b303b0a99c | ||
|
|
342fb57d14 | ||
|
|
a9ca77e27f | ||
|
|
6542304340 | ||
|
|
6b0f7f94b6 | ||
|
|
d87c76a23e | ||
|
|
305423b856 | ||
|
|
4b62190aff | ||
|
|
e17771a6a5 | ||
|
|
bac30ae78b | ||
|
|
c0219922b2 | ||
|
|
b5ed952d5c | ||
|
|
7994bc4909 | ||
|
|
b88d34c639 | ||
|
|
96cb1ee3fd | ||
|
|
003abe86bb | ||
|
|
6ec411a618 | ||
|
|
c7c0903804 | ||
|
|
cdc972a5ce | ||
|
|
17de912608 | ||
|
|
9876508887 | ||
|
|
72d62220e8 | ||
|
|
46ad337fa5 | ||
|
|
7a3ba7721f | ||
|
|
e0198e626b | ||
|
|
142705f386 | ||
|
|
4cb49c7825 | ||
|
|
05adb82dda | ||
|
|
faf7671799 | ||
|
|
d64db329dd | ||
|
|
e34d2339ad | ||
|
|
d196c5091c | ||
|
|
3e83f92532 | ||
|
|
41d7aaf323 | ||
|
|
e110f3726a | ||
|
|
eb5c897ac0 | ||
|
|
e864cf0c7d | ||
|
|
565c12405e | ||
|
|
2a6a8b42a6 | ||
|
|
5cb4cc0d1d | ||
|
|
62eb4f026f | ||
|
|
411d1a89c1 | ||
|
|
6ae0bf366a | ||
|
|
178cdc9dfa | ||
|
|
5bf1f86886 | ||
|
|
e482aba030 | ||
|
|
535e4d42bb | ||
|
|
54398d4c4b | ||
|
|
0987b44e23 | ||
|
|
58098e96d4 | ||
|
|
f649c180cf | ||
|
|
966478b131 | ||
|
|
ca0759a011 | ||
|
|
f1cdf2fe79 | ||
|
|
04fa5e2719 | ||
|
|
eccb95690c | ||
|
|
acf7456371 | ||
|
|
8ea41480b7 | ||
|
|
0d1bc0d7fe | ||
|
|
f9b7774286 | ||
|
|
2e672badf2 | ||
|
|
cd5d6fd15c | ||
|
|
cf76982cfa | ||
|
|
2624ea7d2a | ||
|
|
2e7c4b6dff | ||
|
|
e5dbee1073 | ||
|
|
c06463b61d | ||
|
|
10341e3776 | ||
|
|
824cdc5dcd | ||
|
|
7aa7a35aa5 | ||
|
|
79b57b3359 | ||
|
|
c7aeffe586 | ||
|
|
79c02f2457 | ||
|
|
b9ed68fa9e | ||
|
|
6c6d6c9c8d | ||
|
|
e169d7be68 | ||
|
|
192fd7d4dd | ||
|
|
1f1193e857 | ||
|
|
0a412ade88 | ||
|
|
3cef2fe9aa | ||
|
|
18e53f06fe | ||
|
|
eaac539087 | ||
|
|
57005c4fd5 | ||
|
|
ea58999a2a | ||
|
|
a5a69fd9fc | ||
|
|
f1d0f1c1c9 | ||
|
|
83492d7495 | ||
|
|
10abc2254a | ||
|
|
5d5536a95e | ||
|
|
fe81e55925 | ||
|
|
654690d73e | ||
|
|
256aba4bc1 | ||
|
|
49f8845105 | ||
|
|
12370846b4 | ||
|
|
07637dfe96 | ||
|
|
f389a4a1db | ||
|
|
af077c796c | ||
|
|
1c319f2fa8 | ||
|
|
e8afa03f7b | ||
|
|
9b22545842 | ||
|
|
adc733d25c | ||
|
|
6896257647 | ||
|
|
1d9845a6cb | ||
|
|
8461339e9a | ||
|
|
9904d94e4c | ||
|
|
da25c80d0b | ||
|
|
5fa63733fd | ||
|
|
8b54bf0ba1 | ||
|
|
173dc12cb3 | ||
|
|
e136529ff0 | ||
|
|
75542dacb3 | ||
|
|
07abfbde43 | ||
|
|
96112fe118 | ||
|
|
9d0b313261 | ||
|
|
277ddfa9be | ||
|
|
5d88bdb256 | ||
|
|
8d29b43155 | ||
|
|
cc071a6415 | ||
|
|
8954def25c | ||
|
|
d484098781 | ||
|
|
9f06cbf3a0 | ||
|
|
af4a3ca0af | ||
|
|
43715143e4 | ||
|
|
16aa354d36 | ||
|
|
c739025453 | ||
|
|
f0526d17de | ||
|
|
6cc2e919c0 | ||
|
|
e1f9eaefa9 | ||
|
|
5d62bf9b60 | ||
|
|
4aa5814a0a | ||
|
|
5bca244062 | ||
|
|
7cf57714a4 | ||
|
|
92e6f862ad | ||
|
|
2377f41036 | ||
|
|
26a08f4d9f | ||
|
|
672d207961 | ||
|
|
a3d8e50f26 | ||
|
|
d3abca46a8 | ||
|
|
17fdd18c55 | ||
|
|
e482d038c6 | ||
|
|
d0482a004e | ||
|
|
561a3e3256 | ||
|
|
158dac5e1c | ||
|
|
1413f8c0b6 | ||
|
|
ffb2aa332a | ||
|
|
45e8208e9c | ||
|
|
7f230a8bf4 | ||
|
|
afe26b8c16 | ||
|
|
d153f62b8a | ||
|
|
5c80cd6e50 | ||
|
|
df1aced95d | ||
|
|
5a0a8f5077 | ||
|
|
1c281539e0 | ||
|
|
ef41d14f50 | ||
|
|
aa29397732 | ||
|
|
ca8865a3ce | ||
|
|
7599ef6626 | ||
|
|
43e71f9242 | ||
|
|
aac9b25efe | ||
|
|
532d170585 | ||
|
|
3dcb14013b | ||
|
|
a4c782cd35 | ||
|
|
0779baa0ac | ||
|
|
0849fab644 | ||
|
|
d85bca2e7e | ||
|
|
c7ab6c435c | ||
|
|
98db3b4f08 | ||
|
|
0e7ba839ed | ||
|
|
2c27691e57 | ||
|
|
934506bdfd | ||
|
|
f313fa4ae1 | ||
|
|
78755b6130 | ||
|
|
8bf59e5cbb | ||
|
|
8f71d13714 | ||
|
|
f4a790eb50 | ||
|
|
0d951c8ad1 | ||
|
|
59ef5f05ea | ||
|
|
b84aab0960 | ||
|
|
0e89b91c38 | ||
|
|
7888a704e1 | ||
|
|
804fd3de8e | ||
|
|
4992c36f08 | ||
|
|
27af0c841b | ||
|
|
cc4f4df4f9 | ||
|
|
5989ab3b85 | ||
|
|
040b9c1452 | ||
|
|
bb7bd2e27e | ||
|
|
02dbd4ba75 | ||
|
|
3b149cc875 | ||
|
|
f3745c68d2 | ||
|
|
eddbe7d0cf | ||
|
|
faf86d006a | ||
|
|
71e4dfeb14 | ||
|
|
da8ed6ddf9 | ||
|
|
503e687c55 | ||
|
|
dd667cce0b | ||
|
|
f911009752 | ||
|
|
cae4c46669 | ||
|
|
a937fc38db | ||
|
|
ff43ffce07 | ||
|
|
5bb3dc258a | ||
|
|
98238e4516 | ||
|
|
23b8308282 | ||
|
|
a1f7cdc2b6 | ||
|
|
dcb30351c5 | ||
|
|
1c0dc60071 | ||
|
|
0401c3ac0e | ||
|
|
3b800d1cd7 | ||
|
|
35efb464e5 | ||
|
|
19a955a327 | ||
|
|
836b18e68a | ||
|
|
046dc0d598 | ||
|
|
039d92ce78 | ||
|
|
2136da15d6 | ||
|
|
e5041d80ef | ||
|
|
f66860bdfe | ||
|
|
50f0a2bd2e | ||
|
|
7cd9bd383e | ||
|
|
45a20a1633 | ||
|
|
ec5b3f80ef | ||
|
|
3b257ea87a | ||
|
|
6b82cead9c | ||
|
|
79ae4c52cd | ||
|
|
a4616cda7d | ||
|
|
95648361be | ||
|
|
0c41e9c10b | ||
|
|
61c7ef3cb0 | ||
|
|
65898125d0 | ||
|
|
da05078ff3 | ||
|
|
badb5c3636 | ||
|
|
9453588ab1 | ||
|
|
4cbb3874a4 | ||
|
|
940aff7ef4 | ||
|
|
a3edb93273 | ||
|
|
782b054b1a | ||
|
|
fc27b2911e | ||
|
|
486b067567 | ||
|
|
bdd1afea57 | ||
|
|
5ccd02939b | ||
|
|
ca5b8ab309 | ||
|
|
9481fdd38d | ||
|
|
56bf6d282b | ||
|
|
5364c9c1bc | ||
|
|
a4d4beeb97 | ||
|
|
30a01381be | ||
|
|
def99a9e4d | ||
|
|
1214fc8b0d | ||
|
|
5a5b1268d3 | ||
|
|
6a1fffd1ce | ||
|
|
571ef3060a | ||
|
|
3cf82e112f | ||
|
|
9b459c20d6 | ||
|
|
5c85cd27c3 | ||
|
|
4bf73c7a8a | ||
|
|
62e51b7535 | ||
|
|
729a7f0053 | ||
|
|
03c297190d | ||
|
|
9c790eddd7 | ||
|
|
8ebe7e332b | ||
|
|
f43777e37e | ||
|
|
691a8fcdeb | ||
|
|
69c0e8d70e | ||
|
|
330c92c9a8 | ||
|
|
cf0ff60d31 | ||
|
|
9e9cf5dd1f | ||
|
|
5768d8e4a4 | ||
|
|
3b433e53be | ||
|
|
28ddf40344 | ||
|
|
c1286db9c1 | ||
|
|
f13940262e | ||
|
|
9f0a6101b8 | ||
|
|
0b0fe01a7c | ||
|
|
85e098d5c8 | ||
|
|
e8d1faf4e2 | ||
|
|
2a46df1110 | ||
|
|
1601e6d26e | ||
|
|
0e4f6dea2b | ||
|
|
a2356773dc | ||
|
|
4a4da4737d | ||
|
|
2cefd1bf2e | ||
|
|
c5ce15fe34 | ||
|
|
76dea568c9 | ||
|
|
8e81140eff | ||
|
|
d58e1f0792 | ||
|
|
830c242751 | ||
|
|
91d45afd0f | ||
|
|
102c77b23e | ||
|
|
aa4039a2e7 | ||
|
|
c065ded440 | ||
|
|
f69096dadb | ||
|
|
363f95ba14 | ||
|
|
dcc15e8911 | ||
|
|
bd13047b58 | ||
|
|
1a83789c01 | ||
|
|
1cbe272e19 | ||
|
|
6de74b41b3 | ||
|
|
b0fc9ee507 | ||
|
|
a95ffe1d31 | ||
|
|
4119a510f5 | ||
|
|
cfa7f03815 | ||
|
|
9b4c89820d | ||
|
|
fe874720aa | ||
|
|
f7c0f33eb5 | ||
|
|
c1bf9d9dfc | ||
|
|
255bf439d3 | ||
|
|
2a31937b81 | ||
|
|
eb8964e1d1 | ||
|
|
fe0ca280a1 | ||
|
|
e8e3b7b985 | ||
|
|
c437507442 | ||
|
|
ca151b434e | ||
|
|
10ba74c21e | ||
|
|
6191b6371d | ||
|
|
5be4175ac3 | ||
|
|
f61f3671a6 | ||
|
|
477e0ada32 | ||
|
|
a884f693ae | ||
|
|
3107b1b21b | ||
|
|
04529de7b3 | ||
|
|
199a39208c | ||
|
|
0187da5f24 | ||
|
|
2e1c37ffff | ||
|
|
4b0189dd55 | ||
|
|
d4a39674de | ||
|
|
07c6fd3711 | ||
|
|
cf5b72aa9a | ||
|
|
cdfbebd637 | ||
|
|
e86e3316a9 | ||
|
|
bb2dcbaea0 | ||
|
|
fefc7a69cd | ||
|
|
1b24cd36e0 | ||
|
|
6322c1f46d | ||
|
|
00a29bb6be | ||
|
|
3a029caf2b | ||
|
|
57263905e7 | ||
|
|
4f46012c11 | ||
|
|
7ea9a1e3e8 | ||
|
|
c7510e12c4 | ||
|
|
89b5d9294c | ||
|
|
339e1060b7 | ||
|
|
52e1dbd846 | ||
|
|
b4963f4ff7 | ||
|
|
838e8f6bde | ||
|
|
8f21514855 | ||
|
|
44041f33e3 | ||
|
|
5d9bc4473c | ||
|
|
a3e0d42e86 | ||
|
|
4a9c1d0d1b | ||
|
|
ff47f79c62 | ||
|
|
f46a391873 | ||
|
|
99b8ada42b | ||
|
|
f2227aa7cb | ||
|
|
a9f6c20113 | ||
|
|
e261e7a2c7 | ||
|
|
ae811aafd0 | ||
|
|
cc593b465d | ||
|
|
f81e8339e3 | ||
|
|
cdc5513726 | ||
|
|
a398338c02 | ||
|
|
26e2e81188 | ||
|
|
46d7bba87d | ||
|
|
57d5610a58 | ||
|
|
1968e1fdb7 | ||
|
|
f2584cf807 | ||
|
|
dd592d1d6d | ||
|
|
cfff08cdfd | ||
|
|
e83437c193 | ||
|
|
1d900a6a3c | ||
|
|
859c7bea8a | ||
|
|
1b9e90e786 | ||
|
|
41396367ac | ||
|
|
16c9a23d55 | ||
|
|
ce338a2a72 | ||
|
|
1f7f2b22e7 | ||
|
|
47c7f63d75 | ||
|
|
8b1e85fbb4 | ||
|
|
ff4e9fb027 | ||
|
|
d15ef40988 | ||
|
|
eadfb18f74 | ||
|
|
4b89bb087a | ||
|
|
f0a602b48b | ||
|
|
fe10da9a6c | ||
|
|
948303aac8 | ||
|
|
12df80da56 | ||
|
|
a48c238ed7 | ||
|
|
da6c7b4d7a | ||
|
|
15cbbacc2f | ||
|
|
c3f6a17bae | ||
|
|
0fbeb70865 | ||
|
|
02f98a4053 | ||
|
|
d2e2a13479 | ||
|
|
f32cb592e2 | ||
|
|
72342f3118 | ||
|
|
246fd5f6c4 | ||
|
|
715022def5 | ||
|
|
78b2bafde1 | ||
|
|
2d7ebac8e6 | ||
|
|
bcffd2eb99 | ||
|
|
459bf62fab | ||
|
|
bdaa671823 | ||
|
|
463bf9b116 | ||
|
|
0c7a94c062 | ||
|
|
af866f0665 | ||
|
|
2493c70241 | ||
|
|
3c3359999c | ||
|
|
2467004dc2 | ||
|
|
3a9f438390 | ||
|
|
3126de2c37 | ||
|
|
b7d026b98e | ||
|
|
3f8b178f88 | ||
|
|
6184f4ce4f | ||
|
|
fa736697a9 | ||
|
|
2c88c700ca | ||
|
|
0b2e9ce1ad | ||
|
|
ccafdf250e | ||
|
|
57b28faa74 | ||
|
|
d0d7c58af1 | ||
|
|
339fc979f5 | ||
|
|
47614c3102 | ||
|
|
94e1501329 | ||
|
|
dfb015acc2 | ||
|
|
f1daafbf9e | ||
|
|
432e10c205 | ||
|
|
62b626a931 | ||
|
|
9a2893c6bc | ||
|
|
3a538f98ed | ||
|
|
7c71708de7 | ||
|
|
b64d97e808 | ||
|
|
428a19789d | ||
|
|
7cc4055d94 | ||
|
|
114c93fa46 | ||
|
|
07801a0283 | ||
|
|
30c8ea583b | ||
|
|
d54a5f647e | ||
|
|
457ed05174 | ||
|
|
44c94da2d8 | ||
|
|
ec614e1912 | ||
|
|
559b509a03 | ||
|
|
e2e9209655 | ||
|
|
e55b72f73f | ||
|
|
85e30cc968 | ||
|
|
51dcfbab75 | ||
|
|
7c0aa45057 | ||
|
|
a5fdc2a9cc | ||
|
|
64fd2f9cf8 | ||
|
|
a77495c562 | ||
|
|
3880708e1d | ||
|
|
cc68d57f11 | ||
|
|
56f5a61362 | ||
|
|
40540c3637 | ||
|
|
82678235ab | ||
|
|
5123d21290 | ||
|
|
b0dcf515f0 | ||
|
|
9aafccc946 | ||
|
|
ccf98d7283 | ||
|
|
1b35c98be5 | ||
|
|
db23c3d35a | ||
|
|
1c6c1298d5 | ||
|
|
b9ebed14a5 | ||
|
|
7077f0f1f3 | ||
|
|
d1887c6cd3 | ||
|
|
9278c81611 | ||
|
|
dec9ec32df | ||
|
|
1967f738a8 | ||
|
|
1031600c63 | ||
|
|
24c697bea7 | ||
|
|
83665018d4 | ||
|
|
b093626a21 | ||
|
|
f3dc78b7cd | ||
|
|
f0cd4333c3 | ||
|
|
6d38a785ed | ||
|
|
e46f9fd94f | ||
|
|
6b620ddcef | ||
|
|
e5684ad947 | ||
|
|
dfabc07044 | ||
|
|
dca88e8272 | ||
|
|
9f947ae2c5 | ||
|
|
28156bcaf2 | ||
|
|
a14aacbcf9 | ||
|
|
321e026e43 | ||
|
|
30351f7b9d | ||
|
|
2393a1a114 | ||
|
|
479476561e | ||
|
|
a02f485040 | ||
|
|
2957e8ad73 | ||
|
|
f83abe15cb | ||
|
|
709cba45ce | ||
|
|
8a9a72fd50 | ||
|
|
13cb0a27a0 | ||
|
|
fa817e3cdd | ||
|
|
3bd8549f41 | ||
|
|
4e47655b17 | ||
|
|
18533d5944 | ||
|
|
c5839c94ca | ||
|
|
ffc146ff9b | ||
|
|
7b82e3d641 | ||
|
|
ab6cedb659 | ||
|
|
e60be94be0 | ||
|
|
f830b024d5 | ||
|
|
98519da7d9 | ||
|
|
cb3211cf46 | ||
|
|
c051153a1f | ||
|
|
956d7e0918 | ||
|
|
530a7aa834 | ||
|
|
052ca52cc3 | ||
|
|
2025b8a494 | ||
|
|
238233ca81 | ||
|
|
938c8259c3 | ||
|
|
35df5f25c7 | ||
|
|
cd84fb9fc1 | ||
|
|
8bcf12e317 | ||
|
|
10ed353e0a | ||
|
|
72d88200e4 | ||
|
|
6ef832b3a9 | ||
|
|
17d91563d5 | ||
|
|
fb65693627 | ||
|
|
079fc738ad | ||
|
|
ae0f3b0b02 | ||
|
|
b2c515f2e6 | ||
|
|
6f1469a551 | ||
|
|
1d441b86d2 | ||
|
|
b72e6e54e6 | ||
|
|
1ee679395d | ||
|
|
cf62ee5ccb | ||
|
|
995685adfe | ||
|
|
f0eb955628 | ||
|
|
923f661428 | ||
|
|
b562d11994 | ||
|
|
3554112f31 | ||
|
|
7cada2608f | ||
|
|
21d4acebc3 | ||
|
|
73bbaaf0af | ||
|
|
40105515d2 | ||
|
|
79f09deecb | ||
|
|
c4a7f8675f | ||
|
|
f9a5fee364 | ||
|
|
bdd9c58cae | ||
|
|
09368e87cf | ||
|
|
46858c45b8 | ||
|
|
fe1a96f850 | ||
|
|
581449d992 | ||
|
|
15e848d4bf | ||
|
|
0dbbf44ab2 | ||
|
|
c62a5d15ad | ||
|
|
53bd02a890 | ||
|
|
099aa26ef8 | ||
|
|
2598ea3577 | ||
|
|
7be5b36494 | ||
|
|
3dff4d1061 | ||
|
|
5b7037550d | ||
|
|
b94f21402c | ||
|
|
9f5875d4bb | ||
|
|
694b6101ca | ||
|
|
1737da9c1f | ||
|
|
bcf4fea598 | ||
|
|
90e41f3ce9 | ||
|
|
5f5fb51eae | ||
|
|
95f06d56d8 | ||
|
|
1c6788ea45 | ||
|
|
457a8a5cf8 | ||
|
|
282caf10a5 | ||
|
|
b2fc6c165b | ||
|
|
0a1fa9588f | ||
|
|
5f5bfadf2b | ||
|
|
b512e707a5 | ||
|
|
3678ddfa36 | ||
|
|
ab8aa89f2a | ||
|
|
fd22482f06 | ||
|
|
e741f7b557 | ||
|
|
a70b7ee150 | ||
|
|
87227c96ed | ||
|
|
91d12aeb4f | ||
|
|
b3f132201b | ||
|
|
24b9f552eb | ||
|
|
b4a8dec68c | ||
|
|
c140823bea | ||
|
|
f166cc93cd | ||
|
|
125c9767f1 | ||
|
|
7b7c978db6 | ||
|
|
26fc097672 | ||
|
|
0f2e2406e8 | ||
|
|
061a096600 | ||
|
|
07fe963bed | ||
|
|
7a1eda98ca | ||
|
|
e8a5e44319 | ||
|
|
486319407a | ||
|
|
4f3d917c9b | ||
|
|
e2761965d5 | ||
|
|
171ab196c8 | ||
|
|
5630cea9a0 | ||
|
|
5a4189a770 | ||
|
|
2440d9fc48 | ||
|
|
bd33aebc3d | ||
|
|
a46d467b75 | ||
|
|
8c6981da0d | ||
|
|
500a9f647f | ||
|
|
71968fca0d | ||
|
|
fc6f84897f | ||
|
|
b31ad4b1f8 | ||
|
|
aababf37a8 | ||
|
|
44a42a177a | ||
|
|
e773a92e53 | ||
|
|
196d27b959 | ||
|
|
ec8861e983 | ||
|
|
9b601eb9fe | ||
|
|
c80d9cbf01 | ||
|
|
717c15256f | ||
|
|
13dafc2091 | ||
|
|
cfc3bcfa41 | ||
|
|
2e052e97c5 | ||
|
|
5d48d05287 | ||
|
|
e34ba844c5 | ||
|
|
1d84d3e440 | ||
|
|
7a7e9087e6 | ||
|
|
8481caa67c | ||
|
|
651e63a0fb | ||
|
|
88e462c4e5 | ||
|
|
8ef797d1a4 | ||
|
|
877fd1af35 | ||
|
|
fc01eecdcb | ||
|
|
e6ba6203bb | ||
|
|
6a786f18e6 | ||
|
|
57d46287d7 | ||
|
|
bb6ab74c19 | ||
|
|
3e759b3e8e | ||
|
|
47ee8d54a8 | ||
|
|
824b984a64 | ||
|
|
618185a52d | ||
|
|
8dbc6ab7d3 | ||
|
|
5eb6754b7c | ||
|
|
a914a92cea | ||
|
|
439a1a758e | ||
|
|
9383240627 | ||
|
|
ea765eb929 | ||
|
|
d5c816f24d | ||
|
|
cf5f20bbb5 | ||
|
|
27839dfd21 | ||
|
|
27517e1aee | ||
|
|
a31bfbefa7 | ||
|
|
f1729163cf | ||
|
|
6747d97d60 | ||
|
|
2fb2342fd4 | ||
|
|
0f530b7e80 | ||
|
|
78e3cf4ffb | ||
|
|
4e512b7023 | ||
|
|
ee3fed97e0 | ||
|
|
b45d4f6401 | ||
|
|
d4c28de2c4 | ||
|
|
b7d613ace6 | ||
|
|
68dbb31272 | ||
|
|
8fe1fa48b8 | ||
|
|
e5575b782c | ||
|
|
ea112dd93d | ||
|
|
056fc5e087 | ||
|
|
cacb02b7c8 | ||
|
|
c0b6ec8ccb | ||
|
|
f4c8f0a3d8 | ||
|
|
81407ffe3c | ||
|
|
6f7dbe99fe | ||
|
|
ec9afba029 | ||
|
|
6164c5d78d | ||
|
|
94dbcd5c06 | ||
|
|
4d0dee7ee8 | ||
|
|
49e10698b1 | ||
|
|
8e1536b59b | ||
|
|
aed160994a | ||
|
|
b2b3173420 | ||
|
|
f465af964a | ||
|
|
5a7d6a673a | ||
|
|
7fb9aa2115 | ||
|
|
fea303ac8b | ||
|
|
8e8291a0bd | ||
|
|
4dc175fcd2 | ||
|
|
009a99a8eb | ||
|
|
91caa67e04 | ||
|
|
53793f7dd2 | ||
|
|
32af8783ba | ||
|
|
d6f51f8eba | ||
|
|
f40b7361e8 | ||
|
|
0231e114d5 | ||
|
|
ca61a3e83f | ||
|
|
6979f5c82d | ||
|
|
7d45b45667 | ||
|
|
7575504b80 | ||
|
|
36bcb611f1 | ||
|
|
cc34c36794 | ||
|
|
b84cb93d2c | ||
|
|
08211a7740 | ||
|
|
97985dd458 | ||
|
|
edf157215c | ||
|
|
89671c71fa | ||
|
|
eeedcc50ab | ||
|
|
de0bb069aa | ||
|
|
5d53f8bfb3 | ||
|
|
d4a7de8070 | ||
|
|
3fe15641c7 | ||
|
|
08df71a7e4 | ||
|
|
77db25b4d9 | ||
|
|
62bb5a28aa | ||
|
|
f768b200d2 | ||
|
|
a5b6db74e5 | ||
|
|
cbe4467a22 | ||
|
|
8417d18eec | ||
|
|
93ae8187cd | ||
|
|
972338d86a | ||
|
|
a4a6e3b8f6 | ||
|
|
d788dd3042 | ||
|
|
974760f020 | ||
|
|
65569c4a60 | ||
|
|
39da45f0bd | ||
|
|
dc7c27e619 | ||
|
|
90661b7743 | ||
|
|
d07571ea7e | ||
|
|
fbf29e336f | ||
|
|
09a0a803a1 | ||
|
|
dd0327ba85 | ||
|
|
d900887d97 | ||
|
|
2a0efe70a5 | ||
|
|
ce09e57315 | ||
|
|
91023c5239 | ||
|
|
714fb10fac | ||
|
|
aa3e43207f | ||
|
|
e045989d39 | ||
|
|
ad5695210d | ||
|
|
4dab6e5008 | ||
|
|
522f26cf4e | ||
|
|
3eca4bc439 | ||
|
|
b374f67b52 | ||
|
|
c68277be2c | ||
|
|
bb8a3e82e6 | ||
|
|
b511d4ba53 | ||
|
|
fd997d4bda | ||
|
|
21d04a9f85 | ||
|
|
dcb5c12197 | ||
|
|
125e8766c5 | ||
|
|
7bce1f6df4 | ||
|
|
8f66fb6fef | ||
|
|
711e009e6d | ||
|
|
212fe31cb3 | ||
|
|
a3dc4fa001 | ||
|
|
ff53113358 | ||
|
|
94cbe15b54 | ||
|
|
90516e035d | ||
|
|
c77c754f6d | ||
|
|
288c52a2cd | ||
|
|
f0fa7326dd | ||
|
|
f29b0f2f26 | ||
|
|
5dcac24f99 | ||
|
|
1eb11ac377 | ||
|
|
2e89cdcfad | ||
|
|
1b0beccd3d | ||
|
|
abb5a72fc4 | ||
|
|
bf706f7586 | ||
|
|
4459663510 | ||
|
|
68a34dc28b | ||
|
|
ba1826587c | ||
|
|
2e7a8d50b0 | ||
|
|
2a4ca21050 | ||
|
|
0ed6c57c7f | ||
|
|
983d44947e | ||
|
|
5bd88dc82f | ||
|
|
51bbab803f | ||
|
|
a587ba4558 | ||
|
|
075b6cca30 | ||
|
|
4c263bbb3e | ||
|
|
420627d892 | ||
|
|
ce3a409997 | ||
|
|
0f9f1782ae | ||
|
|
d4704a02c5 | ||
|
|
9ca2ed2c93 | ||
|
|
ae12ffdefb | ||
|
|
1e13097126 | ||
|
|
9b8a6b1168 | ||
|
|
8f6dda5cd3 | ||
|
|
91780f236e | ||
|
|
f16474815c | ||
|
|
a8ff6d7e6e | ||
|
|
8395782a2e | ||
|
|
28ea625b01 | ||
|
|
efc569f6ed | ||
|
|
d377e10771 | ||
|
|
fef85877e6 | ||
|
|
19135bfaf4 | ||
|
|
5a147fccc2 | ||
|
|
20976814ba | ||
|
|
27e2612fc9 | ||
|
|
3ea432ef2d | ||
|
|
fe6acbc1e3 | ||
|
|
379b162e64 | ||
|
|
84832955dd | ||
|
|
34b99a51c3 | ||
|
|
fb5245e2af | ||
|
|
ff0a830e0c | ||
|
|
a365f071a4 | ||
|
|
f290e7b5b2 | ||
|
|
83a0ae673c | ||
|
|
7de646c290 | ||
|
|
5d681a72ac | ||
|
|
d725970e6e | ||
|
|
54b82760cd | ||
|
|
77f79fa913 | ||
|
|
6db714a2b1 | ||
|
|
913ac7c9f5 | ||
|
|
eecbfd29e7 | ||
|
|
41025225b2 | ||
|
|
07c7b28c03 | ||
|
|
3faf3d2a56 | ||
|
|
be094d5b7c | ||
|
|
459673bd59 | ||
|
|
2968b70e6b | ||
|
|
b6989a18fc | ||
|
|
4d6a504836 | ||
|
|
6c3737df68 | ||
|
|
9f382ed9bd | ||
|
|
f9e86e58d6 | ||
|
|
469f9c927f | ||
|
|
312724189b | ||
|
|
ec0a1f1ce4 | ||
|
|
ad93631ef8 | ||
|
|
3b6238ff93 | ||
|
|
5b9facee00 | ||
|
|
376f2a554a | ||
|
|
5fd0bd3db9 | ||
|
|
2deb3bbf23 | ||
|
|
01bef0c2b8 | ||
|
|
b65a8ce680 | ||
|
|
5fc4f63238 | ||
|
|
9b0e0c175b | ||
|
|
eb97e925e6 | ||
|
|
16f8975b18 | ||
|
|
5073fce2f7 | ||
|
|
cf5036eec6 | ||
|
|
20be37dd8b | ||
|
|
93349ae2dc | ||
|
|
be81de74cd | ||
|
|
88e739090d | ||
|
|
7f324f1957 | ||
|
|
4f31c9e72f | ||
|
|
c9a663ed39 | ||
|
|
105d8aeb6b | ||
|
|
6ea1211bc5 | ||
|
|
c13291a598 | ||
|
|
aaa78c618c | ||
|
|
2890c677d4 | ||
|
|
51a010de00 | ||
|
|
ca0aabd814 | ||
|
|
91d1ba1910 | ||
|
|
c7c739dc00 | ||
|
|
7a8389bf11 | ||
|
|
5f062b8ea3 | ||
|
|
b9ee14a0f9 | ||
|
|
c3baf73455 | ||
|
|
4a597e0ba7 | ||
|
|
d5f3e25bea | ||
|
|
5e4c1a8359 | ||
|
|
d86e655ad2 | ||
|
|
80154bbf9f | ||
|
|
be853ba2a7 | ||
|
|
4d3036d030 | ||
|
|
ecb9b5e28f | ||
|
|
38e3c198f2 | ||
|
|
2f64501556 | ||
|
|
2c2554d73d | ||
|
|
69d1accf3f | ||
|
|
785bdb8ecb | ||
|
|
78a1947cec | ||
|
|
0ff59ecb4e | ||
|
|
b58fed16b4 | ||
|
|
6719be02c3 | ||
|
|
8757834e07 | ||
|
|
aa243d1b8a | ||
|
|
aeb18eb124 | ||
|
|
6c3e118ee3 | ||
|
|
3c0fe4d684 | ||
|
|
12fd9aa1ef | ||
|
|
821122a33d | ||
|
|
0d9406d991 | ||
|
|
350eec3bc7 | ||
|
|
e700b3105a | ||
|
|
dd2a730b4a | ||
|
|
c6766bbe77 | ||
|
|
e5d3204b6c | ||
|
|
4767cbd12b | ||
|
|
deb4118c5d | ||
|
|
4516df5aac | ||
|
|
663df7bdc2 | ||
|
|
e81f0a4a95 | ||
|
|
38cd13dc0c | ||
|
|
14fd470363 | ||
|
|
fc8d9dc1fe | ||
|
|
1659adb419 | ||
|
|
6490b77d4c | ||
|
|
23463b620e | ||
|
|
6bc331be75 | ||
|
|
87f6410877 | ||
|
|
b1ddfc3a49 | ||
|
|
d01e757d2f | ||
|
|
e593ce0420 | ||
|
|
578abfabb3 | ||
|
|
aa7b7e43ff | ||
|
|
af4d4e0246 | ||
|
|
fecb11cba4 | ||
|
|
614f886008 | ||
|
|
6fcb895d70 | ||
|
|
5a98ede45e | ||
|
|
779d462932 | ||
|
|
e301116e87 | ||
|
|
bd3a4a719d | ||
|
|
4cfdc72c00 | ||
|
|
3620a9d256 | ||
|
|
f254a51d59 | ||
|
|
99bbe58255 | ||
|
|
a400abff4c | ||
|
|
585806837e | ||
|
|
249aa999a3 | ||
|
|
aae1d8b34f | ||
|
|
9d3638fa46 | ||
|
|
5b2a830d2d | ||
|
|
b87943e39d | ||
|
|
c421fd0b25 | ||
|
|
a1395a382a | ||
|
|
a54364642a | ||
|
|
14ab85adbd | ||
|
|
c284b7de40 | ||
|
|
e23056ac2f | ||
|
|
8ce2e605a2 | ||
|
|
9408247708 | ||
|
|
3e1c467b65 | ||
|
|
811db2e668 | ||
|
|
f833642733 | ||
|
|
30ce927621 | ||
|
|
f21f01be1a | ||
|
|
d81eaa6c13 | ||
|
|
5ad830a6d7 | ||
|
|
14e1829164 | ||
|
|
0a2810a98b | ||
|
|
7b84fce737 | ||
|
|
1e1b409f8b | ||
|
|
5f91a8fdfe | ||
|
|
5bb70c2675 | ||
|
|
ed6d8415bd | ||
|
|
d53ffd8da2 | ||
|
|
d52ecaa050 | ||
|
|
9ac001bd3e | ||
|
|
4b81166782 | ||
|
|
95866d0f2e | ||
|
|
54b8a1aea5 | ||
|
|
0822aa9240 | ||
|
|
c883850142 | ||
|
|
54da826447 | ||
|
|
ce5ea92076 | ||
|
|
98f8c5c0f2 | ||
|
|
6ced8f32b1 | ||
|
|
f870107c66 | ||
|
|
4e7aa5d0a9 | ||
|
|
779bcaeccb | ||
|
|
f2849357f8 | ||
|
|
998fca721a | ||
|
|
a83c68f2fc | ||
|
|
665809e11a | ||
|
|
a306fa26e0 | ||
|
|
9574f8ebd7 | ||
|
|
b62d946f83 | ||
|
|
b26b36da5d | ||
|
|
8d640ab467 | ||
|
|
eff4301027 | ||
|
|
0be4e6921d | ||
|
|
049875e8fc | ||
|
|
b9f7d3d889 | ||
|
|
70ba101fe1 | ||
|
|
b2753cb268 | ||
|
|
11f398b553 | ||
|
|
24a4099d82 | ||
|
|
99ea374fc5 | ||
|
|
1992ce1cfb | ||
|
|
2724534156 | ||
|
|
c57989a790 | ||
|
|
1888c83b6e | ||
|
|
837fd22254 | ||
|
|
02a3da111a | ||
|
|
5c2bf65eaa | ||
|
|
b5ae6a5785 | ||
|
|
699e07a0f7 | ||
|
|
b8025452bf | ||
|
|
1138c05dff | ||
|
|
ae36688bf2 | ||
|
|
64e2ef3d3b | ||
|
|
9cfeee0577 | ||
|
|
86e0f3fabe | ||
|
|
edcb3d8638 | ||
|
|
1582d8f504 | ||
|
|
5816bb64dc | ||
|
|
b7a28aa511 | ||
|
|
37ed80055b | ||
|
|
e6c2f1c15d | ||
|
|
a74c11aef5 | ||
|
|
ad3999066d | ||
|
|
77c10d14c9 | ||
|
|
8c2e430a56 | ||
|
|
0aaefe9a66 | ||
|
|
14db86e4b7 | ||
|
|
9949a6c9ee | ||
|
|
9bf5bd11e2 | ||
|
|
a3cc39cd92 | ||
|
|
45af35cccd | ||
|
|
20e9688268 | ||
|
|
3be5988083 | ||
|
|
474d877290 | ||
|
|
b6a2b7dded | ||
|
|
2e42c28485 | ||
|
|
4453211c0d | ||
|
|
01dd603bd5 | ||
|
|
fff71e1de0 | ||
|
|
c0ffbd360e | ||
|
|
2f1aadd497 | ||
|
|
645ab0ae98 | ||
|
|
9ac7ebc335 | ||
|
|
d807d22350 | ||
|
|
05594ecca0 | ||
|
|
a511a5b259 | ||
|
|
9125f96302 | ||
|
|
1b9ab43b6d | ||
|
|
1176c9bbf1 | ||
|
|
65e551c5cf | ||
|
|
8fadafcd24 | ||
|
|
57442a4848 | ||
|
|
7154f2c328 | ||
|
|
e3e4809446 | ||
|
|
5707b8c7e8 | ||
|
|
f125814847 | ||
|
|
893587040d | ||
|
|
308369b14f | ||
|
|
9516a1aeea | ||
|
|
f43dc99f7a | ||
|
|
3feb8c3344 | ||
|
|
4d0a2b806c | ||
|
|
a89fff1a22 | ||
|
|
52f01da400 | ||
|
|
b732c43274 | ||
|
|
ee0a5cada3 | ||
|
|
dbd499701f | ||
|
|
fc3f750efb | ||
|
|
457f3c8f54 | ||
|
|
e4d3c3b045 | ||
|
|
e4379e29af | ||
|
|
4542febaaf | ||
|
|
f691d8a6a5 | ||
|
|
503d7400f3 | ||
|
|
061ea0e7a3 | ||
|
|
dd4d542d7e | ||
|
|
75359e2b83 | ||
|
|
db7f8ffb1b | ||
|
|
f59a9040fc | ||
|
|
5114d12daf | ||
|
|
13a7c1ba81 | ||
|
|
26a7bb9746 | ||
|
|
d427f01224 | ||
|
|
25c73ed917 | ||
|
|
ce6a9dc448 | ||
|
|
c50922e90c | ||
|
|
bcd5fab33c | ||
|
|
49a2bc8ace | ||
|
|
f1c5268670 | ||
|
|
29fe7492cc | ||
|
|
2340a07408 | ||
|
|
797c39347f | ||
|
|
5a9d3959e2 | ||
|
|
1e0a7dea73 | ||
|
|
c61d6a5804 | ||
|
|
8aee5c769f | ||
|
|
7c585fcbce | ||
|
|
f7689f3154 | ||
|
|
6617b8b6c4 | ||
|
|
9db31c9191 | ||
|
|
767a59a481 | ||
|
|
f632444a0e | ||
|
|
16c3c1f498 | ||
|
|
c8bb9bf2e9 | ||
|
|
729ab25581 | ||
|
|
38e0f82812 | ||
|
|
b4fd2e31b3 | ||
|
|
e09d23d6c2 | ||
|
|
9ad0eabb85 | ||
|
|
fb950cb007 | ||
|
|
60c5c2d344 | ||
|
|
a29c4f2b65 | ||
|
|
5a99281e23 | ||
|
|
31e1f61548 | ||
|
|
dbad0a16c4 | ||
|
|
a69cbf45df | ||
|
|
284f423220 | ||
|
|
81a18e88ad | ||
|
|
72bc77f1d5 | ||
|
|
80b9b7594b | ||
|
|
7e671ee76a | ||
|
|
5d608cb498 | ||
|
|
c6982f70b4 | ||
|
|
ef0d1e7cec | ||
|
|
1db06194c7 | ||
|
|
f3e7e54675 | ||
|
|
2bd64db8d9 | ||
|
|
ae8098b80a | ||
|
|
bfe8dcd224 | ||
|
|
8a26994084 | ||
|
|
77d0865d58 | ||
|
|
bece337d79 | ||
|
|
5a5da3240f | ||
|
|
4138bb7ee1 | ||
|
|
4088bb47f0 | ||
|
|
d10b6d324e | ||
|
|
43b27831eb | ||
|
|
20c9c89b27 | ||
|
|
e1c420d37d | ||
|
|
be58598a3e | ||
|
|
5e08093609 | ||
|
|
f5599b95b3 | ||
|
|
ba930ea584 | ||
|
|
ba3fa24f9a | ||
|
|
ff0f5cd49c | ||
|
|
284f5cfb71 | ||
|
|
871796f172 | ||
|
|
9733f059c2 | ||
|
|
db71bdfde7 | ||
|
|
9aa27f4c11 | ||
|
|
8893d0ddde | ||
|
|
0b46860cdd | ||
|
|
ec1f9bd706 | ||
|
|
e30545c132 | ||
|
|
7d7f671920 | ||
|
|
fb7bfa928a | ||
|
|
100d69262c | ||
|
|
5e01658846 | ||
|
|
ccec93aee8 | ||
|
|
a089d82023 | ||
|
|
82ba583fa0 | ||
|
|
1b82044d7d | ||
|
|
0d49a2e0af | ||
|
|
1343d3a2a9 | ||
|
|
a86001b85c | ||
|
|
d6dd46c467 | ||
|
|
320974074c | ||
|
|
7b824abe52 | ||
|
|
bfd33f4b8d | ||
|
|
d5a21a0513 | ||
|
|
c894b38f06 | ||
|
|
a86347ea32 | ||
|
|
b60bfc7495 | ||
|
|
4b2f27b26d | ||
|
|
f4d7dda8e3 | ||
|
|
0d6e3e66ce | ||
|
|
7e4d451765 | ||
|
|
804bbc93b7 | ||
|
|
e7fe4ac3ec | ||
|
|
40c41b4400 | ||
|
|
0936c7c97d | ||
|
|
9ac537d38f | ||
|
|
fcfd55ff76 | ||
|
|
c2495ed57f | ||
|
|
6ad763fc01 | ||
|
|
c856a3e361 | ||
|
|
aa5f0cc335 | ||
|
|
b067335bbf | ||
|
|
7d24a14004 | ||
|
|
910be640e9 | ||
|
|
3137753afa | ||
|
|
000ff351a5 | ||
|
|
533c8ddaf7 | ||
|
|
f777d1b6c6 | ||
|
|
8b17d3b7d9 | ||
|
|
6fbe1daf8e | ||
|
|
3bd191414b | ||
|
|
fd2cb3495b | ||
|
|
e9d33bdc02 | ||
|
|
c0f4b31ab3 | ||
|
|
78de30dcf2 | ||
|
|
23c59dcae7 | ||
|
|
274ba5810b | ||
|
|
3661d0daec | ||
|
|
f215df891c | ||
|
|
ce5fe00a66 | ||
|
|
2d71e3ebea | ||
|
|
187194a615 | ||
|
|
9e956e593d | ||
|
|
c2cfcc7a2c | ||
|
|
8c482f75dd | ||
|
|
29a83cb626 | ||
|
|
a563e4c381 | ||
|
|
9cee587f22 | ||
|
|
b5cc243466 | ||
|
|
73c6b5a08c | ||
|
|
1f5a686570 | ||
|
|
6fc497f95e | ||
|
|
52eea667a7 | ||
|
|
c2f7e268ff | ||
|
|
31e5c558ab | ||
|
|
7a7ac9550d | ||
|
|
4d0fdf0dfd | ||
|
|
b448e8007e | ||
|
|
bad0e67266 | ||
|
|
3dee3dd627 | ||
|
|
4772350968 | ||
|
|
eabcc0bd1d | ||
|
|
3a3af6ab10 | ||
|
|
9723622b66 | ||
|
|
3b1d647acb | ||
|
|
6fa925a402 | ||
|
|
c8f837d726 | ||
|
|
4557396564 | ||
|
|
d3d67d47e1 | ||
|
|
42107e0715 | ||
|
|
ed183e0805 | ||
|
|
116be16392 | ||
|
|
2c188738e6 | ||
|
|
ebffba9589 | ||
|
|
ab644c3dfa | ||
|
|
c6cdd8a815 | ||
|
|
d99df59d9b | ||
|
|
c5f8247543 | ||
|
|
72557c9d23 | ||
|
|
1a1b6d923f | ||
|
|
27cf2c8740 | ||
|
|
c08a598d3f | ||
|
|
1318842c36 | ||
|
|
86d7d033d7 | ||
|
|
2df1dfa1cb | ||
|
|
78a1c658d8 | ||
|
|
f52f43fe90 | ||
|
|
58b244b7e9 | ||
|
|
d8bcbee463 | ||
|
|
f00152a9ac | ||
|
|
9720c56765 | ||
|
|
13ce04abb2 | ||
|
|
ab22b55b84 | ||
|
|
73117edbe7 | ||
|
|
d7e2a3542c | ||
|
|
fe240ed577 | ||
|
|
5e98cbe68f | ||
|
|
7a372dc50c | ||
|
|
958b6660be | ||
|
|
e731811911 | ||
|
|
66144cef2f | ||
|
|
68ba3fee6c | ||
|
|
95581dbaa6 | ||
|
|
aaaf3ba0b9 | ||
|
|
b885cae086 | ||
|
|
0f78eb933a | ||
|
|
6ee9a48e20 | ||
|
|
f90c51e923 | ||
|
|
6472a2cbce | ||
|
|
c75cbebbf0 | ||
|
|
12bfcd7b66 | ||
|
|
d365bc076e | ||
|
|
67ac6b7cec | ||
|
|
01325c8c7e | ||
|
|
21e9625c36 | ||
|
|
5bf1f11190 |
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||||
@@ -3,14 +3,27 @@ root = true
|
|||||||
[*]
|
[*]
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
[*.rs]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = false
|
||||||
|
|
||||||
[*.json]
|
[*.{json,js,css}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
[*.md]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{rs,toml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.snap]
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.lua]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.luau]
|
||||||
|
indent_style = tab
|
||||||
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# stylua formatting
|
||||||
|
0f8e1625d572a5fe0f7b5c08653ff92cc837d346
|
||||||
23
.github/workflows/changelog.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Changelog Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [assigned, opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Check Actions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Changelog check
|
||||||
|
uses: Zomzog/changelog-checker@v1.3.0
|
||||||
|
with:
|
||||||
|
fileName: CHANGELOG.md
|
||||||
|
noChangelogLabel: skip changelog
|
||||||
|
checkNotification: Simple
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
109
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build and Test
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.3.0
|
||||||
|
with:
|
||||||
|
version: 'v0.2.7'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --locked --verbose
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: cargo test --locked --verbose
|
||||||
|
|
||||||
|
msrv:
|
||||||
|
name: Check MSRV
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.3.0
|
||||||
|
with:
|
||||||
|
version: 'v0.2.7'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --locked --verbose
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Rustfmt, Clippy, Stylua, & Selene
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.3.0
|
||||||
|
with:
|
||||||
|
version: 'v0.2.7'
|
||||||
|
|
||||||
|
- name: Stylua
|
||||||
|
run: stylua --check plugin/src
|
||||||
|
|
||||||
|
- name: Selene
|
||||||
|
run: selene plugin/src
|
||||||
|
|
||||||
|
- name: Rustfmt
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
|
- name: Clippy
|
||||||
|
run: cargo clippy
|
||||||
|
|
||||||
158
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ["v*"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-release:
|
||||||
|
name: Create Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
steps:
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: ${{ github.ref }}
|
||||||
|
draft: true
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
build-plugin:
|
||||||
|
needs: ["create-release"]
|
||||||
|
name: Build Roblox Studio Plugin
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.1.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
trust-check: false
|
||||||
|
version: 'v0.3.0'
|
||||||
|
|
||||||
|
- name: Build Plugin
|
||||||
|
run: rojo build plugin --output Rojo.rbxm
|
||||||
|
|
||||||
|
- name: Upload Plugin to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: Rojo.rbxm
|
||||||
|
asset_name: Rojo.rbxm
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Plugin to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Rojo.rbxm
|
||||||
|
path: Rojo.rbxm
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: ["create-release"]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# https://doc.rust-lang.org/rustc/platform-support.html
|
||||||
|
include:
|
||||||
|
- host: linux
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
label: linux-x86_64
|
||||||
|
|
||||||
|
- host: windows
|
||||||
|
os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
label: windows-x86_64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
label: macos-x86_64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
label: macos-aarch64
|
||||||
|
|
||||||
|
name: Build (${{ matrix.target }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
BIN: rojo
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Get Version from Tag
|
||||||
|
shell: bash
|
||||||
|
# https://github.community/t/how-to-get-just-the-tag-name/16241/7#M1027
|
||||||
|
run: |
|
||||||
|
echo "PROJECT_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
echo "Version is: ${{ env.PROJECT_VERSION }}"
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
|
- name: Setup Aftman
|
||||||
|
uses: ok-nick/setup-aftman@v0.1.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
trust-check: false
|
||||||
|
version: 'v0.2.6'
|
||||||
|
|
||||||
|
- name: Build Release
|
||||||
|
run: cargo build --release --locked --verbose --target ${{ matrix.target }}
|
||||||
|
env:
|
||||||
|
# Build into a known directory so we can find our build artifact more
|
||||||
|
# easily.
|
||||||
|
CARGO_TARGET_DIR: output
|
||||||
|
|
||||||
|
# On platforms that use OpenSSL, ensure it is statically linked to
|
||||||
|
# make binaries more portable.
|
||||||
|
OPENSSL_STATIC: 1
|
||||||
|
|
||||||
|
- name: Create Release Archive
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir staging
|
||||||
|
|
||||||
|
if [ "${{ matrix.host }}" = "windows" ]; then
|
||||||
|
cp "output/${{ matrix.target }}/release/$BIN.exe" staging/
|
||||||
|
cd staging
|
||||||
|
7z a ../release.zip *
|
||||||
|
else
|
||||||
|
cp "output/${{ matrix.target }}/release/$BIN" staging/
|
||||||
|
cd staging
|
||||||
|
zip ../release.zip *
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload Archive to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: release.zip
|
||||||
|
asset_name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Archive to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
path: release.zip
|
||||||
23
.gitignore
vendored
@@ -1,2 +1,21 @@
|
|||||||
/target/
|
# Rust output directory
|
||||||
**/*.rs.bk
|
/target
|
||||||
|
|
||||||
|
# Headers for clibrojo
|
||||||
|
/include
|
||||||
|
|
||||||
|
# Roblox model and place files in the root, used for debugging
|
||||||
|
/*.rbxm
|
||||||
|
/*.rbxmx
|
||||||
|
/*.rbxl
|
||||||
|
/*.rbxlx
|
||||||
|
|
||||||
|
# Test places for the Roblox Studio Plugin
|
||||||
|
/plugin/*.rbxlx
|
||||||
|
|
||||||
|
# Roblox Studio holds 'lock' files on places
|
||||||
|
*.rbxl.lock
|
||||||
|
*.rbxlx.lock
|
||||||
|
|
||||||
|
# Snapshot files from the 'insta' Rust crate
|
||||||
|
**/*.snap.new
|
||||||
|
|||||||
18
.gitmodules
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[submodule "plugin/Packages/Roact"]
|
||||||
|
path = plugin/Packages/Roact
|
||||||
|
url = https://github.com/roblox/roact.git
|
||||||
|
[submodule "plugin/Packages/Flipper"]
|
||||||
|
path = plugin/Packages/Flipper
|
||||||
|
url = https://github.com/reselim/flipper.git
|
||||||
|
[submodule "plugin/Packages/Promise"]
|
||||||
|
path = plugin/Packages/Promise
|
||||||
|
url = https://github.com/evaera/roblox-lua-promise.git
|
||||||
|
[submodule "plugin/Packages/t"]
|
||||||
|
path = plugin/Packages/t
|
||||||
|
url = https://github.com/osyrisrblx/t.git
|
||||||
|
[submodule "plugin/Packages/TestEZ"]
|
||||||
|
path = plugin/Packages/TestEZ
|
||||||
|
url = https://github.com/roblox/testez.git
|
||||||
|
[submodule "plugin/Packages/Highlighter"]
|
||||||
|
path = plugin/Packages/Highlighter
|
||||||
|
url = https://github.com/boatbomber/highlighter.git
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
language: rust
|
|
||||||
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
791
CHANGELOG.md
Normal file
@@ -0,0 +1,791 @@
|
|||||||
|
# Rojo Changelog
|
||||||
|
|
||||||
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## [7.4.3] - August 6th, 2024
|
||||||
|
* Fixed issue with building binary files introduced in 7.4.2
|
||||||
|
* Fixed `value of type nil cannot be converted to number` warning spam in output. [#955]
|
||||||
|
|
||||||
|
[#955]: https://github.com/rojo-rbx/rojo/pull/893
|
||||||
|
|
||||||
|
## [7.4.2] - July 23, 2024
|
||||||
|
* Added Never option to Confirmation ([#893])
|
||||||
|
* Fixed removing trailing newlines ([#903])
|
||||||
|
* Updated the internal property database, correcting an issue with `SurfaceAppearance.Color` that was reported [here][Surface_Appearance_Color_1] and [here][Surface_Appearance_Color_2] ([#948])
|
||||||
|
|
||||||
|
[#893]: https://github.com/rojo-rbx/rojo/pull/893
|
||||||
|
[#903]: https://github.com/rojo-rbx/rojo/pull/903
|
||||||
|
[#948]: https://github.com/rojo-rbx/rojo/pull/948
|
||||||
|
[Surface_Appearance_Color_1]: https://devforum.roblox.com/t/jailbreak-custom-character-turned-shiny-black-no-texture/3075563
|
||||||
|
[Surface_Appearance_Color_2]: https://devforum.roblox.com/t/surfaceappearance-not-displaying-correctly/3075588
|
||||||
|
|
||||||
|
## [7.4.1] - February 20, 2024
|
||||||
|
* Made the `name` field optional on project files ([#870])
|
||||||
|
Files named `default.project.json` inherit the name of the folder they're in and all other projects
|
||||||
|
are named as expect (e.g. `foo.project.json` becomes an Instance named `foo`)
|
||||||
|
|
||||||
|
There is no change in behavior if `name` is set.
|
||||||
|
* Fixed incorrect results when building model pivots ([#865])
|
||||||
|
* Fixed incorrect results when serving model pivots ([#868])
|
||||||
|
* Rojo now converts any line endings to LF, preventing spurious diffs when syncing Lua files on Windows ([#854])
|
||||||
|
* Fixed Rojo plugin failing to connect when project contains certain unreadable properties ([#848])
|
||||||
|
* Fixed various cases where patch visualizer would not display sync failures ([#845], [#844])
|
||||||
|
* Fixed http error handling so Rojo can be used in Github Codespaces ([#847])
|
||||||
|
|
||||||
|
[#848]: https://github.com/rojo-rbx/rojo/pull/848
|
||||||
|
[#845]: https://github.com/rojo-rbx/rojo/pull/845
|
||||||
|
[#844]: https://github.com/rojo-rbx/rojo/pull/844
|
||||||
|
[#847]: https://github.com/rojo-rbx/rojo/pull/847
|
||||||
|
[#854]: https://github.com/rojo-rbx/rojo/pull/854
|
||||||
|
[#865]: https://github.com/rojo-rbx/rojo/pull/865
|
||||||
|
[#868]: https://github.com/rojo-rbx/rojo/pull/868
|
||||||
|
[#870]: https://github.com/rojo-rbx/rojo/pull/870
|
||||||
|
|
||||||
|
## [7.4.0] - January 16, 2024
|
||||||
|
* Improved the visualization for array properties like Tags ([#829])
|
||||||
|
* Significantly improved performance of `rojo serve`, `rojo build --watch`, and `rojo sourcemap --watch` on macOS. ([#830])
|
||||||
|
* Changed *.lua files that init command generates to *.luau ([#831])
|
||||||
|
* Does not remind users to sync if the sync lock is claimed already ([#833])
|
||||||
|
|
||||||
|
[#829]: https://github.com/rojo-rbx/rojo/pull/829
|
||||||
|
[#830]: https://github.com/rojo-rbx/rojo/pull/830
|
||||||
|
[#831]: https://github.com/rojo-rbx/rojo/pull/831
|
||||||
|
[#833]: https://github.com/rojo-rbx/rojo/pull/833
|
||||||
|
|
||||||
|
## [7.4.0-rc3] - October 25, 2023
|
||||||
|
* Changed `sourcemap --watch` to only generate the sourcemap when it's necessary ([#800])
|
||||||
|
* Switched script source property getter and setter to `ScriptEditorService` methods ([#801])
|
||||||
|
|
||||||
|
This ensures that the script editor reflects any changes Rojo makes to a script while it is open in the script editor.
|
||||||
|
|
||||||
|
* Fixed issues when handling `SecurityCapabilities` values ([#803], [#807])
|
||||||
|
* Fixed Rojo plugin erroring out when attempting to sync attributes with invalid names ([#809])
|
||||||
|
|
||||||
|
[#800]: https://github.com/rojo-rbx/rojo/pull/800
|
||||||
|
[#801]: https://github.com/rojo-rbx/rojo/pull/801
|
||||||
|
[#803]: https://github.com/rojo-rbx/rojo/pull/803
|
||||||
|
[#807]: https://github.com/rojo-rbx/rojo/pull/807
|
||||||
|
[#809]: https://github.com/rojo-rbx/rojo/pull/809
|
||||||
|
|
||||||
|
## [7.4.0-rc2] - October 3, 2023
|
||||||
|
* Fixed bug with parsing version for plugin validation ([#797])
|
||||||
|
|
||||||
|
[#797]: https://github.com/rojo-rbx/rojo/pull/797
|
||||||
|
|
||||||
|
## [7.4.0-rc1] - October 3, 2023
|
||||||
|
### Additions
|
||||||
|
#### Project format
|
||||||
|
* Added support for `.toml` files to `$path` ([#633])
|
||||||
|
* Added support for `Font` and `CFrame` attributes ([rbx-dom#299], [rbx-dom#296])
|
||||||
|
* Added the `emitLegacyScripts` field to the project format ([#765]). The behavior is outlined below:
|
||||||
|
|
||||||
|
| `emitLegacyScripts` Value | Action Taken by Rojo |
|
||||||
|
|---------------------------|------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| false | Rojo emits Scripts with the appropriate `RunContext` for `*.client.lua` and `*.server.lua` files in the project. |
|
||||||
|
| true (default) | Rojo emits LocalScripts and Scripts with legacy `RunContext` (same behavior as previously). |
|
||||||
|
|
||||||
|
|
||||||
|
It can be used like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"emitLegacyScripts": false,
|
||||||
|
"name": "MyCoolRunContextProject",
|
||||||
|
"tree": {
|
||||||
|
"$path": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Added `Terrain` classname inference, similar to services ([#771])
|
||||||
|
|
||||||
|
`Terrain` may now be defined in projects without using `$className`:
|
||||||
|
```json
|
||||||
|
"Workspace": {
|
||||||
|
"Terrain": {
|
||||||
|
"$path": "path/to/terrain.rbxm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Added support for `Terrain.MaterialColors` ([#770])
|
||||||
|
|
||||||
|
`Terrain.MaterialColors` is now represented in projects in a human readable format:
|
||||||
|
```json
|
||||||
|
"Workspace": {
|
||||||
|
"Terrain": {
|
||||||
|
"$path": "path/to/terrain.rbxm"
|
||||||
|
"$properties": {
|
||||||
|
"MaterialColors": {
|
||||||
|
"Grass": [10, 20, 30],
|
||||||
|
"Asphalt": [40, 50, 60],
|
||||||
|
"LeafyGrass": [255, 155, 55]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Added better support for `Font` properties ([#731])
|
||||||
|
|
||||||
|
`FontFace` properties may now be defined using implicit property syntax:
|
||||||
|
```json
|
||||||
|
"TextBox": {
|
||||||
|
"$className": "TextBox",
|
||||||
|
"$properties": {
|
||||||
|
"FontFace": {
|
||||||
|
"family": "rbxasset://fonts/families/RobotoMono.json",
|
||||||
|
"weight": "Thin",
|
||||||
|
"style": "Normal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Patch visualizer and notifications
|
||||||
|
* Added a setting to control patch confirmation behavior ([#774])
|
||||||
|
|
||||||
|
This is a new setting for controlling when the Rojo plugin prompts for confirmation before syncing. It has four options:
|
||||||
|
* Initial (default): prompts only once for a project in a given Studio session
|
||||||
|
* Always: always prompts for confirmation
|
||||||
|
* Large Changes: only prompts when there are more than X changed instances. The number of instances is configurable - an additional setting for the number of instances becomes available when this option is chosen
|
||||||
|
* Unlisted PlaceId: only prompts if the place ID is not present in servePlaceIds
|
||||||
|
|
||||||
|
* Added the ability to select Instances in patch visualizer ([#709])
|
||||||
|
|
||||||
|
Double-clicking an instance in the patch visualizer sets Roblox Studio's selection to the instance.
|
||||||
|
|
||||||
|
* Added a sync reminder notification. ([#689])
|
||||||
|
|
||||||
|
Rojo detects if you have previously synced to a place, and displays a notification reminding you to sync again:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* Added rich Source diffs in patch visualizer ([#748])
|
||||||
|
|
||||||
|
A "View Diff" button for script sources is now present in the patch visualizer. Clicking it displays a side-by-side diff of the script changes:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* Patch visualizer now indicates what changes failed to apply. ([#717])
|
||||||
|
|
||||||
|
A clickable warning label is displayed when the Rojo plugin is unable to apply changes. Clicking the label displays precise information about which changes failed:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
#### Miscellaneous
|
||||||
|
* Added `plugin` flag to the `build` command that outputs to the local plugins folder ([#735])
|
||||||
|
|
||||||
|
This is a flag that builds a Rojo project into Roblox Studio's plugins directory. This allows you to build a Rojo project and load it into Studio as a plugin without having to type the full path to the plugins directory. It can be used like this: `rojo build <PATH-TO-PROJECT> --plugin <FILE-NAME>`
|
||||||
|
|
||||||
|
* Added new plugin template to the `init` command ([#738])
|
||||||
|
|
||||||
|
This is a new template geared towards plugins. It is similar to the model template, but creates a `Script` instead of a `ModuleScript` in the `src` directory. It can be used like this: `rojo init --kind plugin`
|
||||||
|
|
||||||
|
* Added protection against syncing non-place projects as a place. ([#691])
|
||||||
|
* Add buttons for navigation on the Connected page ([#722])
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* Significantly improved performance of `rojo sourcemap` ([#668])
|
||||||
|
* Fixed the diff visualizer of connected sessions. ([#674])
|
||||||
|
* Fixed disconnected session activity. ([#675])
|
||||||
|
* Skip confirming patches that contain only a datamodel name change. ([#688])
|
||||||
|
* Fix Rojo breaking when users undo/redo in Studio ([#708])
|
||||||
|
* Improve tooltip behavior ([#723])
|
||||||
|
* Better settings controls ([#725])
|
||||||
|
* Rework patch visualizer with many fixes and improvements ([#713], [#726], [#755])
|
||||||
|
|
||||||
|
[#668]: https://github.com/rojo-rbx/rojo/pull/668
|
||||||
|
[#674]: https://github.com/rojo-rbx/rojo/pull/674
|
||||||
|
[#675]: https://github.com/rojo-rbx/rojo/pull/675
|
||||||
|
[#688]: https://github.com/rojo-rbx/rojo/pull/688
|
||||||
|
[#689]: https://github.com/rojo-rbx/rojo/pull/689
|
||||||
|
[#691]: https://github.com/rojo-rbx/rojo/pull/691
|
||||||
|
[#709]: https://github.com/rojo-rbx/rojo/pull/709
|
||||||
|
[#708]: https://github.com/rojo-rbx/rojo/pull/708
|
||||||
|
[#713]: https://github.com/rojo-rbx/rojo/pull/713
|
||||||
|
[#717]: https://github.com/rojo-rbx/rojo/pull/717
|
||||||
|
[#722]: https://github.com/rojo-rbx/rojo/pull/722
|
||||||
|
[#723]: https://github.com/rojo-rbx/rojo/pull/723
|
||||||
|
[#725]: https://github.com/rojo-rbx/rojo/pull/725
|
||||||
|
[#726]: https://github.com/rojo-rbx/rojo/pull/726
|
||||||
|
[#633]: https://github.com/rojo-rbx/rojo/pull/633
|
||||||
|
[#735]: https://github.com/rojo-rbx/rojo/pull/735
|
||||||
|
[#731]: https://github.com/rojo-rbx/rojo/pull/731
|
||||||
|
[#738]: https://github.com/rojo-rbx/rojo/pull/738
|
||||||
|
[#748]: https://github.com/rojo-rbx/rojo/pull/748
|
||||||
|
[#755]: https://github.com/rojo-rbx/rojo/pull/755
|
||||||
|
[#765]: https://github.com/rojo-rbx/rojo/pull/765
|
||||||
|
[#770]: https://github.com/rojo-rbx/rojo/pull/770
|
||||||
|
[#771]: https://github.com/rojo-rbx/rojo/pull/771
|
||||||
|
[#774]: https://github.com/rojo-rbx/rojo/pull/774
|
||||||
|
[rbx-dom#299]: https://github.com/rojo-rbx/rbx-dom/pull/299
|
||||||
|
[rbx-dom#296]: https://github.com/rojo-rbx/rbx-dom/pull/296
|
||||||
|
|
||||||
|
## [7.3.0] - April 22, 2023
|
||||||
|
* Added `$attributes` to project format. ([#574])
|
||||||
|
* Added `--watch` flag to `rojo sourcemap`. ([#602])
|
||||||
|
* Added support for `init.csv` files. ([#594])
|
||||||
|
* Added real-time sync status to the Studio plugin. ([#569])
|
||||||
|
* Added support for copying error messages to the clipboard. ([#614])
|
||||||
|
* Added sync locking for Team Create. ([#590])
|
||||||
|
* Added support for specifying HTTP or HTTPS protocol in plugin. ([#642])
|
||||||
|
* Added tooltips to buttons in the Studio plugin. ([#637])
|
||||||
|
* Added visual diffs when connecting from the Studio plugin. ([#603])
|
||||||
|
* Host and port are now saved in the Studio plugin. ([#613])
|
||||||
|
* Improved padding on notifications in Studio plugin. ([#589])
|
||||||
|
* Renamed `Common` to `Shared` in the default Rojo project. ([#611])
|
||||||
|
* Reduced the minimum size of the Studio plugin widget. ([#606])
|
||||||
|
* Fixed current directory in `rojo fmt-project`. ([#581])
|
||||||
|
* Fixed errors after a session has already ended. ([#587])
|
||||||
|
* Fixed an uncommon security permission error ([#619])
|
||||||
|
|
||||||
|
[#569]: https://github.com/rojo-rbx/rojo/pull/569
|
||||||
|
[#574]: https://github.com/rojo-rbx/rojo/pull/574
|
||||||
|
[#581]: https://github.com/rojo-rbx/rojo/pull/581
|
||||||
|
[#587]: https://github.com/rojo-rbx/rojo/pull/587
|
||||||
|
[#589]: https://github.com/rojo-rbx/rojo/pull/589
|
||||||
|
[#590]: https://github.com/rojo-rbx/rojo/pull/590
|
||||||
|
[#594]: https://github.com/rojo-rbx/rojo/pull/594
|
||||||
|
[#602]: https://github.com/rojo-rbx/rojo/pull/602
|
||||||
|
[#603]: https://github.com/rojo-rbx/rojo/pull/603
|
||||||
|
[#606]: https://github.com/rojo-rbx/rojo/pull/606
|
||||||
|
[#611]: https://github.com/rojo-rbx/rojo/pull/611
|
||||||
|
[#613]: https://github.com/rojo-rbx/rojo/pull/613
|
||||||
|
[#614]: https://github.com/rojo-rbx/rojo/pull/614
|
||||||
|
[#619]: https://github.com/rojo-rbx/rojo/pull/619
|
||||||
|
[#637]: https://github.com/rojo-rbx/rojo/pull/637
|
||||||
|
[#642]: https://github.com/rojo-rbx/rojo/pull/642
|
||||||
|
[7.3.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.3.0
|
||||||
|
|
||||||
|
## [7.2.1] - July 8, 2022
|
||||||
|
* Fixed notification sound by changing it to a generic sound. ([#566])
|
||||||
|
* Added setting to turn off sound effects. ([#568])
|
||||||
|
|
||||||
|
[#566]: https://github.com/rojo-rbx/rojo/pull/566
|
||||||
|
[#568]: https://github.com/rojo-rbx/rojo/pull/568
|
||||||
|
[7.2.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.2.1
|
||||||
|
|
||||||
|
## [7.2.0] - June 29, 2022
|
||||||
|
* Added support for `.luau` files. ([#552])
|
||||||
|
* Added support for live syncing Attributes and Tags. ([#553])
|
||||||
|
* Added notification popups in the Roblox Studio plugin. ([#540])
|
||||||
|
* Fixed `init.meta.json` when used with `init.lua` and related files. ([#549])
|
||||||
|
* Fixed incorrect output when serving from a non-default address or port ([#556])
|
||||||
|
* Fixed Linux binaries not running on systems with older glibc. ([#561])
|
||||||
|
* Added `camelCase` casing for JSON models, deprecating `PascalCase` names. ([#563])
|
||||||
|
* Switched from structopt to clap for command line argument parsing.
|
||||||
|
* Significantly improved performance of building and serving. ([#548])
|
||||||
|
* Increased minimum supported Rust version to 1.57.0. ([#564])
|
||||||
|
|
||||||
|
[#540]: https://github.com/rojo-rbx/rojo/pull/540
|
||||||
|
[#548]: https://github.com/rojo-rbx/rojo/pull/548
|
||||||
|
[#549]: https://github.com/rojo-rbx/rojo/pull/549
|
||||||
|
[#552]: https://github.com/rojo-rbx/rojo/pull/552
|
||||||
|
[#553]: https://github.com/rojo-rbx/rojo/pull/553
|
||||||
|
[#556]: https://github.com/rojo-rbx/rojo/pull/556
|
||||||
|
[#561]: https://github.com/rojo-rbx/rojo/pull/561
|
||||||
|
[#563]: https://github.com/rojo-rbx/rojo/pull/563
|
||||||
|
[#564]: https://github.com/rojo-rbx/rojo/pull/564
|
||||||
|
[7.2.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.2.0
|
||||||
|
|
||||||
|
## [7.1.1] - May 26, 2022
|
||||||
|
* Fixed sourcemap command not stripping paths correctly ([#544])
|
||||||
|
* Fixed Studio plugin settings not saving correctly.
|
||||||
|
|
||||||
|
[#544]: https://github.com/rojo-rbx/rojo/pull/544
|
||||||
|
[#545]: https://github.com/rojo-rbx/rojo/pull/545
|
||||||
|
[7.1.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.1.1
|
||||||
|
|
||||||
|
## [7.1.0] - May 22, 2022
|
||||||
|
* Added support for specifying an address to be used by default in project files. ([#507])
|
||||||
|
* Added support for optional paths in project files. ([#472])
|
||||||
|
* Added support for the new Open Cloud API when uploading. ([#504])
|
||||||
|
* Added `sourcemap` command for generating sourcemaps to feed into other tools. ([#530])
|
||||||
|
* Added PluginActions for connecting/disconnecting a session ([#537])
|
||||||
|
* Added changing toolbar icon to indicate state ([#538])
|
||||||
|
|
||||||
|
[#472]: https://github.com/rojo-rbx/rojo/pull/472
|
||||||
|
[#504]: https://github.com/rojo-rbx/rojo/pull/504
|
||||||
|
[#507]: https://github.com/rojo-rbx/rojo/pull/507
|
||||||
|
[#530]: https://github.com/rojo-rbx/rojo/pull/530
|
||||||
|
[#537]: https://github.com/rojo-rbx/rojo/pull/537
|
||||||
|
[#538]: https://github.com/rojo-rbx/rojo/pull/538
|
||||||
|
[7.1.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.1.0
|
||||||
|
|
||||||
|
## [7.0.0] - December 10, 2021
|
||||||
|
* Fixed Rojo's interactions with properties enabled by FFlags that are not yet enabled. ([#493])
|
||||||
|
* Improved output in Roblox Studio plugin when bad property data is encountered.
|
||||||
|
* Reintroduced support for CFrame shorthand syntax in Rojo project and `.meta.json` files, matching Rojo 6. ([#430])
|
||||||
|
* Connection settings are now remembered when reconnecting in Roblox Studio. ([#500])
|
||||||
|
* Updated reflection database to Roblox v503.
|
||||||
|
|
||||||
|
[#430]: https://github.com/rojo-rbx/rojo/issues/430
|
||||||
|
[#493]: https://github.com/rojo-rbx/rojo/pull/493
|
||||||
|
[#500]: https://github.com/rojo-rbx/rojo/pull/500
|
||||||
|
[7.0.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0
|
||||||
|
|
||||||
|
## [7.0.0-rc.3] - October 19, 2021
|
||||||
|
This is the last release candidate for Rojo 7. In an effort to get Rojo 7 out the door, we'll be freezing features from here on out, something we should've done a couple months ago.
|
||||||
|
|
||||||
|
Expect to see Rojo 7 stable soon!
|
||||||
|
|
||||||
|
* Added support for writing `Tags` in project files, model files, and meta files. ([#484])
|
||||||
|
* Adjusted Studio plugin colors to match Roblox Studio palette. ([#482])
|
||||||
|
* Improved experimental two-way sync feature by batching changes. ([#478])
|
||||||
|
|
||||||
|
[#482]: https://github.com/rojo-rbx/rojo/pull/482
|
||||||
|
[#484]: https://github.com/rojo-rbx/rojo/pull/484
|
||||||
|
[#478]: https://github.com/rojo-rbx/rojo/pull/478
|
||||||
|
[7.0.0-rc.3]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-rc.3
|
||||||
|
|
||||||
|
## 7.0.0-rc.2 - October 19, 2021
|
||||||
|
(Botched release due to Git mishap, oops!)
|
||||||
|
|
||||||
|
## [7.0.0-rc.1] - August 23, 2021
|
||||||
|
In Rojo 6 and previous Rojo 7 alphas, an explicit Vector3 property would be written like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"className": "Part",
|
||||||
|
"properties": {
|
||||||
|
"Position": {
|
||||||
|
"Type": "Vector3",
|
||||||
|
"Value": [1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For Rojo 7, this will need to be changed to:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"className": "Part",
|
||||||
|
"properties": {
|
||||||
|
"Position": {
|
||||||
|
"Vector3": [1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The shorthand property format that most users use is not impacted. For reference, it looks like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"className": "Part",
|
||||||
|
"properties": {
|
||||||
|
"Position": [1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Major breaking change: changed property syntax for project files; shorthand syntax is unchanged.
|
||||||
|
* Added the `fmt-project` subcommand for formatting Rojo project files.
|
||||||
|
* Improved error output for many subcommands.
|
||||||
|
* Updated to stable versions of rbx-dom libraries.
|
||||||
|
* Updated async infrastructure, which should fix a handful of bugs. ([#459])
|
||||||
|
* Fixed syncing refs in the Roblox Studio plugin ([#462], [#466])
|
||||||
|
* Added support for long paths on Windows. ([#464])
|
||||||
|
|
||||||
|
[#459]: https://github.com/rojo-rbx/rojo/pull/459
|
||||||
|
[#462]: https://github.com/rojo-rbx/rojo/pull/462
|
||||||
|
[#464]: https://github.com/rojo-rbx/rojo/pull/464
|
||||||
|
[#466]: https://github.com/rojo-rbx/rojo/pull/466
|
||||||
|
[7.0.0-rc.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-rc.1
|
||||||
|
|
||||||
|
## [7.0.0-alpha.4][7.0.0-alpha.4] (May 5, 2021)
|
||||||
|
* Added the `gameId` and `placeId` optional properties to project files.
|
||||||
|
* When connecting from the Rojo Roblox Studio plugin, Rojo will set the game and place ID of the current place to these values, if set.
|
||||||
|
* This is equivalent to running `game:SetUniverseId(...)` and `game:SetPlaceId(...)` from the command bar in Studio.
|
||||||
|
* Added "EXPERIMENTAL!" label to two-way sync toggle in Rojo's Roblox Studio plugin.
|
||||||
|
* Fixed `Name` and `Parent` properties being allowed in Rojo projects. ([#413][pr-413])
|
||||||
|
* Fixed "Open Scripts Externally" feature crashing Studio. ([#369][issue-369])
|
||||||
|
* Empty `.model.json` files will no longer cause errors. ([#420][pr-420])
|
||||||
|
* When specifying `$path` on a service, Rojo now keeps the correct class name. ([#331][issue-331])
|
||||||
|
* Improved error messages for misconfigured projects.
|
||||||
|
|
||||||
|
[issue-331]: https://github.com/rojo-rbx/rojo/issues/331
|
||||||
|
[issue-369]: https://github.com/rojo-rbx/rojo/issues/369
|
||||||
|
[pr-420]: https://github.com/rojo-rbx/rojo/pull/420
|
||||||
|
[pr-413]: https://github.com/rojo-rbx/rojo/pull/413
|
||||||
|
[7.0.0-alpha.4]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.4
|
||||||
|
|
||||||
|
## [7.0.0-alpha.3][7.0.0-alpha.3] (February 19, 2021)
|
||||||
|
* Updated dependencies, fixing `OptionalCoordinateFrame`-related issues.
|
||||||
|
* Added `--address` flag to `rojo serve` to allow for external connections. ([#403][pr-403])
|
||||||
|
|
||||||
|
[pr-403]: https://github.com/rojo-rbx/rojo/pull/403
|
||||||
|
[7.0.0-alpha.3]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.3
|
||||||
|
|
||||||
|
## [7.0.0-alpha.2][7.0.0-alpha.2] (February 19, 2021)
|
||||||
|
* Fixed incorrect protocol version between the client and server.
|
||||||
|
|
||||||
|
[7.0.0-alpha.2]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.2
|
||||||
|
|
||||||
|
## [7.0.0-alpha.1][7.0.0-alpha.1] (February 18, 2021)
|
||||||
|
This release includes a brand new implementation of the Roblox DOM. It brings performance improvements, much better support for `rbxl` and `rbxm` files, and a better internal API.
|
||||||
|
|
||||||
|
* Added support for all remaining property types.
|
||||||
|
* Added support for the entire Roblox binary model format.
|
||||||
|
* Changed `rojo upload` to upload binary places and models instead of XML.
|
||||||
|
* This should make using `rojo upload` much more feasible for large places.
|
||||||
|
* **Breaking**: Changed format of some types of values in `project.json`, `model.json`, and `meta.json` files.
|
||||||
|
* This should impact few projects. See [this file][allValues.json] for new examples of each property type.
|
||||||
|
|
||||||
|
Formatting of types will change more before the stable release of Rojo 7. We're hoping to use this opportunity to normalize some of the case inconsistency introduced in Rojo 0.5.
|
||||||
|
|
||||||
|
[7.0.0-alpha.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.1
|
||||||
|
[allValues.json]: https://github.com/rojo-rbx/rojo/blob/f4a790eb50b74e482000bad1dcfe22533992fb20/plugin/rbx_dom_lua/src/allValues.json
|
||||||
|
|
||||||
|
## [6.0.2](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.2) (February 9, 2021)
|
||||||
|
* Fixed `rojo upload` to handle CSRF challenges.
|
||||||
|
|
||||||
|
## [6.0.1](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.1) (January 22, 2021)
|
||||||
|
* Fixed `rojo upload` requests being rejected by Roblox
|
||||||
|
|
||||||
|
## [6.0.0](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0) (January 16, 2021)
|
||||||
|
* Improved server error messages
|
||||||
|
* The server will now keep running in more error cases
|
||||||
|
* Fixed Rojo being unable to diff ClassName changes
|
||||||
|
|
||||||
|
## [6.0.0 Release Candidate 4](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.4) (December 14, 2020)
|
||||||
|
* Added brand new Rojo UI ([#367](https://github.com/rojo-rbx/rojo/pull/367))
|
||||||
|
* Added `projectName` to `/api/rojo` output.
|
||||||
|
|
||||||
|
## [6.0.0 Release Candidate 3](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.3) (November 19, 2020)
|
||||||
|
* Fixed the Rojo plugin attempting to write the non-scriptable properties `Instance.SourceAssetId` and `HttpServer.HttpEnabled`.
|
||||||
|
* Fixed the Rojo plugin's handling of null referents.
|
||||||
|
|
||||||
|
## [6.0.0 Release Candidate 2](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.2) (November 19, 2020)
|
||||||
|
* Fixed crash when malformed CSV files are put into a project. ([#310](https://github.com/rojo-rbx/rojo/issues/310))
|
||||||
|
* Fixed incorrect string escaping when producing Lua code from JSON files. ([#314](https://github.com/rojo-rbx/rojo/issues/314))
|
||||||
|
* Fixed performance issues introduced in Rojo 6.0.0-rc.1. ([#317](https://github.com/rojo-rbx/rojo/issues/317))
|
||||||
|
* Fixed `rojo plugin install` subcommand failing for everyone except Rojo developers. ([#320](https://github.com/rojo-rbx/rojo/issues/320))
|
||||||
|
* Updated default place template to take advantage of [#210](https://github.com/rojo-rbx/rojo/pull/210).
|
||||||
|
* Enabled glob ignore patterns by default and removed the `unstable_glob_ignore` feature.
|
||||||
|
* `globIgnorePaths` can be set on a project to a list of globs to ignore.
|
||||||
|
* The Rojo plugin now completes as much as it can from a patch without disconnecting. Warnings are shown in the console.
|
||||||
|
* Fixed 6.0.0-rc.1 regression causing instances that changed ClassName to instead... not change ClassName.
|
||||||
|
|
||||||
|
## [6.0.0 Release Candidate 1](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.1) (March 29, 2020)
|
||||||
|
This release jumped from 0.6.0 to 6.0.0. Rojo has been in use in production for many users for quite a long times, and so 6.0 is a more accurate reflection of Rojo's version than a pre-1.0 version.
|
||||||
|
|
||||||
|
* Added basic settings panel to plugin, with two settings:
|
||||||
|
* "Open Scripts Externally": When enabled, opening a script in Studio will instead open it in your default text editor.
|
||||||
|
* "Two-Way Sync": When enabled, Rojo will attempt to save changes to your place back to the filesystem. **Very early feature, very broken, beware!**
|
||||||
|
* Added `--color` option to force-enable or force-disable color in Rojo's output.
|
||||||
|
* Added support for turning `.json` files into `ModuleScript` instances ([#308](https://github.com/rojo-rbx/rojo/pull/308))
|
||||||
|
* Added `rojo plugin install` and `rojo plugin uninstall` to allow Rojo to manage its Roblox Studio plugin. ([#304](https://github.com/rojo-rbx/rojo/pull/304))
|
||||||
|
* Class names no longer need to be specified for Roblox services in Rojo projects. ([#210](https://github.com/rojo-rbx/rojo/pull/210))
|
||||||
|
* The server half of **experimental** two-way sync is now enabled by default.
|
||||||
|
* Increased default logging verbosity in commands like `rojo build`.
|
||||||
|
* Rojo now requires a project file again, just like 0.5.4.
|
||||||
|
|
||||||
|
## [0.6.0 Alpha 3](https://github.com/rojo-rbx/rojo/releases/tag/v0.6.0-alpha.3) (March 13, 2020)
|
||||||
|
* Added `--watch` argument to `rojo build`. ([#284](https://github.com/rojo-rbx/rojo/pull/284))
|
||||||
|
* Added dark theme support to plugin. ([#241](https://github.com/rojo-rbx/rojo/issues/241))
|
||||||
|
* Added a revamped `rojo init` command, which will now create more complete projects.
|
||||||
|
* Added the `rojo doc` command, which opens Rojo's documentation in your browser.
|
||||||
|
* Fixed many crashes from malformed projects and filesystem edge cases in `rojo serve`.
|
||||||
|
* Simplified filesystem access code dramatically.
|
||||||
|
* Improved error reporting and logging across the board.
|
||||||
|
* Log messages have a less noisy prefix.
|
||||||
|
* Any thread panicking now causes Rojo to abort instead of existing as a zombie.
|
||||||
|
* Errors now have a list of causes, helping make many errors more clear.
|
||||||
|
|
||||||
|
## [0.6.0 Alpha 2](https://github.com/rojo-rbx/rojo/releases/tag/v0.6.0-alpha.2) (March 6, 2020)
|
||||||
|
* Fixed `rojo upload` command always uploading models.
|
||||||
|
* Removed `--kind` parameter to `rojo upload`; Rojo now automatically uploads the correct kind of asset based on your project file.
|
||||||
|
|
||||||
|
## [0.5.4](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.4) (February 26, 2020)
|
||||||
|
This is a general maintenance release for the Rojo 0.5.x release series.
|
||||||
|
|
||||||
|
* Updated reflection database and other dependencies.
|
||||||
|
* First stable release with binaries for macOS and Linux.
|
||||||
|
|
||||||
|
## [0.6.0 Alpha 1](https://github.com/rojo-rbx/rojo/releases/tag/v0.6.0-alpha.1) (January 22, 2020)
|
||||||
|
|
||||||
|
### General
|
||||||
|
* Added support for nested project files. ([#95](https://github.com/rojo-rbx/rojo/issues/95))
|
||||||
|
* Added project file hot-reloading. ([#10](https://github.com/rojo-rbx/rojo/issues/10)])
|
||||||
|
* Fixed Rojo dropping Ref properties ([#142](https://github.com/rojo-rbx/rojo/issues/142))
|
||||||
|
* This means that properties like `PrimaryPart` now work!
|
||||||
|
* Improved live sync protocol to reduce round-trips and improve syncing consistency.
|
||||||
|
* Improved support for binary model files and places.
|
||||||
|
|
||||||
|
### Command Line
|
||||||
|
* Added `--verbose`/`-v` flag, which can be specified multiple times to increase verbosity.
|
||||||
|
* Added support for automatically finding Roblox Studio's auth cookie for `rojo upload` on Windows.
|
||||||
|
* Added support for building, serving and uploading sources that aren't Rojo projects.
|
||||||
|
* Improved feedback from `rojo serve`.
|
||||||
|
* Removed support for legacy `roblox-project.json` projects, deprecated in an early Rojo 0.5.0 alpha.
|
||||||
|
* Rojo no longer traverses directories upwards looking for project files.
|
||||||
|
* Though undocumented, Rojo 0.5.x will search for a project file contained in any ancestor folders. This feature was removed to better support other 0.6.x features.
|
||||||
|
|
||||||
|
### Roblox Studio Plugin
|
||||||
|
* Added "connecting" state to improve experience when live syncing.
|
||||||
|
* Added "error" state to show errors in a place that isn't the output panel.
|
||||||
|
* Improved diagnostics for when the Rojo plugin cannot create an instance.
|
||||||
|
|
||||||
|
## [0.5.3](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.3) (October 15, 2019)
|
||||||
|
* Fixed an issue where Rojo would throw an error when encountering recently-added instance classes.
|
||||||
|
|
||||||
|
## [0.5.2](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.2) (October 14, 2019)
|
||||||
|
* Fixed an issue where `LocalizationTable` instances would have their column order randomized. ([#173](https://github.com/rojo-rbx/rojo/issues/173))
|
||||||
|
|
||||||
|
## [0.5.1](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.1) (October 4, 2019)
|
||||||
|
* Fixed an issue where Rojo would drop changes if they happened too quickly ([#252](https://github.com/rojo-rbx/rojo/issues/252))
|
||||||
|
* Improved diagnostics for when the Rojo plugin cannot create an instance.
|
||||||
|
* Updated dependencies
|
||||||
|
* This brings Rojo's reflection database from client release 395 to client release 404.
|
||||||
|
|
||||||
|
## [0.5.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0) (August 27, 2019)
|
||||||
|
* Changed `.model.json` naming, which may require projects to migrate ambiguous cases:
|
||||||
|
* The file name now takes precedence over the `Name` field in the model, like Rojo 0.4.x.
|
||||||
|
* The `Name` field of the top-level instance is now optional. It's recommended that you remove it from your models.
|
||||||
|
* Rojo will emit a warning when `Name` is specified and does not match the name from the file.
|
||||||
|
* Fixed `Rect` values being set to `0, 0, 0, 0` when synced with the Rojo plugin. ([#201](https://github.com/rojo-rbx/rojo/issues/201))
|
||||||
|
* Fixed live-syncing of `PhysicalProperties`, `NumberSequence`, and `ColorSequence` values
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 13](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.13) (August 2, 2019)
|
||||||
|
* Bumped minimum Rust version to 1.34.0.
|
||||||
|
* Fixed default port documentation in `rojo serve --help` ([#219](https://github.com/rojo-rbx/rojo/issues/219))
|
||||||
|
* Fixed BrickColor support by upgrading Roblox-related dependencies
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 12](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.12) (July 2, 2019)
|
||||||
|
* Added `.meta.json` files
|
||||||
|
* `init.meta.json` files replace `init.model.json` files from Rojo 0.4.x ([#183](https://github.com/rojo-rbx/rojo/pull/183))
|
||||||
|
* Other `.meta.json` files allow attaching extra data to other files ([#189](https://github.com/rojo-rbx/rojo/pull/189))
|
||||||
|
* Added support for infinite and NaN values in types like `Vector2` when building models and places.
|
||||||
|
* These types aren't supported for live-syncing yet due to limitations around JSON encoding.
|
||||||
|
* Added support for using `SharedString` values when building XML models and places.
|
||||||
|
* Added support for live-syncing `CollectionService` tags.
|
||||||
|
* Added a warning when building binary place files, since they're still experimental and have bugs.
|
||||||
|
* Added a warning when trying to use Rojo 0.5.x with a Rojo 0.4.x-only project.
|
||||||
|
* Added a warning when a Rojo project contains keys that start with `$`, which are reserved names. ([#191](https://github.com/rojo-rbx/rojo/issues/191))
|
||||||
|
* Rojo now throws an error if unknown keys are found most files.
|
||||||
|
* Added an icon to the plugin's toolbar button
|
||||||
|
* Changed the plugin to use a docking widget for all UI.
|
||||||
|
* Changed the plugin to ignore unknown properties when live-syncing.
|
||||||
|
* Rojo's approach to this problem might change later, like with a strict model mode ([#190](https://github.com/rojo-rbx/rojo/issues/190)) or another approach.
|
||||||
|
* Upgraded to reflection database from client release 388.
|
||||||
|
* Updated Rojo's branding to shift the color palette to make it work better on dark backgrounds
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 11](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.11) (May 29, 2019)
|
||||||
|
* Added support for implicit property values in JSON model files ([#154](https://github.com/rojo-rbx/rojo/pull/154))
|
||||||
|
* `Content` propertyes can now be specified in projects and model files as regular string literals.
|
||||||
|
* Added support for `BrickColor` properties.
|
||||||
|
* Added support for properties added in client release 384, like `Lighting.Technology` being set to `"ShadowMap"`.
|
||||||
|
* Improved performance when working with XML models and places
|
||||||
|
* Fixed serializing empty `Content` properties as XML
|
||||||
|
* Fixed serializing infinite and NaN floating point properties in XML
|
||||||
|
* Improved compatibility with XML models
|
||||||
|
* Plugin should now be able to live-sync more properties, and ignore ones it can't, like `Lighting.Technology`.
|
||||||
|
|
||||||
|
## 0.5.0 Alpha 10
|
||||||
|
* This release was a dud due to [issue #176](https://github.com/rojo-rbx/rojo/issues/176) and was rolled back.
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 9](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.9) (April 4, 2019)
|
||||||
|
* Changed `rojo build` to use buffered I/O, which can make it up to 2x faster in some cases.
|
||||||
|
* Building [*Road Not Taken*](https://github.com/LPGhatguy/roads) to an `rbxlx` file dropped from 150ms to 70ms on my machine
|
||||||
|
* Fixed `LocalizationTable` instances being made from `csv` files incorrectly interpreting empty rows and columns. ([#149](https://github.com/rojo-rbx/rojo/pull/149))
|
||||||
|
* Fixed CSV files with entries that parse as numbers causing Rojo to panic. ([#152](https://github.com/rojo-rbx/rojo/pull/152))
|
||||||
|
* Improved error messages when malformed CSV files are found in a Rojo project.
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 8](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.8) (March 29, 2019)
|
||||||
|
* Added support for a bunch of new types when dealing with XML model/place files:
|
||||||
|
* `ColorSequence`
|
||||||
|
* `Float64`
|
||||||
|
* `Int64`
|
||||||
|
* `NumberRange`
|
||||||
|
* `NumberSequence`
|
||||||
|
* `PhysicalProperties`
|
||||||
|
* `Ray`
|
||||||
|
* `Rect`
|
||||||
|
* `Ref`
|
||||||
|
* Improved server instance ordering behavior when files are added during a live session ([#135](https://github.com/rojo-rbx/rojo/pull/135))
|
||||||
|
* Fixed error being thrown when trying to unload the Rojo plugin.
|
||||||
|
* Added partial fix for [issue #141](https://github.com/rojo-rbx/rojo/issues/141) for `Lighting.Technology`, which should restore live sync functionality for the default project file.
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 6](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.6) (March 19, 2019)
|
||||||
|
* Fixed `rojo init` giving unexpected results by upgrading to `rbx_dom_weak` 1.1.0
|
||||||
|
* Fixed live server not responding when the Rojo plugin is connected ([#133](https://github.com/rojo-rbx/rojo/issues/133))
|
||||||
|
* Updated default place file:
|
||||||
|
* Improved default properties to be closer to Studio's built-in 'Baseplate' template
|
||||||
|
* Added a baseplate to the project file (Thanks, [@AmaranthineCodices](https://github.com/AmaranthineCodices/)!)
|
||||||
|
* Added more type support to Rojo plugin
|
||||||
|
* Fixed some cases where the Rojo plugin would leave around objects that it knows should be deleted
|
||||||
|
* Updated plugin to correctly listen to `Plugin.Unloading` when installing or uninstalling new plugins
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 5](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.5) (March 1, 2019)
|
||||||
|
* Upgraded core dependencies, which improves compatibility for lots of instance types
|
||||||
|
* Upgraded from `rbx_tree` 0.2.0 to `rbx_dom_weak` 1.0.0
|
||||||
|
* Upgraded from `rbx_xml` 0.2.0 to `rbx_xml` 0.4.0
|
||||||
|
* Upgraded from `rbx_binary` 0.2.0 to `rbx_binary` 0.4.0
|
||||||
|
* Added support for non-primitive types in the Rojo plugin.
|
||||||
|
* Types like `Color3` and `CFrame` can now be updated live!
|
||||||
|
* Fixed plugin assets flashing in on first load ([#121](https://github.com/rojo-rbx/rojo/issues/121))
|
||||||
|
* Changed Rojo's HTTP server from Rouille to Hyper, which reduced the release size by around a megabyte.
|
||||||
|
* Added property type inference to projects, which makes specifying services a lot easier ([#130](https://github.com/rojo-rbx/rojo/pull/130))
|
||||||
|
* Made error messages from invalid and missing files more user-friendly
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 4](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.4) (February 8, 2019)
|
||||||
|
* Added support for nested partitions ([#102](https://github.com/rojo-rbx/rojo/issues/102))
|
||||||
|
* Added support for 'transmuting' partitions ([#112](https://github.com/rojo-rbx/rojo/issues/112))
|
||||||
|
* Added support for aliasing filesystem paths ([#105](https://github.com/rojo-rbx/rojo/issues/105))
|
||||||
|
* Changed Windows builds to statically link the CRT ([#89](https://github.com/rojo-rbx/rojo/issues/89))
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 3](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.3) (February 1, 2019)
|
||||||
|
* Changed default project file name from `roblox-project.json` to `default.project.json` ([#120](https://github.com/rojo-rbx/rojo/pull/120))
|
||||||
|
* The old file name will still be supported until 0.5.0 is fully released.
|
||||||
|
* Added warning when loading project files that don't end in `.project.json`
|
||||||
|
* This new extension enables Rojo to distinguish project files from random JSON files, which is necessary to support nested projects.
|
||||||
|
* Added new (empty) diagnostic page served from the server
|
||||||
|
* Added better error messages for when a file is missing that's referenced by a Rojo project
|
||||||
|
* Added support for visualization endpoints returning GraphViz source when Dot is not available
|
||||||
|
* Fixed an in-memory filesystem regression introduced recently ([#119](https://github.com/rojo-rbx/rojo/pull/119))
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 2](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.2) (January 28, 2019)
|
||||||
|
* Added support for `.model.json` files, compatible with 0.4.x
|
||||||
|
* Fixed in-memory filesystem not handling out-of-order filesystem change events
|
||||||
|
* Fixed long-polling error caused by a promise mixup ([#110](https://github.com/rojo-rbx/rojo/issues/110))
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 1](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.1) (January 25, 2019)
|
||||||
|
* Changed plugin UI to be way prettier
|
||||||
|
* Thanks to [Reselim](https://github.com/Reselim) for the design!
|
||||||
|
* Changed plugin error messages to be a little more useful
|
||||||
|
* Removed unused 'Config' button in plugin UI
|
||||||
|
* Fixed bug where bad server responses could cause the plugin to be in a bad state
|
||||||
|
* Upgraded to rbx\_tree, rbx\_xml, and rbx\_binary 0.2.0, which dramatically expands the kinds of properties that Rojo can handle, especially in XML.
|
||||||
|
|
||||||
|
## [0.5.0 Alpha 0](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.0) (January 14, 2019)
|
||||||
|
* "Epiphany" rewrite, in progress since the beginning of time
|
||||||
|
* New live sync protocol
|
||||||
|
* Uses HTTP long polling to reduce request count and improve responsiveness
|
||||||
|
* New project format
|
||||||
|
* Hierarchical, preventing overlapping partitions
|
||||||
|
* Added `rojo build` command
|
||||||
|
* Generates `rbxm`, `rbxmx`, `rbxl`, or `rbxlx` files out of your project
|
||||||
|
* Usage: `rojo build <PROJECT> --output <OUTPUT>.rbxm`
|
||||||
|
* Added `rojo upload` command
|
||||||
|
* Generates and uploads a place or model to roblox.com out of your project
|
||||||
|
* Usage: `rojo upload <PROJECT> --cookie "<ROBLOSECURITY>" --asset_id <PLACE_ID>`
|
||||||
|
* New plugin
|
||||||
|
* Only one button now, "Connect"
|
||||||
|
* New UI to pick server address and port
|
||||||
|
* Better error reporting
|
||||||
|
* Added support for `.csv` files turning into `LocalizationTable` instances
|
||||||
|
* Added support for `.txt` files turning into `StringValue` instances
|
||||||
|
* Added debug visualization code to diagnose problems
|
||||||
|
* `/visualize/rbx` and `/visualize/imfs` show instance and file state respectively; they require GraphViz to be installed on your machine.
|
||||||
|
* Added optional place ID restrictions to project files
|
||||||
|
* This helps prevent syncing in content to the wrong place
|
||||||
|
* Multiple places can be specified, like when building a multi-place game
|
||||||
|
* Added support for specifying properties on services in project files
|
||||||
|
|
||||||
|
## [0.4.13](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.13) (November 12, 2018)
|
||||||
|
* When `rojo.json` points to a file or directory that does not exist, Rojo now issues a warning instead of throwing an error and exiting
|
||||||
|
|
||||||
|
## [0.4.12](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.12) (June 21, 2018)
|
||||||
|
* Fixed obscure assertion failure when renaming or deleting files ([#78](https://github.com/rojo-rbx/rojo/issues/78))
|
||||||
|
* Added a `PluginAction` for the sync in command, which should help with some automation scripts ([#80](https://github.com/rojo-rbx/rojo/pull/80))
|
||||||
|
|
||||||
|
## [0.4.11](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.11) (June 10, 2018)
|
||||||
|
* Defensively insert existing instances into RouteMap; should fix most duplication cases when syncing into existing trees.
|
||||||
|
* Fixed incorrect synchronization from `Plugin:_pull` that would cause polling to create issues
|
||||||
|
* Fixed incorrect file routes being assigned to `init.lua` and `init.model.json` files
|
||||||
|
* Untangled route handling-internals slightly
|
||||||
|
|
||||||
|
## [0.4.10](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.10) (June 2, 2018)
|
||||||
|
* Added support for `init.model.json` files, which enable versioning `Tool` instances (among other things) with Rojo. ([#66](https://github.com/rojo-rbx/rojo/issues/66))
|
||||||
|
* Fixed obscure error when syncing into an invalid service.
|
||||||
|
* Fixed multiple sync processes occurring when a server ID mismatch is detected.
|
||||||
|
|
||||||
|
## [0.4.9](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.9) (May 26, 2018)
|
||||||
|
* Fixed warning when renaming or removing files that would sometimes corrupt the instance cache ([#72](https://github.com/rojo-rbx/rojo/pull/72))
|
||||||
|
* JSON models are no longer as strict -- `Children` and `Properties` are now optional.
|
||||||
|
|
||||||
|
## [0.4.8](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.8) (May 26, 2018)
|
||||||
|
* Hotfix to prevent errors from being thrown when objects managed by Rojo are deleted
|
||||||
|
|
||||||
|
## [0.4.7](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.7) (May 25, 2018)
|
||||||
|
* Added icons to the Rojo plugin, made by [@Vorlias](https://github.com/Vorlias)! ([#70](https://github.com/rojo-rbx/rojo/pull/70))
|
||||||
|
* Server will now issue a warning if no partitions are specified in `rojo serve` ([#40](https://github.com/rojo-rbx/rojo/issues/40))
|
||||||
|
|
||||||
|
## [0.4.6](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.6) (May 21, 2018)
|
||||||
|
* Rojo handles being restarted by Roblox Studio more gracefully ([#67](https://github.com/rojo-rbx/rojo/issues/67))
|
||||||
|
* Folders should no longer get collapsed when syncing occurs.
|
||||||
|
* **Significant** robustness improvements with regards to caching.
|
||||||
|
* **This should catch all existing script duplication bugs.**
|
||||||
|
* If there are any bugs with script duplication or caching in the future, restarting the Rojo server process will fix them for that session.
|
||||||
|
* Fixed message in plugin not being prefixed with `Rojo: `.
|
||||||
|
|
||||||
|
## [0.4.5](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.5) (May 1, 2018)
|
||||||
|
* Rojo messages are now prefixed with `Rojo: ` to make them stand out in the output more.
|
||||||
|
* Fixed server to notice file changes *much* more quickly. (200ms vs 1000ms)
|
||||||
|
* Server now lists name of project when starting up.
|
||||||
|
* Rojo now throws an error if no project file is found. ([#63](https://github.com/rojo-rbx/rojo/issues/63))
|
||||||
|
* Fixed multiple sync operations occuring at the same time. ([#61](https://github.com/rojo-rbx/rojo/issues/61))
|
||||||
|
* Partitions targeting files directly now work as expected. ([#57](https://github.com/rojo-rbx/rojo/issues/57))
|
||||||
|
|
||||||
|
## [0.4.4](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.4) (April 7, 2018)
|
||||||
|
* Fix small regression introduced in 0.4.3
|
||||||
|
|
||||||
|
## [0.4.3](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.3) (April 7, 2018)
|
||||||
|
* Plugin now automatically selects `HttpService` if it determines that HTTP isn't enabled ([#58](https://github.com/rojo-rbx/rojo/pull/58))
|
||||||
|
* Plugin now has much more robust handling and will wipe all state when the server changes.
|
||||||
|
* This should fix issues that would otherwise be solved by restarting Roblox Studio.
|
||||||
|
|
||||||
|
## [0.4.2](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.2) (April 4, 2018)
|
||||||
|
* Fixed final case of duplicated instance insertion, caused by reconciled instances not being inserted into `RouteMap`.
|
||||||
|
* The reconciler is still not a perfect solution, especially if script instances get moved around without being destroyed. I don't think this can be fixed before a big refactor.
|
||||||
|
|
||||||
|
## [0.4.1](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.1) (April 1, 2018)
|
||||||
|
* Merged plugin repository into main Rojo repository for easier tracking.
|
||||||
|
* Improved `RouteMap` object tracking; this should fix some cases of duplicated instances being synced into the tree.
|
||||||
|
|
||||||
|
## [0.4.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.0) (March 27, 2018)
|
||||||
|
* Protocol version 1, which shifts more responsibility onto the server
|
||||||
|
* This is a **major breaking** change!
|
||||||
|
* The server now has a content of 'filter plugins', which transform data at various stages in the pipeline
|
||||||
|
* The server now exposes Roblox instance objects instead of file contents, which lines up with how `rojo pack` will work, and paves the way for more robust syncing.
|
||||||
|
* Added `*.model.json` files, which let you embed small Roblox objects into your Rojo tree.
|
||||||
|
* Improved error messages in some cases ([#46](https://github.com/rojo-rbx/rojo/issues/46))
|
||||||
|
|
||||||
|
## [0.3.2](https://github.com/rojo-rbx/rojo/releases/tag/v0.3.2) (December 20, 2017)
|
||||||
|
* Fixed `rojo serve` failing to correctly construct an absolute root path when passed as an argument
|
||||||
|
* Fixed intense CPU usage when running `rojo serve`
|
||||||
|
|
||||||
|
## [0.3.1](https://github.com/rojo-rbx/rojo/releases/tag/v0.3.1) (December 14, 2017)
|
||||||
|
* Improved error reporting when invalid JSON is found in a `rojo.json` project
|
||||||
|
* These messages are passed on from Serde
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.3.0) (December 12, 2017)
|
||||||
|
* Factored out the plugin into a separate repository
|
||||||
|
* Fixed server when using a file as a partition
|
||||||
|
* Previously, trailing slashes were put on the end of a partition even if the read request was an empty string. This broke file reading on Windows when a partition pointed to a file instead of a directory!
|
||||||
|
* Started running automatic tests on Travis CI (#9)
|
||||||
|
|
||||||
|
## [0.2.3](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.3) (December 4, 2017)
|
||||||
|
* Plugin only release
|
||||||
|
* Tightened `init` file rules to only match script files
|
||||||
|
* Previously, Rojo would sometimes pick up the wrong file when syncing
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.2) (December 1, 2017)
|
||||||
|
* Plugin only release
|
||||||
|
* Fixed broken reconciliation behavior with `init` files
|
||||||
|
|
||||||
|
## [0.2.1](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.1) (December 1, 2017)
|
||||||
|
* Plugin only release
|
||||||
|
* Changes default port to 8000
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.0) (December 1, 2017)
|
||||||
|
* Support for `init.lua` like rbxfs and rbxpacker
|
||||||
|
* More robust syncing with a new reconciler
|
||||||
|
|
||||||
|
## [0.1.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.1.0) (November 29, 2017)
|
||||||
|
* Initial release, functionally very similar to [rbxfs](https://github.com/LPGhatguy/rbxfs)
|
||||||
37
CHANGES.md
@@ -1,37 +0,0 @@
|
|||||||
# Rojo Change Log
|
|
||||||
|
|
||||||
## Current Master
|
|
||||||
* No changes
|
|
||||||
|
|
||||||
## 0.3.2
|
|
||||||
* Fixed `rojo serve` failing to correctly construct an absolute root path when passed as an argument
|
|
||||||
|
|
||||||
## 0.3.1
|
|
||||||
* Improved error reporting when invalid JSON is found in a `rojo.json` project
|
|
||||||
* These messages are passed on from Serde
|
|
||||||
|
|
||||||
## 0.3.0
|
|
||||||
* Factored out the plugin into a separate repository
|
|
||||||
* Fixed server when using a file as a partition
|
|
||||||
* Previously, trailing slashes were put on the end of a partition even if the read request was an empty string. This broke file reading on Windows when a partition pointed to a file instead of a directory!
|
|
||||||
* Started running automatic tests on Travis CI (#9)
|
|
||||||
|
|
||||||
## 0.2.3
|
|
||||||
* Plugin only release
|
|
||||||
* Tightened `init` file rules to only match script files
|
|
||||||
* Previously, Rojo would sometimes pick up the wrong file when syncing
|
|
||||||
|
|
||||||
## 0.2.2
|
|
||||||
* Plugin only release
|
|
||||||
* Fixed broken reconciliation behavior with `init` files
|
|
||||||
|
|
||||||
## 0.2.1
|
|
||||||
* Plugin only release
|
|
||||||
* Changes default port to 8000
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
* Support for `init.lua` like rbxfs and rbxpacker
|
|
||||||
* More robust syncing with a new reconciler
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
* Initial release, functionally very similar to [rbxfs](https://github.com/LPGhatguy/rbxfs)
|
|
||||||
57
CONTRIBUTING.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Contributing to the Rojo Project
|
||||||
|
Rojo is a big project and can always use more help!
|
||||||
|
|
||||||
|
Some of the repositories covered are:
|
||||||
|
|
||||||
|
* https://github.com/rojo-rbx/rojo
|
||||||
|
* https://github.com/rojo-rbx/rbx-dom
|
||||||
|
* https://github.com/rojo-rbx/vscode-rojo
|
||||||
|
* https://github.com/rojo-rbx/rbxlx-to-rojo
|
||||||
|
|
||||||
|
## Code
|
||||||
|
Code contributions are welcome for features and bugs that have been reported in the project's bug tracker. We want to make sure that no one wastes their time, so be sure to talk with maintainers about what changes would be accepted before doing any work!
|
||||||
|
|
||||||
|
You'll want these tools to work on Rojo:
|
||||||
|
|
||||||
|
* Latest stable Rust compiler
|
||||||
|
* Latest stable [Rojo](https://github.com/rojo-rbx/rojo)
|
||||||
|
* [Foreman](https://github.com/Roblox/foreman)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
Documentation impacts way more people than the individual lines of code we write.
|
||||||
|
|
||||||
|
If you find any problems in documentation, including typos, bad grammar, misleading phrasing, or missing content, feel free to file issues and pull requests to fix them.
|
||||||
|
|
||||||
|
## Bug Reports and Feature Requests
|
||||||
|
Most of the tools around Rojo try to be clear when an issue is a bug. Even if they aren't, sometimes things don't work quite right.
|
||||||
|
|
||||||
|
Sometimes there's something that Rojo doesn't do that it probably should.
|
||||||
|
|
||||||
|
Please file issues and we'll try to help figure out what the best way forward is.
|
||||||
|
|
||||||
|
## Local Development Gotchas
|
||||||
|
|
||||||
|
If your build fails with "Error: failed to open file `D:\code\rojo\plugin\modules\roact\src`" you need to update your Git submodules.
|
||||||
|
Run the command and try building again: `git submodule update --init --recursive`.
|
||||||
|
|
||||||
|
## Pushing a Rojo Release
|
||||||
|
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
||||||
|
|
||||||
|
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
||||||
|
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
||||||
|
3. Run `cargo test` to update `Cargo.lock` and run tests
|
||||||
|
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
|
5. Commit!
|
||||||
|
* `git add . && git commit -m "Release vX.Y.Z"`
|
||||||
|
6. Tag the commit
|
||||||
|
* `git tag vX.Y.Z`
|
||||||
|
7. Publish the CLI
|
||||||
|
* `cargo publish`
|
||||||
|
8. Publish the Plugin
|
||||||
|
* `cargo run -- upload plugin --asset_id 6415005344`
|
||||||
|
9. Push commits and tags
|
||||||
|
* `git push && git push --tags`
|
||||||
|
10. Copy GitHub release content from previous release
|
||||||
|
* Update the leading text with a summary about the release
|
||||||
|
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
|
* Write a small summary of each major feature
|
||||||
2917
Cargo.lock
generated
123
Cargo.toml
@@ -1,20 +1,115 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "0.3.2"
|
version = "7.4.3"
|
||||||
|
rust-version = "1.70.0"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
description = "A tool to create robust Roblox projects"
|
description = "Enables professional-grade development tools for Roblox developers"
|
||||||
license = "MIT"
|
license = "MPL-2.0"
|
||||||
repository = "https://github.com/LPGhatguy/rojo"
|
homepage = "https://rojo.space"
|
||||||
|
documentation = "https://rojo.space/docs"
|
||||||
|
repository = "https://github.com/rojo-rbx/rojo"
|
||||||
|
readme = "README.md"
|
||||||
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
[[bin]]
|
exclude = ["/test-projects/**"]
|
||||||
name = "rojo"
|
|
||||||
path = "src/bin.rs"
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
# Enable this feature to live-reload assets from the web UI.
|
||||||
|
dev_live_assets = []
|
||||||
|
|
||||||
|
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client"]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["crates/*"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "librojo"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "build"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.27.1"
|
memofs = { version = "0.3.0", path = "crates/memofs" }
|
||||||
rouille = "1.0"
|
|
||||||
serde = "1.0"
|
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
||||||
serde_derive = "1.0"
|
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
||||||
serde_json = "1.0"
|
# rbx_dom_weak = { path = "../rbx-dom/rbx_dom_weak" }
|
||||||
notify = "4.0.0"
|
# rbx_reflection = { path = "../rbx-dom/rbx_reflection" }
|
||||||
rand = "0.3"
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
|
rbx_binary = "0.7.6"
|
||||||
|
rbx_dom_weak = "2.8.0"
|
||||||
|
rbx_reflection = "4.6.0"
|
||||||
|
rbx_reflection_database = "0.2.11"
|
||||||
|
rbx_xml = "0.13.4"
|
||||||
|
|
||||||
|
anyhow = "1.0.44"
|
||||||
|
backtrace = "0.3.61"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
crossbeam-channel = "0.5.1"
|
||||||
|
csv = "1.1.6"
|
||||||
|
env_logger = "0.9.0"
|
||||||
|
fs-err = "2.6.0"
|
||||||
|
futures = "0.3.17"
|
||||||
|
globset = "0.4.8"
|
||||||
|
humantime = "2.1.0"
|
||||||
|
hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
|
||||||
|
jod-thread = "0.1.2"
|
||||||
|
log = "0.4.14"
|
||||||
|
maplit = "1.0.2"
|
||||||
|
num_cpus = "1.15.0"
|
||||||
|
opener = "0.5.0"
|
||||||
|
rayon = "1.7.0"
|
||||||
|
reqwest = { version = "0.11.10", features = [
|
||||||
|
"blocking",
|
||||||
|
"json",
|
||||||
|
"native-tls-vendored",
|
||||||
|
] }
|
||||||
|
ritz = "0.1.0"
|
||||||
|
roblox_install = "1.0.0"
|
||||||
|
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
||||||
|
serde_json = "1.0.68"
|
||||||
|
toml = "0.5.9"
|
||||||
|
termcolor = "1.1.2"
|
||||||
|
thiserror = "1.0.30"
|
||||||
|
tokio = { version = "1.12.0", features = ["rt", "rt-multi-thread"] }
|
||||||
|
uuid = { version = "1.0.0", features = ["v4", "serde"] }
|
||||||
|
clap = { version = "3.1.18", features = ["derive"] }
|
||||||
|
profiling = "1.0.6"
|
||||||
|
tracy-client = { version = "0.13.2", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winreg = "0.10.1"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
memofs = { version = "0.3.0", path = "crates/memofs" }
|
||||||
|
|
||||||
|
embed-resource = "1.6.4"
|
||||||
|
anyhow = "1.0.44"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
fs-err = "2.6.0"
|
||||||
|
maplit = "1.0.2"
|
||||||
|
semver = "1.0.19"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rojo-insta-ext = { path = "crates/rojo-insta-ext" }
|
||||||
|
|
||||||
|
criterion = "0.3.5"
|
||||||
|
insta = { version = "1.8.0", features = ["redactions", "yaml"] }
|
||||||
|
paste = "1.0.5"
|
||||||
|
pretty_assertions = "1.2.1"
|
||||||
|
serde_yaml = "0.8.21"
|
||||||
|
tempfile = "3.2.0"
|
||||||
|
walkdir = "2.3.2"
|
||||||
|
|||||||
373
LICENSE.txt
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
153
README.md
@@ -1,143 +1,46 @@
|
|||||||
<h1 align="center">Rojo</h1>
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://travis-ci.org/LPGhatguy/rojo">
|
<a href="https://rojo.space"><img src="assets/logo-512.png" alt="Rojo" height="217" /></a>
|
||||||
<img src="https://api.travis-ci.org/LPGhatguy/rojo.svg?branch=master" alt="Travis-CI Build Status" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div> </div>
|
<div> </div>
|
||||||
|
|
||||||
Rojo is a flexible multi-tool designed for creating robust Roblox projects. It's in early development, but is still useful for many projects.
|
<div align="center">
|
||||||
|
<a href="https://github.com/rojo-rbx/rojo/actions"><img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" /></a>
|
||||||
|
<a href="https://crates.io/crates/rojo"><img src="https://img.shields.io/crates/v/rojo.svg?label=latest%20release" alt="Latest server version" /></a>
|
||||||
|
<a href="https://rojo.space/docs"><img src="https://img.shields.io/badge/docs-website-brightgreen.svg" alt="Rojo Documentation" /></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
It's designed for power users who want to use the **best tools available** for building games, libraries, and plugins.
|
<hr />
|
||||||
|
|
||||||
This is the main Rojo repository, containing the binary server component. For the source for the Roblox plugin, [see the rojo-plugin repository](https://github.com/LPGhatguy/rojo-plugin).
|
**Rojo** is a tool designed to enable Roblox developers to use professional-grade software engineering tools.
|
||||||
|
|
||||||
|
With Rojo, it's possible to use industry-leading tools like **Visual Studio Code** and **Git**.
|
||||||
|
|
||||||
|
Rojo is designed for power users who want to use the best tools available for building games, libraries, and plugins.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
Rojo enables:
|
||||||
|
|
||||||
Rojo has a number of desirable features *right now*:
|
* Working on scripts and models from the filesystem, in your favorite editor
|
||||||
|
* Versioning your game, library, or plugin using Git or another VCS
|
||||||
|
* Streaming `rbxmx` and `rbxm` models into your game in real time
|
||||||
|
* Packaging and deploying your project to Roblox.com from the command line
|
||||||
|
|
||||||
* Work on scripts from the filesystem, in your favorite editor
|
In the future, Rojo will be able to:
|
||||||
* Version your place, library, or plugin using Git or another VCS
|
|
||||||
|
|
||||||
Soon, Rojo will be able to:
|
* Sync instances from Roblox Studio to the filesystem
|
||||||
|
* Automatically convert your existing game to work with Rojo
|
||||||
|
* Import custom instances like MoonScript code
|
||||||
|
|
||||||
* Sync Roblox objects (including models) bi-directionally between the filesystem and Roblox Studio
|
## [Documentation](https://rojo.space/docs)
|
||||||
* Create installation scripts for libraries to be used in standalone places
|
Documentation is hosted in the [rojo.space repository](https://github.com/rojo-rbx/rojo.space).
|
||||||
* Similar to [rbxpacker](https://github.com/LPGhatguy/rbxpacker), another one of my projects
|
|
||||||
* Add strongly-versioned dependencies to your project
|
|
||||||
|
|
||||||
## Installation
|
## Contributing
|
||||||
Rojo has two components:
|
Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions for helping work on Rojo!
|
||||||
* The command line tool, written in Rust
|
|
||||||
* The [Roblox Studio plugin](https://www.roblox.com/library/1211549683/Rojo), written in Lua
|
|
||||||
|
|
||||||
To install the command line tool, there are two options:
|
Pull requests are welcome!
|
||||||
* Cargo, if you have Rust installed
|
|
||||||
* Use `cargo install rojo` -- Rojo will be available with the `rojo` command
|
|
||||||
* Download a pre-built Windows binary from [the GitHub releases page](https://github.com/LPGhatguy/rojo/releases)
|
|
||||||
|
|
||||||
## Usage
|
Rojo supports Rust 1.70.0 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
||||||
For more help, use `rojo help`.
|
|
||||||
|
|
||||||
### New Project
|
|
||||||
Just create a new folder and tell Rojo to initialize it!
|
|
||||||
|
|
||||||
```sh
|
|
||||||
mkdir my-new-project
|
|
||||||
cd my-new-project
|
|
||||||
|
|
||||||
rojo init
|
|
||||||
```
|
|
||||||
|
|
||||||
Rojo will create an empty project in the directory.
|
|
||||||
|
|
||||||
The default project looks like this:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "my-new-project",
|
|
||||||
"servePort": 8000,
|
|
||||||
"partitions": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Start Dev Server
|
|
||||||
To create a server that allows the Rojo Dev Plugin to access your project, use:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
rojo serve
|
|
||||||
```
|
|
||||||
|
|
||||||
The tool will tell you whether it found an existing project. You should then be able to connect and use the project from within Roblox Studio!
|
|
||||||
|
|
||||||
### Migrating an Existing Roblox Project
|
|
||||||
**Coming soon!**
|
|
||||||
|
|
||||||
### Syncing into Roblox
|
|
||||||
In order to sync code into Roblox, you'll need to add one or more "partitions" to your configuration. A partition tells Rojo how to map directories to Roblox objects.
|
|
||||||
|
|
||||||
Each entry in the partitions table has a unique name, a filesystem path, and the full name of the Roblox object to sync into.
|
|
||||||
|
|
||||||
For example, if you want to map your `src` directory to an object named `My Cool Game` in `ReplicatedStorage`, you could use this configuration:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "rojo",
|
|
||||||
"servePort": 8000,
|
|
||||||
"partitions": {
|
|
||||||
"game": {
|
|
||||||
"path": "src",
|
|
||||||
"target": "ReplicatedStorage.My Cool Game"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `path` parameter is relative to the project file.
|
|
||||||
|
|
||||||
The `target` starts at `game` and crawls down the tree. If any objects don't exist along the way, they'll be created as `Folder` instances.
|
|
||||||
|
|
||||||
Run `rojo serve` in the directory containing this project, then press the "Sync In" or "Toggle Polling" buttons in the Roblox Studio plugin to move code into your game.
|
|
||||||
|
|
||||||
### Sync Details
|
|
||||||
The structure of files and folders on the filesystem are preserved when syncing into game.
|
|
||||||
|
|
||||||
Creation of Roblox instances follows a simple set of rules. The first rule that matches the file name is chosen:
|
|
||||||
|
|
||||||
| File Name | Instance Type | Notes |
|
|
||||||
| -------------- | -------------- | ----------------------------------------- |
|
|
||||||
| `*.server.lua` | `Script` | `Source` will contain the file's contents |
|
|
||||||
| `*.client.lua` | `LocalScript` | `Source` will contain the file's contents |
|
|
||||||
| `*.lua` | `ModuleScript` | `Source` will contain the file's contents |
|
|
||||||
| `*` | `StringValue` | `Value` will contain the file's contents |
|
|
||||||
|
|
||||||
Any folders on the filesystem will turn into `Folder` objects unless they contain a file named `init.lua`, `init.server.lua`, or `init.client.lua`. Following the convention of Lua, those objects will instead be whatever the `init` file would turn into.
|
|
||||||
|
|
||||||
For example, this file tree:
|
|
||||||
|
|
||||||
* my-game
|
|
||||||
* init.client.lua
|
|
||||||
* foo.lua
|
|
||||||
|
|
||||||
Will turn into this tree in Roblox:
|
|
||||||
|
|
||||||
* `my-game` (`LocalScript` with source from `my-game/init.client.lua`)
|
|
||||||
* `foo` (`ModuleScript` with source from `my-game/foo.lua`)
|
|
||||||
|
|
||||||
## Inspiration
|
|
||||||
There are lots of other tools that sync scripts into Roblox, or otherwise work to improve the development flow outside of Roblox Studio.
|
|
||||||
|
|
||||||
Here are a few, if you're looking for alternatives or supplements to Rojo:
|
|
||||||
* [Studio Bridge by Vocksel](https://github.com/vocksel/studio-bridge)
|
|
||||||
* [RbxRefresh by Osyris](https://github.com/osyrisrblx/RbxRefresh)
|
|
||||||
* [RbxSync by evaera](https://github.com/evaera/RbxSync)
|
|
||||||
* [CodeSync](https://github.com/MemoryPenguin/CodeSync) and [rbx-exteditor](https://github.com/MemoryPenguin/rbx-exteditor) by [MemoryPenguin](https://github.com/MemoryPenguin)
|
|
||||||
* [rbxmk by Anaminus](https://github.com/anaminus/rbxmk)
|
|
||||||
|
|
||||||
I also have a couple tools that Rojo intends to replace:
|
|
||||||
* [rbxfs](https://github.com/LPGhatguy/rbxfs), which has been deprecated by Rojo
|
|
||||||
* [rbxpacker](https://github.com/LPGhatguy/rbxpacker), which is still useful
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Rojo is available under the terms of the MIT license. See [LICENSE.md](LICENSE.md) for details.
|
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
||||||
5
aftman.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[tools]
|
||||||
|
rojo = "rojo-rbx/rojo@7.4.1"
|
||||||
|
selene = "Kampfkarren/selene@0.26.1"
|
||||||
|
stylua = "JohnnyMorganz/stylua@0.18.2"
|
||||||
|
run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0"
|
||||||
BIN
assets/NotificationPop.mp3
Normal file
11
assets/default-model-project/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# {project_name}
|
||||||
|
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
To build this library, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -o "{project_name}.rbxmx"
|
||||||
|
```
|
||||||
|
|
||||||
|
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||||
6
assets/default-model-project/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "{project_name}",
|
||||||
|
"tree": {
|
||||||
|
"$path": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
assets/default-model-project/gitignore.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Roblox Studio lock files
|
||||||
|
/*.rbxlx.lock
|
||||||
|
/*.rbxl.lock
|
||||||
5
assets/default-model-project/src-init.luau
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
return {
|
||||||
|
hello = function()
|
||||||
|
print("Hello world, from {project_name}!")
|
||||||
|
end,
|
||||||
|
}
|
||||||
17
assets/default-place-project/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# {project_name}
|
||||||
|
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
To build the place from scratch, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -o "{project_name}.rbxlx"
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, open `{project_name}.rbxlx` in Roblox Studio and start the Rojo server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo serve
|
||||||
|
```
|
||||||
|
|
||||||
|
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||||
72
assets/default-place-project/default.project.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"name": "{project_name}",
|
||||||
|
"tree": {
|
||||||
|
"$className": "DataModel",
|
||||||
|
|
||||||
|
"ReplicatedStorage": {
|
||||||
|
"Shared": {
|
||||||
|
"$path": "src/shared"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ServerScriptService": {
|
||||||
|
"Server": {
|
||||||
|
"$path": "src/server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"StarterPlayer": {
|
||||||
|
"StarterPlayerScripts": {
|
||||||
|
"Client": {
|
||||||
|
"$path": "src/client"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"Workspace": {
|
||||||
|
"$properties": {
|
||||||
|
"FilteringEnabled": true
|
||||||
|
},
|
||||||
|
"Baseplate": {
|
||||||
|
"$className": "Part",
|
||||||
|
"$properties": {
|
||||||
|
"Anchored": true,
|
||||||
|
"Color": [
|
||||||
|
0.38823,
|
||||||
|
0.37254,
|
||||||
|
0.38823
|
||||||
|
],
|
||||||
|
"Locked": true,
|
||||||
|
"Position": [
|
||||||
|
0,
|
||||||
|
-10,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"Size": [
|
||||||
|
512,
|
||||||
|
20,
|
||||||
|
512
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Lighting": {
|
||||||
|
"$properties": {
|
||||||
|
"Ambient": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"Brightness": 2,
|
||||||
|
"GlobalShadows": true,
|
||||||
|
"Outlines": false,
|
||||||
|
"Technology": "Voxel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SoundService": {
|
||||||
|
"$properties": {
|
||||||
|
"RespectFilteringEnabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
assets/default-place-project/gitignore.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Project place file
|
||||||
|
/{project_name}.rbxlx
|
||||||
|
|
||||||
|
# Roblox Studio lock files
|
||||||
|
/*.rbxlx.lock
|
||||||
|
/*.rbxl.lock
|
||||||
17
assets/default-plugin-project/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# {project_name}
|
||||||
|
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
To build this plugin to your local plugins folder, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -p "{project_name}.rbxm"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can include the `watch` flag to re-build it on save:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -p "{project_name}.rbxm" --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||||
6
assets/default-plugin-project/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "{project_name}",
|
||||||
|
"tree": {
|
||||||
|
"$path": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
assets/default-plugin-project/gitignore.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Plugin model files
|
||||||
|
/{project_name}.rbxmx
|
||||||
|
/{project_name}.rbxm
|
||||||
BIN
assets/icon-32.png
Normal file
|
After Width: | Height: | Size: 975 B |
BIN
assets/icon-link-32.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/icon-warn-32.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/images/back.png
Normal file
|
After Width: | Height: | Size: 229 B |
5
assets/images/back.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<path d="M20,11L20,13L8,13L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11L20,11Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 584 B |
BIN
assets/images/checkbox-off.png
Normal file
|
After Width: | Height: | Size: 269 B |
15
assets/images/checkbox-off.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(1,0,0,1,-54,-1)">
|
||||||
|
<g id="Artboard3" transform="matrix(1.77778,0,0,1.45455,-42,-0.454545)">
|
||||||
|
<rect x="54" y="1" width="9" height="11" style="fill:none;"/>
|
||||||
|
<g transform="matrix(3.375,0,0,4.125,-3654,-2753.12)">
|
||||||
|
<path d="M1099,670L1101,668" style="fill:none;stroke:white;stroke-width:0.5px;"/>
|
||||||
|
<g transform="matrix(-1,0,0,1,2200,0)">
|
||||||
|
<path d="M1099,670L1101,668" style="fill:none;stroke:white;stroke-width:0.5px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/checkbox-on.png
Normal file
|
After Width: | Height: | Size: 249 B |
12
assets/images/checkbox-on.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(1,0,0,1,-32,0)">
|
||||||
|
<g id="Artboard2" transform="matrix(0.8,0,0,0.941176,6.4,0)">
|
||||||
|
<rect x="32" y="0" width="20" height="17" style="fill:none;"/>
|
||||||
|
<g transform="matrix(5,0,0,4.25,-5470.5,-2371.5)">
|
||||||
|
<path d="M1101,560L1102,561L1104,559" style="fill:none;stroke:white;stroke-width:0.75px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 870 B |
BIN
assets/images/circle-128.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/images/circle-16.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
assets/images/circle-32.png
Normal file
|
After Width: | Height: | Size: 317 B |
BIN
assets/images/circle-500.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
assets/images/circle-64.png
Normal file
|
After Width: | Height: | Size: 613 B |
BIN
assets/images/close.png
Normal file
|
After Width: | Height: | Size: 295 B |
15
assets/images/close.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.0666667,0,0,0.097561,-31,-18.6341)">
|
||||||
|
<rect x="465" y="191" width="360" height="246" style="fill:none;"/>
|
||||||
|
<g transform="matrix(134.328,0,0,102.5,-74228.5,-15214.7)">
|
||||||
|
<g transform="matrix(1.11667,0,0,1,-57.3333,0)">
|
||||||
|
<path d="M551,152L550,151" style="fill:none;stroke:white;stroke-width:0.3px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.11667,0,0,1,-57.3333,0)">
|
||||||
|
<path d="M550,152L551,151" style="fill:none;stroke:white;stroke-width:0.3px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/header-logo.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
21
assets/images/header-logo.svg
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 60 27" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.952381,0,0,0.710526,-228.571,-156.316)">
|
||||||
|
<rect x="240" y="220" width="63" height="38" style="fill:none;"/>
|
||||||
|
<g transform="matrix(0.0789166,0,0,0.105779,211.848,170.749)">
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,376,753)">
|
||||||
|
<path d="M0.302,-0.836L0.306,-0.836L0.302,-0.829L0.302,-0.814C0.333,-0.82 0.349,-0.828 0.349,-0.836L0.371,-0.833C0.408,-0.835 0.441,-0.836 0.472,-0.836L0.476,-0.836C0.524,-0.836 0.58,-0.81 0.646,-0.757C0.66,-0.734 0.668,-0.714 0.672,-0.695L0.672,-0.659C0.664,-0.609 0.65,-0.571 0.632,-0.546C0.621,-0.537 0.597,-0.507 0.56,-0.456L0.556,-0.456L0.556,-0.463L0.566,-0.478L0.56,-0.478C0.474,-0.405 0.378,-0.349 0.27,-0.311C0.243,-0.304 0.216,-0.3 0.19,-0.3C0.224,-0.26 0.287,-0.207 0.378,-0.141C0.387,-0.136 0.445,-0.099 0.552,-0.028C0.641,0.027 0.747,0.087 0.871,0.153L0.871,0.157L0.856,0.157C0.832,0.148 0.82,0.141 0.82,0.138L0.813,0.142L0.806,0.142C0.803,0.142 0.802,0.141 0.802,0.138C0.793,0.14 0.786,0.149 0.78,0.164C0.795,0.168 0.802,0.173 0.802,0.178C0.796,0.183 0.792,0.186 0.788,0.186L0.77,0.182L0.77,0.186C0.77,0.19 0.773,0.193 0.78,0.193L0.78,0.204L0.777,0.204C0.772,0.204 0.717,0.172 0.614,0.109C0.541,0.072 0.451,0.015 0.346,-0.061C0.311,-0.077 0.247,-0.124 0.153,-0.202L0.143,-0.202C0.129,-0.181 0.111,-0.129 0.088,-0.046C0.083,-0.027 0.059,0.001 0.016,0.037L0.008,0.037L0.001,0.026L0.001,0.022C0.001,0.017 0.005,0.006 0.012,-0.01L0.012,-0.014L0.008,-0.014C0.008,-0.007 -0.007,0.004 -0.035,0.019L-0.039,0.019L-0.039,0.012C0.046,-0.24 0.105,-0.404 0.139,-0.481C0.197,-0.614 0.226,-0.69 0.226,-0.709C0.213,-0.709 0.184,-0.693 0.139,-0.659L0.132,-0.659L0.135,-0.666L0.135,-0.673C0.122,-0.673 0.092,-0.652 0.045,-0.608L0.041,-0.608L0.041,-0.612L0.088,-0.666L0.096,-0.677L0.096,-0.681L0.088,-0.681L0.045,-0.645C0.04,-0.645 0.038,-0.647 0.038,-0.651C0.083,-0.701 0.138,-0.744 0.204,-0.778C0.265,-0.794 0.295,-0.812 0.295,-0.833L0.302,-0.836ZM0.632,-0.735L0.632,-0.731C0.632,-0.727 0.635,-0.724 0.639,-0.724L0.639,-0.728C0.639,-0.732 0.637,-0.735 0.632,-0.735ZM0.208,-0.387L0.211,-0.387C0.255,-0.396 0.277,-0.403 0.277,-0.409C0.274,-0.414 0.273,-0.417 0.273,-0.42C0.365,-0.451 0.427,-0.482 0.458,-0.514C0.461,-0.514 0.48,-0.534 0.516,-0.576L0.52,-0.576L0.52,-0.572C0.515,-0.564 0.512,-0.558 0.512,-0.554L0.516,-0.554C0.547,-0.578 0.563,-0.604 0.563,-0.633L0.563,-0.655C0.556,-0.655 0.552,-0.658 0.552,-0.663C0.564,-0.665 0.57,-0.671 0.57,-0.681C0.57,-0.693 0.546,-0.705 0.498,-0.717C0.459,-0.727 0.417,-0.731 0.371,-0.731C0.348,-0.731 0.325,-0.689 0.302,-0.604C0.292,-0.594 0.261,-0.522 0.208,-0.387ZM0.251,-0.695L0.251,-0.691C0.255,-0.691 0.258,-0.695 0.262,-0.702L0.262,-0.706C0.259,-0.706 0.255,-0.702 0.251,-0.695ZM0.255,-0.626L0.259,-0.626C0.266,-0.629 0.27,-0.636 0.27,-0.648C0.266,-0.648 0.261,-0.641 0.255,-0.626ZM0.596,-0.612L0.596,-0.604C0.599,-0.604 0.603,-0.608 0.606,-0.615L0.606,-0.619L0.603,-0.619C0.598,-0.618 0.596,-0.616 0.596,-0.612ZM0.204,-0.369L0.204,-0.365L0.211,-0.365C0.216,-0.368 0.22,-0.369 0.222,-0.369L0.222,-0.365C0.281,-0.376 0.316,-0.388 0.328,-0.401L0.324,-0.401C0.271,-0.389 0.231,-0.378 0.204,-0.369ZM0.19,-0.333L0.193,-0.333L0.259,-0.347L0.255,-0.354C0.212,-0.349 0.19,-0.341 0.19,-0.333ZM0.334,-0.108L0.334,-0.101C0.372,-0.072 0.395,-0.057 0.403,-0.057C0.394,-0.064 0.389,-0.07 0.389,-0.075L0.334,-0.108ZM0.44,-0.028C0.442,-0.019 0.449,-0.014 0.461,-0.014L0.465,-0.014L0.465,-0.018C0.459,-0.018 0.452,-0.021 0.443,-0.028L0.44,-0.028ZM0.675,0.102C0.694,0.119 0.723,0.136 0.762,0.153L0.766,0.146C0.736,0.128 0.708,0.113 0.683,0.102L0.675,0.102ZM0.875,0.16L0.886,0.16C0.89,0.161 0.893,0.163 0.893,0.167C0.888,0.167 0.886,0.171 0.886,0.178L0.878,0.178L0.882,0.171L0.882,0.167L0.875,0.167L0.875,0.16Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,588.398,753)">
|
||||||
|
<path d="M0.631,-0.705C0.646,-0.705 0.661,-0.696 0.673,-0.678L0.646,-0.678C0.62,-0.678 0.587,-0.67 0.546,-0.653L0.546,-0.65L0.549,-0.65C0.607,-0.654 0.649,-0.656 0.677,-0.656L0.683,-0.656L0.683,-0.65L0.67,-0.65C0.668,-0.65 0.667,-0.651 0.667,-0.653L0.503,-0.629C0.503,-0.627 0.493,-0.624 0.473,-0.62L0.473,-0.614L0.519,-0.623L0.521,-0.623L0.521,-0.617C0.386,-0.581 0.318,-0.555 0.318,-0.538C0.252,-0.483 0.204,-0.431 0.175,-0.383C0.131,-0.304 0.108,-0.243 0.108,-0.201C0.108,-0.157 0.131,-0.125 0.175,-0.104C0.183,-0.102 0.19,-0.101 0.196,-0.101C0.286,-0.136 0.353,-0.196 0.397,-0.28C0.409,-0.318 0.419,-0.363 0.427,-0.417L0.434,-0.417L0.427,-0.359C0.433,-0.359 0.439,-0.374 0.443,-0.404C0.441,-0.412 0.439,-0.42 0.439,-0.429L0.439,-0.438L0.446,-0.438C0.448,-0.407 0.449,-0.386 0.449,-0.374C0.449,-0.358 0.444,-0.331 0.434,-0.292C0.445,-0.302 0.458,-0.335 0.473,-0.392C0.48,-0.447 0.486,-0.474 0.491,-0.474C0.491,-0.472 0.492,-0.471 0.494,-0.471C0.492,-0.469 0.49,-0.457 0.488,-0.435L0.488,-0.432L0.491,-0.432C0.499,-0.454 0.504,-0.48 0.506,-0.508C0.502,-0.516 0.5,-0.522 0.5,-0.526L0.506,-0.529C0.512,-0.529 0.518,-0.513 0.525,-0.48C0.525,-0.4 0.491,-0.297 0.424,-0.17C0.372,-0.093 0.305,-0.042 0.224,-0.019C0.188,-0.006 0.154,-0 0.121,-0C0.04,-0.022 -0.001,-0.083 -0.001,-0.183C-0.001,-0.296 0.045,-0.398 0.136,-0.489C0.165,-0.522 0.227,-0.568 0.321,-0.629C0.425,-0.68 0.529,-0.705 0.631,-0.705ZM0.482,-0.656L0.482,-0.653C0.499,-0.653 0.508,-0.652 0.509,-0.65C0.526,-0.659 0.536,-0.663 0.54,-0.663L0.54,-0.668L0.482,-0.656ZM0.099,-0.417C0.144,-0.458 0.179,-0.494 0.206,-0.523C0.23,-0.538 0.242,-0.55 0.242,-0.559C0.201,-0.535 0.156,-0.493 0.108,-0.432C0.102,-0.427 0.099,-0.422 0.099,-0.417ZM0.105,-0.261C0.108,-0.261 0.118,-0.284 0.136,-0.328C0.158,-0.378 0.203,-0.439 0.272,-0.511L0.272,-0.514C0.204,-0.463 0.167,-0.422 0.16,-0.392C0.142,-0.371 0.124,-0.328 0.105,-0.261ZM0.069,-0.334L0.069,-0.332L0.072,-0.332C0.116,-0.393 0.139,-0.429 0.139,-0.438C0.11,-0.413 0.086,-0.378 0.069,-0.334ZM0.467,-0.31L0.467,-0.307L0.47,-0.307C0.492,-0.371 0.503,-0.413 0.503,-0.432C0.49,-0.409 0.478,-0.369 0.467,-0.31ZM0.087,-0.404L0.087,-0.401C0.091,-0.401 0.093,-0.403 0.093,-0.407L0.093,-0.41C0.089,-0.41 0.087,-0.408 0.087,-0.404ZM0.063,-0.368C0.032,-0.297 0.017,-0.245 0.017,-0.213C0.021,-0.148 0.028,-0.107 0.039,-0.088L0.042,-0.088L0.042,-0.094C0.035,-0.107 0.032,-0.129 0.032,-0.158C0.032,-0.191 0.035,-0.227 0.042,-0.268L0.039,-0.273C0.045,-0.3 0.054,-0.331 0.066,-0.365L0.066,-0.368L0.063,-0.368ZM0.096,-0.24L0.096,-0.237C0.1,-0.237 0.103,-0.239 0.103,-0.243L0.103,-0.246C0.098,-0.245 0.096,-0.243 0.096,-0.24ZM0.397,-0.222L0.397,-0.219L0.4,-0.219C0.408,-0.232 0.412,-0.241 0.412,-0.243C0.407,-0.242 0.402,-0.235 0.397,-0.222ZM0.066,-0.24C0.06,-0.222 0.055,-0.202 0.051,-0.179C0.055,-0.138 0.061,-0.108 0.069,-0.088L0.072,-0.088L0.072,-0.094C0.066,-0.121 0.063,-0.139 0.063,-0.146L0.066,-0.152C0.064,-0.155 0.063,-0.16 0.063,-0.164C0.063,-0.169 0.064,-0.173 0.066,-0.176C0.064,-0.18 0.063,-0.183 0.063,-0.186C0.067,-0.218 0.069,-0.235 0.069,-0.237L0.069,-0.24L0.066,-0.24ZM0.294,-0.125L0.294,-0.122C0.3,-0.122 0.322,-0.143 0.357,-0.186L0.357,-0.188L0.354,-0.188C0.314,-0.151 0.294,-0.129 0.294,-0.125ZM0.379,-0.149C0.379,-0.146 0.36,-0.126 0.321,-0.088L0.324,-0.088C0.351,-0.112 0.37,-0.132 0.382,-0.146L0.385,-0.146L0.385,-0.149L0.379,-0.149ZM0.09,-0.104L0.09,-0.101C0.094,-0.08 0.104,-0.07 0.121,-0.07L0.13,-0.07L0.13,-0.073L0.09,-0.104ZM0.151,-0.055L0.188,-0.055L0.203,-0.058L0.203,-0.061L0.157,-0.061C0.153,-0.06 0.151,-0.058 0.151,-0.055ZM0.096,-0.058L0.096,-0.055C0.104,-0.045 0.113,-0.04 0.124,-0.04L0.13,-0.04L0.13,-0.042C0.127,-0.042 0.116,-0.048 0.096,-0.058Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,758.882,753)">
|
||||||
|
<path d="M0.208,-0.7L0.223,-0.7C0.227,-0.7 0.229,-0.697 0.229,-0.691C0.294,-0.687 0.328,-0.684 0.331,-0.682C0.333,-0.682 0.333,-0.683 0.333,-0.685C0.364,-0.683 0.387,-0.682 0.401,-0.682L0.462,-0.682C0.466,-0.682 0.468,-0.684 0.468,-0.688L0.45,-0.688L0.45,-0.694L0.49,-0.694L0.49,-0.688L0.475,-0.688L0.475,-0.682L0.557,-0.682C0.564,-0.682 0.573,-0.681 0.584,-0.679C0.603,-0.681 0.612,-0.685 0.612,-0.691L0.658,-0.691C0.678,-0.691 0.688,-0.692 0.689,-0.694C0.706,-0.692 0.72,-0.691 0.731,-0.691L0.903,-0.691L0.921,-0.688L0.928,-0.691C1.035,-0.686 1.096,-0.681 1.111,-0.676L1.13,-0.682L1.13,-0.676L1.068,-0.667L1.068,-0.664C1.075,-0.664 1.078,-0.661 1.078,-0.657C1.078,-0.654 1.075,-0.651 1.068,-0.648C1.064,-0.65 1.061,-0.651 1.059,-0.651C1.059,-0.647 1.055,-0.645 1.047,-0.645L1.047,-0.642C1.048,-0.638 1.05,-0.636 1.053,-0.636L1.059,-0.636C1.072,-0.636 1.087,-0.635 1.105,-0.633C1.113,-0.635 1.12,-0.636 1.126,-0.636C1.139,-0.625 1.145,-0.619 1.145,-0.618C1.138,-0.613 1.132,-0.611 1.126,-0.611L1.099,-0.611C1.088,-0.611 1.079,-0.608 1.071,-0.602L1.029,-0.602C1.024,-0.602 1.02,-0.603 1.017,-0.605L1.01,-0.602C0.993,-0.604 0.98,-0.605 0.971,-0.605C0.962,-0.605 0.958,-0.604 0.958,-0.602L0.928,-0.605L0.695,-0.605C0.69,-0.605 0.686,-0.604 0.683,-0.602C0.679,-0.604 0.675,-0.605 0.67,-0.605L0.563,-0.605C0.555,-0.605 0.547,-0.604 0.539,-0.602C0.531,-0.604 0.523,-0.605 0.514,-0.605L0.453,-0.605C0.429,-0.578 0.401,-0.525 0.37,-0.446C0.37,-0.439 0.334,-0.358 0.263,-0.201L0.177,-0.021C0.185,-0.001 0.189,0.011 0.189,0.013C0.186,0.013 0.177,0.001 0.162,-0.023L0.156,-0.023L0.153,-0.008C0.159,-0.001 0.162,0.006 0.162,0.013L0.162,0.017L0.15,0.007L0.128,0.007C0.128,-0.005 0.108,-0.045 0.067,-0.112C0.012,-0.235 -0.028,-0.332 -0.052,-0.403L-0.052,-0.412C-0.048,-0.412 -0.046,-0.409 -0.046,-0.403L-0.043,-0.403C-0.039,-0.403 -0.037,-0.405 -0.037,-0.409L-0.046,-0.443L-0.046,-0.446L-0.04,-0.446L0.034,-0.293L0.037,-0.293L0.037,-0.296C0.008,-0.37 -0.006,-0.41 -0.006,-0.416L-0.006,-0.421C0.009,-0.398 0.037,-0.343 0.076,-0.256C0.118,-0.177 0.142,-0.135 0.147,-0.13C0.212,-0.285 0.285,-0.442 0.364,-0.602C0.36,-0.602 0.358,-0.604 0.358,-0.608L0.352,-0.605L0.333,-0.605C0.332,-0.605 0.331,-0.606 0.331,-0.608C0.327,-0.606 0.324,-0.605 0.321,-0.605L0.315,-0.608C0.313,-0.608 0.312,-0.607 0.312,-0.605L0.291,-0.608L0.263,-0.608C0.263,-0.615 0.261,-0.618 0.257,-0.618L0.254,-0.618C0.247,-0.618 0.215,-0.621 0.159,-0.626L0.159,-0.63L0.162,-0.636L0.156,-0.636L0.156,-0.639C0.156,-0.641 0.163,-0.646 0.177,-0.654L0.184,-0.654L0.189,-0.651C0.189,-0.653 0.191,-0.659 0.193,-0.669C0.188,-0.671 0.178,-0.674 0.162,-0.676L0.162,-0.682L0.184,-0.682C0.191,-0.682 0.199,-0.688 0.208,-0.7ZM0.291,-0.697L0.352,-0.697C0.356,-0.697 0.358,-0.694 0.358,-0.691L0.291,-0.691L0.291,-0.697ZM0.41,-0.694L0.419,-0.694L0.419,-0.688L0.41,-0.688L0.41,-0.694ZM0.505,-0.694L0.533,-0.694L0.533,-0.688L0.505,-0.688L0.505,-0.694ZM0.603,-0.694L0.603,-0.691C0.603,-0.687 0.601,-0.685 0.597,-0.685L0.563,-0.685L0.563,-0.691L0.588,-0.691L0.603,-0.694ZM0.202,-0.667L0.202,-0.664L0.239,-0.664L0.239,-0.667L0.202,-0.667ZM0.269,-0.339L0.266,-0.327L0.266,-0.324L0.269,-0.324L0.272,-0.336L0.272,-0.339L0.269,-0.339ZM0.257,-0.308C0.237,-0.266 0.211,-0.205 0.177,-0.125C0.195,-0.147 0.223,-0.207 0.26,-0.305L0.26,-0.308L0.257,-0.308Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,891.444,753)">
|
||||||
|
<path d="M0.631,-0.705C0.646,-0.705 0.661,-0.696 0.673,-0.678L0.646,-0.678C0.62,-0.678 0.587,-0.67 0.546,-0.653L0.546,-0.65L0.549,-0.65C0.607,-0.654 0.649,-0.656 0.677,-0.656L0.683,-0.656L0.683,-0.65L0.67,-0.65C0.668,-0.65 0.667,-0.651 0.667,-0.653L0.503,-0.629C0.503,-0.627 0.493,-0.624 0.473,-0.62L0.473,-0.614L0.519,-0.623L0.521,-0.623L0.521,-0.617C0.386,-0.581 0.318,-0.555 0.318,-0.538C0.252,-0.483 0.204,-0.431 0.175,-0.383C0.131,-0.304 0.108,-0.243 0.108,-0.201C0.108,-0.157 0.131,-0.125 0.175,-0.104C0.183,-0.102 0.19,-0.101 0.196,-0.101C0.286,-0.136 0.353,-0.196 0.397,-0.28C0.409,-0.318 0.419,-0.363 0.427,-0.417L0.434,-0.417L0.427,-0.359C0.433,-0.359 0.439,-0.374 0.443,-0.404C0.441,-0.412 0.439,-0.42 0.439,-0.429L0.439,-0.438L0.446,-0.438C0.448,-0.407 0.449,-0.386 0.449,-0.374C0.449,-0.358 0.444,-0.331 0.434,-0.292C0.445,-0.302 0.458,-0.335 0.473,-0.392C0.48,-0.447 0.486,-0.474 0.491,-0.474C0.491,-0.472 0.492,-0.471 0.494,-0.471C0.492,-0.469 0.49,-0.457 0.488,-0.435L0.488,-0.432L0.491,-0.432C0.499,-0.454 0.504,-0.48 0.506,-0.508C0.502,-0.516 0.5,-0.522 0.5,-0.526L0.506,-0.529C0.512,-0.529 0.518,-0.513 0.525,-0.48C0.525,-0.4 0.491,-0.297 0.424,-0.17C0.372,-0.093 0.305,-0.042 0.224,-0.019C0.188,-0.006 0.154,-0 0.121,-0C0.04,-0.022 -0.001,-0.083 -0.001,-0.183C-0.001,-0.296 0.045,-0.398 0.136,-0.489C0.165,-0.522 0.227,-0.568 0.321,-0.629C0.425,-0.68 0.529,-0.705 0.631,-0.705ZM0.482,-0.656L0.482,-0.653C0.499,-0.653 0.508,-0.652 0.509,-0.65C0.526,-0.659 0.536,-0.663 0.54,-0.663L0.54,-0.668L0.482,-0.656ZM0.099,-0.417C0.144,-0.458 0.179,-0.494 0.206,-0.523C0.23,-0.538 0.242,-0.55 0.242,-0.559C0.201,-0.535 0.156,-0.493 0.108,-0.432C0.102,-0.427 0.099,-0.422 0.099,-0.417ZM0.105,-0.261C0.108,-0.261 0.118,-0.284 0.136,-0.328C0.158,-0.378 0.203,-0.439 0.272,-0.511L0.272,-0.514C0.204,-0.463 0.167,-0.422 0.16,-0.392C0.142,-0.371 0.124,-0.328 0.105,-0.261ZM0.069,-0.334L0.069,-0.332L0.072,-0.332C0.116,-0.393 0.139,-0.429 0.139,-0.438C0.11,-0.413 0.086,-0.378 0.069,-0.334ZM0.467,-0.31L0.467,-0.307L0.47,-0.307C0.492,-0.371 0.503,-0.413 0.503,-0.432C0.49,-0.409 0.478,-0.369 0.467,-0.31ZM0.087,-0.404L0.087,-0.401C0.091,-0.401 0.093,-0.403 0.093,-0.407L0.093,-0.41C0.089,-0.41 0.087,-0.408 0.087,-0.404ZM0.063,-0.368C0.032,-0.297 0.017,-0.245 0.017,-0.213C0.021,-0.148 0.028,-0.107 0.039,-0.088L0.042,-0.088L0.042,-0.094C0.035,-0.107 0.032,-0.129 0.032,-0.158C0.032,-0.191 0.035,-0.227 0.042,-0.268L0.039,-0.273C0.045,-0.3 0.054,-0.331 0.066,-0.365L0.066,-0.368L0.063,-0.368ZM0.096,-0.24L0.096,-0.237C0.1,-0.237 0.103,-0.239 0.103,-0.243L0.103,-0.246C0.098,-0.245 0.096,-0.243 0.096,-0.24ZM0.397,-0.222L0.397,-0.219L0.4,-0.219C0.408,-0.232 0.412,-0.241 0.412,-0.243C0.407,-0.242 0.402,-0.235 0.397,-0.222ZM0.066,-0.24C0.06,-0.222 0.055,-0.202 0.051,-0.179C0.055,-0.138 0.061,-0.108 0.069,-0.088L0.072,-0.088L0.072,-0.094C0.066,-0.121 0.063,-0.139 0.063,-0.146L0.066,-0.152C0.064,-0.155 0.063,-0.16 0.063,-0.164C0.063,-0.169 0.064,-0.173 0.066,-0.176C0.064,-0.18 0.063,-0.183 0.063,-0.186C0.067,-0.218 0.069,-0.235 0.069,-0.237L0.069,-0.24L0.066,-0.24ZM0.294,-0.125L0.294,-0.122C0.3,-0.122 0.322,-0.143 0.357,-0.186L0.357,-0.188L0.354,-0.188C0.314,-0.151 0.294,-0.129 0.294,-0.125ZM0.379,-0.149C0.379,-0.146 0.36,-0.126 0.321,-0.088L0.324,-0.088C0.351,-0.112 0.37,-0.132 0.382,-0.146L0.385,-0.146L0.385,-0.149L0.379,-0.149ZM0.09,-0.104L0.09,-0.101C0.094,-0.08 0.104,-0.07 0.121,-0.07L0.13,-0.07L0.13,-0.073L0.09,-0.104ZM0.151,-0.055L0.188,-0.055L0.203,-0.058L0.203,-0.061L0.157,-0.061C0.153,-0.06 0.151,-0.058 0.151,-0.055ZM0.096,-0.058L0.096,-0.055C0.104,-0.045 0.113,-0.04 0.124,-0.04L0.13,-0.04L0.13,-0.042C0.127,-0.042 0.116,-0.048 0.096,-0.058Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
BIN
assets/images/rounded-background.png
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
assets/images/rounded-border.png
Normal file
|
After Width: | Height: | Size: 315 B |
BIN
assets/images/scrollbar-bottom.png
Normal file
|
After Width: | Height: | Size: 105 B |
17
assets/images/scrollbar-bottom.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-10)">
|
||||||
|
<g id="Bottom" transform="matrix(0.243243,0,0,0.179487,-59.1081,-30.2051)">
|
||||||
|
<rect x="243" y="224" width="37" height="39" style="fill:none;"/>
|
||||||
|
<clipPath id="_clip1">
|
||||||
|
<rect x="243" y="224" width="37" height="39"/>
|
||||||
|
</clipPath>
|
||||||
|
<g clip-path="url(#_clip1)">
|
||||||
|
<g transform="matrix(-4.11111,6.82303e-16,-3.59619e-16,-3.97959,280,259.816)">
|
||||||
|
<path d="M7,5.5C7,3.568 5.88,2 4.5,2C3.12,2 2,3.568 2,5.5L2,9L7,9L7,5.5Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/scrollbar-middle.png
Normal file
|
After Width: | Height: | Size: 75 B |
12
assets/images/scrollbar-middle.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 1" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-8)">
|
||||||
|
<g id="Middle" transform="matrix(1,0,0,0.111111,0,7.11111)">
|
||||||
|
<rect x="0" y="8" width="9" height="9" style="fill:none;"/>
|
||||||
|
<g transform="matrix(1,0,0,9,0,-64)">
|
||||||
|
<rect x="2" y="8" width="5" height="1" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 796 B |
BIN
assets/images/scrollbar-top.png
Normal file
|
After Width: | Height: | Size: 132 B |
10
assets/images/scrollbar-top.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Top" transform="matrix(0.243243,0,0,0.179487,-59.1081,-40.2051)">
|
||||||
|
<rect x="243" y="224" width="37" height="39" style="fill:none;"/>
|
||||||
|
<g transform="matrix(4.11111,0,0,3.97959,243,227.184)">
|
||||||
|
<path d="M7,5.5C7,3.568 5.88,2 4.5,2C3.12,2 2,3.568 2,5.5L2,9L7,9L7,5.5Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 793 B |
BIN
assets/images/spinner-background.png
Normal file
|
After Width: | Height: | Size: 684 B |
8
assets/images/spinner-background.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.5,0,0,0.5,0,0)">
|
||||||
|
<rect x="0" y="0" width="48" height="48" style="fill:none;"/>
|
||||||
|
<path d="M24,0C37.246,0 48,10.754 48,24C48,37.246 37.246,48 24,48C10.754,48 0,37.246 0,24C0,10.754 10.754,0 24,0ZM24,8.4C32.61,8.4 39.6,15.39 39.6,24C39.6,32.61 32.61,39.6 24,39.6C15.39,39.6 8.4,32.61 8.4,24C8.4,15.39 15.39,8.4 24,8.4Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 855 B |
BIN
assets/images/spinner-foreground.png
Normal file
|
After Width: | Height: | Size: 340 B |
8
assets/images/spinner-foreground.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.5,0,0,0.5,0,0)">
|
||||||
|
<rect x="0" y="0" width="48" height="48" style="fill:none;"/>
|
||||||
|
<path d="M48,24C48,10.745 37.255,0 24,0L24,8.4C32.616,8.4 39.6,15.384 39.6,24L48,24Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 704 B |
181
assets/index.css
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
text-decoration: inherit;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
box-sizing: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width:100%;
|
||||||
|
max-height:100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.path-list > li {
|
||||||
|
margin-left: 1.2em;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0.5rem auto;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 50rem;
|
||||||
|
background-color: #efefef;
|
||||||
|
border: 1px solid #666;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
flex: 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-logo {
|
||||||
|
flex: 0 0 10rem;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
flex: 0 0 20rem;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-name {
|
||||||
|
display: inline;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-section:not(:last-of-type) {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-list {
|
||||||
|
flex: 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin: -1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid #666;
|
||||||
|
padding: 0.3em 1em;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-section {
|
||||||
|
margin: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-items {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-label > label {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-input ~ .expandable-label .expandable-visualizer {
|
||||||
|
font-family: monospace;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
transition: transform 100ms ease-in-out;
|
||||||
|
transform-origin: 60% 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-visualizer::before {
|
||||||
|
content: "›";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-input:checked ~ .expandable-label {
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-input:checked ~ .expandable-label .expandable-visualizer {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandable-input:not(:checked) ~ .expandable-items {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vfs-entry {
|
||||||
|
}
|
||||||
|
|
||||||
|
.vfs-entry-name {
|
||||||
|
position: relative;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vfs-entry-children .vfs-entry-name::before {
|
||||||
|
content: "";
|
||||||
|
width: 0.6em;
|
||||||
|
height: 1px;
|
||||||
|
background-color: #999;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: -0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vfs-entry-note {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vfs-entry-children {
|
||||||
|
padding-left: 0.8em;
|
||||||
|
margin-left: 0.2em;
|
||||||
|
border-left: 1px solid #999;
|
||||||
|
}
|
||||||
BIN
assets/logo-512.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/round-rect-4px-radius.png
Normal file
|
After Width: | Height: | Size: 175 B |
BIN
assets/test-folder.rbxm
Normal file
44
benches/build.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||||
|
use tempfile::{tempdir, TempDir};
|
||||||
|
|
||||||
|
use librojo::cli::BuildCommand;
|
||||||
|
|
||||||
|
pub fn benchmark_small_place(c: &mut Criterion) {
|
||||||
|
bench_build_place(c, "Small Place", "test-projects/benchmark_small_place")
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, benchmark_small_place);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
fn bench_build_place(c: &mut Criterion, name: &str, path: &str) {
|
||||||
|
let mut group = c.benchmark_group(name);
|
||||||
|
|
||||||
|
// 'rojo build' generally takes a fair bit of time to execute.
|
||||||
|
group.sample_size(10);
|
||||||
|
group.bench_function("build", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| place_setup(path),
|
||||||
|
|(_dir, options)| options.run().unwrap(),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_setup<P: AsRef<Path>>(input_path: P) -> (TempDir, BuildCommand) {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let input = input_path.as_ref().to_path_buf();
|
||||||
|
let output = Some(dir.path().join("output.rbxlx"));
|
||||||
|
|
||||||
|
let options = BuildCommand {
|
||||||
|
project: input,
|
||||||
|
watch: false,
|
||||||
|
plugin: None,
|
||||||
|
output,
|
||||||
|
};
|
||||||
|
|
||||||
|
(dir, options)
|
||||||
|
}
|
||||||
77
build.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
use std::{
|
||||||
|
env, io,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use fs_err as fs;
|
||||||
|
use fs_err::File;
|
||||||
|
use maplit::hashmap;
|
||||||
|
use memofs::VfsSnapshot;
|
||||||
|
use semver::Version;
|
||||||
|
|
||||||
|
fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
|
||||||
|
println!("cargo:rerun-if-changed={}", path.display());
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
let mut children = Vec::new();
|
||||||
|
|
||||||
|
for entry in fs::read_dir(path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
|
||||||
|
let file_name = entry.file_name().to_str().unwrap().to_owned();
|
||||||
|
|
||||||
|
// We can skip any TestEZ test files since they aren't necessary for
|
||||||
|
// the plugin to run.
|
||||||
|
if file_name.ends_with(".spec.lua") || file_name.ends_with(".spec.luau") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let child_snapshot = snapshot_from_fs_path(&entry.path())?;
|
||||||
|
children.push((file_name, child_snapshot));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VfsSnapshot::dir(children))
|
||||||
|
} else {
|
||||||
|
let content = fs::read_to_string(path)?;
|
||||||
|
|
||||||
|
Ok(VfsSnapshot::file(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
|
||||||
|
let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let plugin_root = PathBuf::from(root_dir).join("plugin");
|
||||||
|
|
||||||
|
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
||||||
|
let plugin_version =
|
||||||
|
Version::parse(fs::read_to_string(plugin_root.join("Version.txt"))?.trim())?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
our_version, plugin_version,
|
||||||
|
"plugin version does not match Cargo version"
|
||||||
|
);
|
||||||
|
|
||||||
|
let snapshot = VfsSnapshot::dir(hashmap! {
|
||||||
|
"default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?,
|
||||||
|
"fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?,
|
||||||
|
"http" => snapshot_from_fs_path(&plugin_root.join("http"))?,
|
||||||
|
"log" => snapshot_from_fs_path(&plugin_root.join("log"))?,
|
||||||
|
"rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?,
|
||||||
|
"src" => snapshot_from_fs_path(&plugin_root.join("src"))?,
|
||||||
|
"Packages" => snapshot_from_fs_path(&plugin_root.join("Packages"))?,
|
||||||
|
"Version.txt" => snapshot_from_fs_path(&plugin_root.join("Version.txt"))?,
|
||||||
|
});
|
||||||
|
|
||||||
|
let out_path = Path::new(&out_dir).join("plugin.bincode");
|
||||||
|
let out_file = File::create(out_path)?;
|
||||||
|
|
||||||
|
bincode::serialize_into(out_file, &snapshot)?;
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
|
||||||
|
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
|
||||||
|
embed_resource::compile("build/windows/rojo-manifest.rc");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
2
build/windows/rojo-manifest.rc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define RT_MANIFEST 24
|
||||||
|
1 RT_MANIFEST "rojo.manifest"
|
||||||
8
build/windows/rojo.manifest
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
||||||
|
<ws2:longPathAware>true</ws2:longPathAware>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
||||||
25
crates/memofs/CHANGELOG.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# memofs Changelog
|
||||||
|
|
||||||
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## 0.3.0 (2024-03-15)
|
||||||
|
* Changed `StdBackend` file watching component to use minimal recursive watches. [#830]
|
||||||
|
* Added `Vfs::read_to_string` and `Vfs::read_to_string_lf_normalized` [#854]
|
||||||
|
|
||||||
|
[#830]: https://github.com/rojo-rbx/rojo/pull/830
|
||||||
|
[#854]: https://github.com/rojo-rbx/rojo/pull/854
|
||||||
|
|
||||||
|
## 0.2.0 (2021-08-23)
|
||||||
|
* Updated to `crossbeam-channel` 0.5.1.
|
||||||
|
|
||||||
|
## 0.1.3 (2020-11-19)
|
||||||
|
* Added `set_watch_enabled` to `Vfs` and `VfsLock` to allow turning off file watching.
|
||||||
|
|
||||||
|
## 0.1.2 (2020-03-29)
|
||||||
|
* `VfsSnapshot` now implements Serde's `Serialize` and `Deserialize` traits.
|
||||||
|
|
||||||
|
## 0.1.1 (2020-03-18)
|
||||||
|
* Improved error messages using the [fs-err](https://crates.io/crates/fs-err) crate.
|
||||||
|
|
||||||
|
## 0.1.0 (2020-03-10)
|
||||||
|
* Initial release
|
||||||
17
crates/memofs/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "memofs"
|
||||||
|
description = "Virtual filesystem with configurable backends."
|
||||||
|
version = "0.3.0"
|
||||||
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT"
|
||||||
|
homepage = "https://github.com/rojo-rbx/rojo/tree/master/memofs"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
crossbeam-channel = "0.5.1"
|
||||||
|
fs-err = "2.3.0"
|
||||||
|
notify = "4.0.15"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
The MIT License (MIT)
|
Copyright 2020 The Rojo Developers
|
||||||
|
|
||||||
Copyright (c) 2017 Lucien Greathouse
|
|
||||||
|
|
||||||
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:
|
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 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.
|
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.
|
||||||
22
crates/memofs/README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# memofs
|
||||||
|
[](https://crates.io/crates/memofs)
|
||||||
|
|
||||||
|
Implementation of a virtual filesystem with a configurable backend and file
|
||||||
|
watching.
|
||||||
|
|
||||||
|
memofs is currently an unstable minimum viable library. Its primary consumer is
|
||||||
|
[Rojo](https://github.com/rojo-rbx/rojo), a build system for Roblox.
|
||||||
|
|
||||||
|
### Current Features
|
||||||
|
* API similar to `std::fs`
|
||||||
|
* Configurable backends
|
||||||
|
* `StdBackend`, which uses `std::fs` and the `notify` crate
|
||||||
|
* `NoopBackend`, which always throws errors
|
||||||
|
* `InMemoryFs`, a simple in-memory filesystem useful for testing
|
||||||
|
|
||||||
|
### Future Features
|
||||||
|
* Hash-based hierarchical memoization keys (hence the name)
|
||||||
|
* Configurable caching (write-through, write-around, write-back)
|
||||||
|
|
||||||
|
## License
|
||||||
|
memofs is available under the terms of the MIT license. See [LICENSE.txt](LICENSE.txt) or <https://opensource.org/licenses/MIT> for more details.
|
||||||
7
crates/memofs/README.tpl
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# {{crate}}
|
||||||
|
[](https://crates.io/crates/memofs)
|
||||||
|
|
||||||
|
{{readme}}
|
||||||
|
|
||||||
|
## License
|
||||||
|
memofs is available under the terms of the MIT license. See [LICENSE.txt](LICENSE.txt) or <https://opensource.org/licenses/MIT> for more details.
|
||||||
255
crates/memofs/src/in_memory_fs.rs
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
use std::collections::{BTreeSet, HashMap, VecDeque};
|
||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
|
|
||||||
|
use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent, VfsSnapshot};
|
||||||
|
|
||||||
|
/// In-memory filesystem that can be used as a VFS backend.
|
||||||
|
///
|
||||||
|
/// Internally reference counted to enable giving a copy to
|
||||||
|
/// [`Vfs`](struct.Vfs.html) and keeping the original to mutate the filesystem's
|
||||||
|
/// state with.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct InMemoryFs {
|
||||||
|
inner: Arc<Mutex<InMemoryFsInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InMemoryFs {
|
||||||
|
/// Create a new empty `InMemoryFs`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Arc::new(Mutex::new(InMemoryFsInner::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a [`VfsSnapshot`](enum.VfsSnapshot.html) into a subtree of the
|
||||||
|
/// in-memory filesystem.
|
||||||
|
///
|
||||||
|
/// This function will return an error if the operations required to apply
|
||||||
|
/// the snapshot result in errors, like trying to create a file inside a
|
||||||
|
/// file.
|
||||||
|
pub fn load_snapshot<P: Into<PathBuf>>(
|
||||||
|
&mut self,
|
||||||
|
path: P,
|
||||||
|
snapshot: VfsSnapshot,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.load_snapshot(path.into(), snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Raises a filesystem change event.
|
||||||
|
///
|
||||||
|
/// If this `InMemoryFs` is being used as the backend of a
|
||||||
|
/// [`Vfs`](struct.Vfs.html), then any listeners be notified of this event.
|
||||||
|
pub fn raise_event(&mut self, event: VfsEvent) {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
inner.event_sender.send(event).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InMemoryFs {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct InMemoryFsInner {
|
||||||
|
entries: HashMap<PathBuf, Entry>,
|
||||||
|
orphans: BTreeSet<PathBuf>,
|
||||||
|
|
||||||
|
event_receiver: Receiver<VfsEvent>,
|
||||||
|
event_sender: Sender<VfsEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InMemoryFsInner {
|
||||||
|
fn new() -> Self {
|
||||||
|
let (event_sender, event_receiver) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
entries: HashMap::new(),
|
||||||
|
orphans: BTreeSet::new(),
|
||||||
|
event_receiver,
|
||||||
|
event_sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_snapshot(&mut self, path: PathBuf, snapshot: VfsSnapshot) -> io::Result<()> {
|
||||||
|
if let Some(parent_path) = path.parent() {
|
||||||
|
if let Some(parent_entry) = self.entries.get_mut(parent_path) {
|
||||||
|
if let Entry::Dir { children } = parent_entry {
|
||||||
|
children.insert(path.clone());
|
||||||
|
} else {
|
||||||
|
return must_be_dir(parent_path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.orphans.insert(path.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.orphans.insert(path.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
match snapshot {
|
||||||
|
VfsSnapshot::File { contents } => {
|
||||||
|
self.entries.insert(path, Entry::File { contents });
|
||||||
|
}
|
||||||
|
VfsSnapshot::Dir { children } => {
|
||||||
|
self.entries.insert(
|
||||||
|
path.clone(),
|
||||||
|
Entry::Dir {
|
||||||
|
children: BTreeSet::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
for (child_name, child) in children {
|
||||||
|
let full_path = path.join(child_name);
|
||||||
|
self.load_snapshot(full_path, child)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&mut self, root_path: PathBuf) {
|
||||||
|
self.orphans.remove(&root_path);
|
||||||
|
|
||||||
|
let mut to_remove = VecDeque::new();
|
||||||
|
to_remove.push_back(root_path);
|
||||||
|
|
||||||
|
while let Some(path) = to_remove.pop_front() {
|
||||||
|
if let Some(Entry::Dir { children }) = self.entries.remove(&path) {
|
||||||
|
to_remove.extend(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Entry {
|
||||||
|
File { contents: Vec<u8> },
|
||||||
|
|
||||||
|
Dir { children: BTreeSet<PathBuf> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsBackend for InMemoryFs {
|
||||||
|
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::File { contents }) => Ok(contents.clone()),
|
||||||
|
Some(Entry::Dir { .. }) => must_be_file(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
inner.load_snapshot(
|
||||||
|
path.to_path_buf(),
|
||||||
|
VfsSnapshot::File {
|
||||||
|
contents: data.to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::Dir { children }) => {
|
||||||
|
let iter = children
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| Ok(DirEntry { path }));
|
||||||
|
|
||||||
|
Ok(ReadDir {
|
||||||
|
inner: Box::new(iter),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some(Entry::File { .. }) => must_be_dir(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::File { .. }) => {
|
||||||
|
inner.remove(path.to_owned());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Some(Entry::Dir { .. }) => must_be_file(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir_all(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::Dir { .. }) => {
|
||||||
|
inner.remove(path.to_owned());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Some(Entry::File { .. }) => must_be_dir(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&mut self, path: &Path) -> io::Result<Metadata> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::File { .. }) => Ok(Metadata { is_file: true }),
|
||||||
|
Some(Entry::Dir { .. }) => Ok(Metadata { is_file: false }),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
inner.event_receiver.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwatch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn must_be_file<T>(path: &Path) -> io::Result<T> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!(
|
||||||
|
"path {} was a directory, but must be a file",
|
||||||
|
path.display()
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn must_be_dir<T>(path: &Path) -> io::Result<T> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!(
|
||||||
|
"path {} was a file, but must be a directory",
|
||||||
|
path.display()
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not_found<T>(path: &Path) -> io::Result<T> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::NotFound,
|
||||||
|
format!("path {} not found", path.display()),
|
||||||
|
))
|
||||||
|
}
|
||||||
495
crates/memofs/src/lib.rs
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
/*!
|
||||||
|
Implementation of a virtual filesystem with a configurable backend and file
|
||||||
|
watching.
|
||||||
|
|
||||||
|
memofs is currently an unstable minimum viable library. Its primary consumer is
|
||||||
|
[Rojo](https://github.com/rojo-rbx/rojo), a build system for Roblox.
|
||||||
|
|
||||||
|
## Current Features
|
||||||
|
* API similar to `std::fs`
|
||||||
|
* Configurable backends
|
||||||
|
* `StdBackend`, which uses `std::fs` and the `notify` crate
|
||||||
|
* `NoopBackend`, which always throws errors
|
||||||
|
* `InMemoryFs`, a simple in-memory filesystem useful for testing
|
||||||
|
|
||||||
|
## Future Features
|
||||||
|
* Hash-based hierarchical memoization keys (hence the name)
|
||||||
|
* Configurable caching (write-through, write-around, write-back)
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod in_memory_fs;
|
||||||
|
mod noop_backend;
|
||||||
|
mod snapshot;
|
||||||
|
mod std_backend;
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
use std::{io, str};
|
||||||
|
|
||||||
|
pub use in_memory_fs::InMemoryFs;
|
||||||
|
pub use noop_backend::NoopBackend;
|
||||||
|
pub use snapshot::VfsSnapshot;
|
||||||
|
pub use std_backend::StdBackend;
|
||||||
|
|
||||||
|
mod sealed {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Sealing trait for VfsBackend.
|
||||||
|
pub trait Sealed {}
|
||||||
|
|
||||||
|
impl Sealed for NoopBackend {}
|
||||||
|
impl Sealed for StdBackend {}
|
||||||
|
impl Sealed for InMemoryFs {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait that transforms `io::Result<T>` into `io::Result<Option<T>>`.
|
||||||
|
///
|
||||||
|
/// `Ok(None)` takes the place of IO errors whose `io::ErrorKind` is `NotFound`.
|
||||||
|
pub trait IoResultExt<T> {
|
||||||
|
fn with_not_found(self) -> io::Result<Option<T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IoResultExt<T> for io::Result<T> {
|
||||||
|
fn with_not_found(self) -> io::Result<Option<T>> {
|
||||||
|
match self {
|
||||||
|
Ok(v) => Ok(Some(v)),
|
||||||
|
Err(err) => {
|
||||||
|
if err.kind() == io::ErrorKind::NotFound {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Backend that can be used to create a `Vfs`.
|
||||||
|
///
|
||||||
|
/// This trait is sealed and cannot not be implemented outside this crate.
|
||||||
|
pub trait VfsBackend: sealed::Sealed + Send + 'static {
|
||||||
|
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>>;
|
||||||
|
fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()>;
|
||||||
|
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir>;
|
||||||
|
fn metadata(&mut self, path: &Path) -> io::Result<Metadata>;
|
||||||
|
fn remove_file(&mut self, path: &Path) -> io::Result<()>;
|
||||||
|
fn remove_dir_all(&mut self, path: &Path) -> io::Result<()>;
|
||||||
|
|
||||||
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent>;
|
||||||
|
fn watch(&mut self, path: &Path) -> io::Result<()>;
|
||||||
|
fn unwatch(&mut self, path: &Path) -> io::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vfs equivalent to [`std::fs::DirEntry`][std::fs::DirEntry].
|
||||||
|
///
|
||||||
|
/// [std::fs::DirEntry]: https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html
|
||||||
|
pub struct DirEntry {
|
||||||
|
pub(crate) path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirEntry {
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vfs equivalent to [`std::fs::ReadDir`][std::fs::ReadDir].
|
||||||
|
///
|
||||||
|
/// [std::fs::ReadDir]: https://doc.rust-lang.org/stable/std/fs/struct.ReadDir.html
|
||||||
|
pub struct ReadDir {
|
||||||
|
pub(crate) inner: Box<dyn Iterator<Item = io::Result<DirEntry>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for ReadDir {
|
||||||
|
type Item = io::Result<DirEntry>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.inner.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vfs equivalent to [`std::fs::Metadata`][std::fs::Metadata].
|
||||||
|
///
|
||||||
|
/// [std::fs::Metadata]: https://doc.rust-lang.org/stable/std/fs/struct.Metadata.html
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Metadata {
|
||||||
|
pub(crate) is_file: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata {
|
||||||
|
pub fn is_file(&self) -> bool {
|
||||||
|
self.is_file
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_dir(&self) -> bool {
|
||||||
|
!self.is_file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an event that a filesystem can raise that might need to be
|
||||||
|
/// handled.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum VfsEvent {
|
||||||
|
Create(PathBuf),
|
||||||
|
Write(PathBuf),
|
||||||
|
Remove(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contains implementation details of the Vfs, wrapped by `Vfs` and `VfsLock`,
|
||||||
|
/// the public interfaces to this type.
|
||||||
|
struct VfsInner {
|
||||||
|
backend: Box<dyn VfsBackend>,
|
||||||
|
watch_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsInner {
|
||||||
|
fn read<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Arc<Vec<u8>>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = self.backend.read(path)?;
|
||||||
|
|
||||||
|
if self.watch_enabled {
|
||||||
|
self.backend.watch(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Arc::new(contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_to_string<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Arc<String>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = self.backend.read(path)?;
|
||||||
|
|
||||||
|
if self.watch_enabled {
|
||||||
|
self.backend.watch(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let contents_str = str::from_utf8(&contents).map_err(|_| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
format!("File was not valid UTF-8: {}", path.display()),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Arc::new(contents_str.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<P: AsRef<Path>, C: AsRef<[u8]>>(&mut self, path: P, contents: C) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = contents.as_ref();
|
||||||
|
self.backend.write(path, contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<ReadDir> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let dir = self.backend.read_dir(path)?;
|
||||||
|
|
||||||
|
if self.watch_enabled {
|
||||||
|
self.backend.watch(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let _ = self.backend.unwatch(path);
|
||||||
|
self.backend.remove_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir_all<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let _ = self.backend.unwatch(path);
|
||||||
|
self.backend.remove_dir_all(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Metadata> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.backend.metadata(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
self.backend.event_receiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit_event(&mut self, event: &VfsEvent) -> io::Result<()> {
|
||||||
|
if let VfsEvent::Remove(path) = event {
|
||||||
|
let _ = self.backend.unwatch(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A virtual filesystem with a configurable backend.
|
||||||
|
///
|
||||||
|
/// All operations on the Vfs take a lock on an internal backend. For performing
|
||||||
|
/// large batches of operations, it might be more performant to call `lock()`
|
||||||
|
/// and use [`VfsLock`](struct.VfsLock.html) instead.
|
||||||
|
pub struct Vfs {
|
||||||
|
inner: Mutex<VfsInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vfs {
|
||||||
|
/// Creates a new `Vfs` with the default backend, `StdBackend`.
|
||||||
|
pub fn new_default() -> Self {
|
||||||
|
Self::new(StdBackend::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `Vfs` with the given backend.
|
||||||
|
pub fn new<B: VfsBackend>(backend: B) -> Self {
|
||||||
|
let lock = VfsInner {
|
||||||
|
backend: Box::new(backend),
|
||||||
|
watch_enabled: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner: Mutex::new(lock),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manually lock the Vfs, useful for large batches of operations.
|
||||||
|
pub fn lock(&self) -> VfsLock<'_> {
|
||||||
|
VfsLock {
|
||||||
|
inner: self.inner.lock().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turns automatic file watching on or off. Enabled by default.
|
||||||
|
///
|
||||||
|
/// Turning off file watching may be useful for single-use cases, especially
|
||||||
|
/// on platforms like macOS where registering file watches has significant
|
||||||
|
/// performance cost.
|
||||||
|
pub fn set_watch_enabled(&self, enabled: bool) {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.watch_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a file from the VFS, or the underlying backend if it isn't
|
||||||
|
/// resident.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read`][std::fs::read].
|
||||||
|
///
|
||||||
|
/// [std::fs::read]: https://doc.rust-lang.org/stable/std/fs/fn.read.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read<P: AsRef<Path>>(&self, path: P) -> io::Result<Arc<Vec<u8>>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().read(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a file from the VFS (or from the underlying backend if it isn't
|
||||||
|
/// resident) into a string.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read_to_string`][std::fs::read_to_string].
|
||||||
|
///
|
||||||
|
/// [std::fs::read_to_string]: https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read_to_string<P: AsRef<Path>>(&self, path: P) -> io::Result<Arc<String>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().read_to_string(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a file from the VFS (or the underlying backend if it isn't
|
||||||
|
/// resident) into a string, and normalize its line endings to LF.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read_to_string`][std::fs::read_to_string], but also performs
|
||||||
|
/// line ending normalization.
|
||||||
|
///
|
||||||
|
/// [std::fs::read_to_string]: https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read_to_string_lf_normalized<P: AsRef<Path>>(&self, path: P) -> io::Result<Arc<String>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = self.inner.lock().unwrap().read_to_string(path)?;
|
||||||
|
|
||||||
|
Ok(contents.replace("\r\n", "\n").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a file to the VFS and the underlying backend.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::write`][std::fs::write].
|
||||||
|
///
|
||||||
|
/// [std::fs::write]: https://doc.rust-lang.org/stable/std/fs/fn.write.html
|
||||||
|
#[inline]
|
||||||
|
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(&self, path: P, contents: C) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = contents.as_ref();
|
||||||
|
self.inner.lock().unwrap().write(path, contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read all of the children of a directory.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read_dir`][std::fs::read_dir].
|
||||||
|
///
|
||||||
|
/// [std::fs::read_dir]: https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<ReadDir> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().read_dir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a file.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file].
|
||||||
|
///
|
||||||
|
/// [std::fs::remove_file]: https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().remove_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a directory and all of its descendants.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::remove_dir_all`][std::fs::remove_dir_all].
|
||||||
|
///
|
||||||
|
/// [std::fs::remove_dir_all]: https://doc.rust-lang.org/stable/std/fs/fn.remove_dir_all.html
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().remove_dir_all(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query metadata about the given path.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::metadata`][std::fs::metadata].
|
||||||
|
///
|
||||||
|
/// [std::fs::metadata]: https://doc.rust-lang.org/stable/std/fs/fn.metadata.html
|
||||||
|
#[inline]
|
||||||
|
pub fn metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().metadata(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a handle to the event receiver for this `Vfs`.
|
||||||
|
#[inline]
|
||||||
|
pub fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
self.inner.lock().unwrap().event_receiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit an event to this `Vfs`.
|
||||||
|
#[inline]
|
||||||
|
pub fn commit_event(&self, event: &VfsEvent) -> io::Result<()> {
|
||||||
|
self.inner.lock().unwrap().commit_event(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A locked handle to a [`Vfs`](struct.Vfs.html), created by `Vfs::lock`.
|
||||||
|
///
|
||||||
|
/// Implements roughly the same API as [`Vfs`](struct.Vfs.html).
|
||||||
|
pub struct VfsLock<'a> {
|
||||||
|
inner: MutexGuard<'a, VfsInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsLock<'_> {
|
||||||
|
/// Turns automatic file watching on or off. Enabled by default.
|
||||||
|
///
|
||||||
|
/// Turning off file watching may be useful for single-use cases, especially
|
||||||
|
/// on platforms like macOS where registering file watches has significant
|
||||||
|
/// performance cost.
|
||||||
|
pub fn set_watch_enabled(&mut self, enabled: bool) {
|
||||||
|
self.inner.watch_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a file from the VFS, or the underlying backend if it isn't
|
||||||
|
/// resident.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read`][std::fs::read].
|
||||||
|
///
|
||||||
|
/// [std::fs::read]: https://doc.rust-lang.org/stable/std/fs/fn.read.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Arc<Vec<u8>>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.read(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a file to the VFS and the underlying backend.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::write`][std::fs::write].
|
||||||
|
///
|
||||||
|
/// [std::fs::write]: https://doc.rust-lang.org/stable/std/fs/fn.write.html
|
||||||
|
#[inline]
|
||||||
|
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(
|
||||||
|
&mut self,
|
||||||
|
path: P,
|
||||||
|
contents: C,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = contents.as_ref();
|
||||||
|
self.inner.write(path, contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read all of the children of a directory.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read_dir`][std::fs::read_dir].
|
||||||
|
///
|
||||||
|
/// [std::fs::read_dir]: https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<ReadDir> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.read_dir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a file.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file].
|
||||||
|
///
|
||||||
|
/// [std::fs::remove_file]: https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.remove_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a directory and all of its descendants.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::remove_dir_all`][std::fs::remove_dir_all].
|
||||||
|
///
|
||||||
|
/// [std::fs::remove_dir_all]: https://doc.rust-lang.org/stable/std/fs/fn.remove_dir_all.html
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_dir_all<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.remove_dir_all(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query metadata about the given path.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::metadata`][std::fs::metadata].
|
||||||
|
///
|
||||||
|
/// [std::fs::metadata]: https://doc.rust-lang.org/stable/std/fs/fn.metadata.html
|
||||||
|
#[inline]
|
||||||
|
pub fn metadata<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Metadata> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.metadata(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a handle to the event receiver for this `Vfs`.
|
||||||
|
#[inline]
|
||||||
|
pub fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
self.inner.event_receiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit an event to this `Vfs`.
|
||||||
|
#[inline]
|
||||||
|
pub fn commit_event(&mut self, event: &VfsEvent) -> io::Result<()> {
|
||||||
|
self.inner.commit_event(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{InMemoryFs, Vfs, VfsSnapshot};
|
||||||
|
|
||||||
|
/// https://github.com/rojo-rbx/rojo/issues/899
|
||||||
|
#[test]
|
||||||
|
fn read_to_string_lf_normalized_keeps_trailing_newline() {
|
||||||
|
let mut imfs = InMemoryFs::new();
|
||||||
|
imfs.load_snapshot("test", VfsSnapshot::file("bar\r\nfoo\r\n\r\n"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let vfs = Vfs::new(imfs);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vfs.read_to_string_lf_normalized("test").unwrap().as_str(),
|
||||||
|
"bar\nfoo\n\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
82
crates/memofs/src/noop_backend.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::{Metadata, ReadDir, VfsBackend, VfsEvent};
|
||||||
|
|
||||||
|
/// `VfsBackend` that returns an error on every operation.
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct NoopBackend;
|
||||||
|
|
||||||
|
impl NoopBackend {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsBackend for NoopBackend {
|
||||||
|
fn read(&mut self, _path: &Path) -> io::Result<Vec<u8>> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _path: &Path, _data: &[u8]) -> io::Result<()> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_dir(&mut self, _path: &Path) -> io::Result<ReadDir> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir_all(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&mut self, _path: &Path) -> io::Result<Metadata> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
crossbeam_channel::never()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwatch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NoopBackend {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
45
crates/memofs/src/snapshot.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
/// A slice of a tree of files. Can be loaded into an
|
||||||
|
/// [`InMemoryFs`](struct.InMemoryFs.html).
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum VfsSnapshot {
|
||||||
|
File {
|
||||||
|
contents: Vec<u8>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Dir {
|
||||||
|
children: BTreeMap<String, VfsSnapshot>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsSnapshot {
|
||||||
|
pub fn file<C: Into<Vec<u8>>>(contents: C) -> Self {
|
||||||
|
Self::File {
|
||||||
|
contents: contents.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dir<K: Into<String>, I: IntoIterator<Item = (K, VfsSnapshot)>>(children: I) -> Self {
|
||||||
|
Self::Dir {
|
||||||
|
children: children
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, value)| (key.into(), value))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_file() -> Self {
|
||||||
|
Self::File {
|
||||||
|
contents: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_dir() -> Self {
|
||||||
|
Self::Dir {
|
||||||
|
children: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
crates/memofs/src/std_backend.rs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::{collections::HashSet, io};
|
||||||
|
|
||||||
|
use crossbeam_channel::Receiver;
|
||||||
|
use notify::{watcher, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
|
||||||
|
use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent};
|
||||||
|
|
||||||
|
/// `VfsBackend` that uses `std::fs` and the `notify` crate.
|
||||||
|
pub struct StdBackend {
|
||||||
|
watcher: RecommendedWatcher,
|
||||||
|
watcher_receiver: Receiver<VfsEvent>,
|
||||||
|
watches: HashSet<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdBackend {
|
||||||
|
pub fn new() -> StdBackend {
|
||||||
|
let (notify_tx, notify_rx) = mpsc::channel();
|
||||||
|
let watcher = watcher(notify_tx, Duration::from_millis(50)).unwrap();
|
||||||
|
|
||||||
|
let (tx, rx) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
for event in notify_rx {
|
||||||
|
match event {
|
||||||
|
DebouncedEvent::Create(path) => {
|
||||||
|
tx.send(VfsEvent::Create(path))?;
|
||||||
|
}
|
||||||
|
DebouncedEvent::Write(path) => {
|
||||||
|
tx.send(VfsEvent::Write(path))?;
|
||||||
|
}
|
||||||
|
DebouncedEvent::Remove(path) => {
|
||||||
|
tx.send(VfsEvent::Remove(path))?;
|
||||||
|
}
|
||||||
|
DebouncedEvent::Rename(from, to) => {
|
||||||
|
tx.send(VfsEvent::Remove(from))?;
|
||||||
|
tx.send(VfsEvent::Create(to))?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result::<(), crossbeam_channel::SendError<VfsEvent>>::Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
watcher,
|
||||||
|
watcher_receiver: rx,
|
||||||
|
watches: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsBackend for StdBackend {
|
||||||
|
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>> {
|
||||||
|
fs_err::read(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
|
||||||
|
fs_err::write(path, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir> {
|
||||||
|
let entries: Result<Vec<_>, _> = fs_err::read_dir(path)?.collect();
|
||||||
|
let mut entries = entries?;
|
||||||
|
|
||||||
|
entries.sort_by_cached_key(|entry| entry.file_name());
|
||||||
|
|
||||||
|
let inner = entries
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| Ok(DirEntry { path: entry.path() }));
|
||||||
|
|
||||||
|
Ok(ReadDir {
|
||||||
|
inner: Box::new(inner),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
fs_err::remove_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir_all(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
fs_err::remove_dir_all(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&mut self, path: &Path) -> io::Result<Metadata> {
|
||||||
|
let inner = fs_err::metadata(path)?;
|
||||||
|
|
||||||
|
Ok(Metadata {
|
||||||
|
is_file: inner.is_file(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
self.watcher_receiver.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
if self.watches.contains(path)
|
||||||
|
|| path
|
||||||
|
.ancestors()
|
||||||
|
.any(|ancestor| self.watches.contains(ancestor))
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
self.watches.insert(path.to_path_buf());
|
||||||
|
self.watcher
|
||||||
|
.watch(path, RecursiveMode::Recursive)
|
||||||
|
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwatch(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
self.watches.remove(path);
|
||||||
|
self.watcher
|
||||||
|
.unwatch(path)
|
||||||
|
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StdBackend {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
10
crates/rojo-insta-ext/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "rojo-insta-ext"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "1.0.99"
|
||||||
|
serde_yaml = "0.8.9"
|
||||||
2
crates/rojo-insta-ext/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Rojo Insta Extensions
|
||||||
|
This crate has add-ons intended for use with Insta that are useful for snapshot tests.
|
||||||
3
crates/rojo-insta-ext/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
mod redaction_map;
|
||||||
|
|
||||||
|
pub use redaction_map::*;
|
||||||
85
crates/rojo-insta-ext/src/redaction_map.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Enables redacting any value that serializes as a string.
|
||||||
|
///
|
||||||
|
/// Used for transforming Rojo instance IDs into something deterministic.
|
||||||
|
pub struct RedactionMap {
|
||||||
|
ids: HashMap<String, usize>,
|
||||||
|
last_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RedactionMap {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ids: HashMap::new(),
|
||||||
|
last_id: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_redacted_value(&self, id: impl ToString) -> Option<String> {
|
||||||
|
let id = id.to_string();
|
||||||
|
|
||||||
|
if self.ids.contains_key(&id) {
|
||||||
|
Some(id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intern(&mut self, id: impl ToString) {
|
||||||
|
let last_id = &mut self.last_id;
|
||||||
|
|
||||||
|
self.ids.entry(id.to_string()).or_insert_with(|| {
|
||||||
|
*last_id += 1;
|
||||||
|
*last_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intern_iter<S: ToString>(&mut self, ids: impl Iterator<Item = S>) {
|
||||||
|
for id in ids {
|
||||||
|
self.intern(id.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redacted_yaml(&self, value: impl Serialize) -> serde_yaml::Value {
|
||||||
|
let mut encoded = serde_yaml::to_value(value).expect("Couldn't encode value as YAML");
|
||||||
|
|
||||||
|
self.redact(&mut encoded);
|
||||||
|
encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redact(&self, yaml_value: &mut serde_yaml::Value) {
|
||||||
|
use serde_yaml::{Mapping, Value};
|
||||||
|
|
||||||
|
match yaml_value {
|
||||||
|
Value::String(value) => {
|
||||||
|
if let Some(redacted) = self.ids.get(value) {
|
||||||
|
*yaml_value = Value::String(format!("id-{}", *redacted));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Sequence(sequence) => {
|
||||||
|
for value in sequence {
|
||||||
|
self.redact(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Mapping(mapping) => {
|
||||||
|
// We can't mutate the keys of a map in-place, so we take
|
||||||
|
// ownership of the map and rebuild it.
|
||||||
|
|
||||||
|
let owned_map = std::mem::replace(mapping, Mapping::new());
|
||||||
|
let mut new_map = Mapping::with_capacity(owned_map.len());
|
||||||
|
|
||||||
|
for (mut key, mut value) in owned_map {
|
||||||
|
self.redact(&mut key);
|
||||||
|
self.redact(&mut value);
|
||||||
|
new_map.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
*mapping = new_map;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
plugin/Packages/Flipper
Submodule
1
plugin/Packages/Highlighter
Submodule
1
plugin/Packages/Promise
Submodule
1
plugin/Packages/Roact
Submodule
1
plugin/Packages/TestEZ
Submodule
1
plugin/Packages/t
Submodule
1
plugin/Version.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
7.4.3
|
||||||
27
plugin/default.project.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "Rojo",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"Plugin": {
|
||||||
|
"$path": "src"
|
||||||
|
},
|
||||||
|
"Packages": {
|
||||||
|
"$path": "Packages",
|
||||||
|
"Log": {
|
||||||
|
"$path": "log"
|
||||||
|
},
|
||||||
|
"Http": {
|
||||||
|
"$path": "http"
|
||||||
|
},
|
||||||
|
"Fmt": {
|
||||||
|
"$path": "fmt"
|
||||||
|
},
|
||||||
|
"RbxDom": {
|
||||||
|
"$path": "rbx_dom_lua"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Version": {
|
||||||
|
"$path": "Version.txt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
245
plugin/fmt/init.lua
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
--[[
|
||||||
|
This library describes a formatting mechanism akin to Rust's std::fmt.
|
||||||
|
|
||||||
|
It has a couple building blocks:
|
||||||
|
|
||||||
|
* A new syntax for formatting strings, taken verbatim from Rust. It'd also
|
||||||
|
be possible to use printf-style formatting specifiers to integrate with
|
||||||
|
the existing string.format utility.
|
||||||
|
|
||||||
|
* An equivalent to Rust's `Display` trait. We're mapping the semantics of
|
||||||
|
tostring and the __tostring metamethod onto this trait. A lot of types
|
||||||
|
should already have __tostring implementations, too!
|
||||||
|
|
||||||
|
* An equivalent to Rust's `Debug` trait. This library Lua-ifies that idea by
|
||||||
|
inventing a new metamethod, `__fmtDebug`. We pass along the "extended
|
||||||
|
form" attribute which is the equivalent of the "alternate mode" in Rust's
|
||||||
|
Debug trait since it's the author's opinion that treating it as a
|
||||||
|
verbosity flag is semantically accurate.
|
||||||
|
]]
|
||||||
|
|
||||||
|
--[[
|
||||||
|
The default implementation of __fmtDebug for tables when the extended option
|
||||||
|
is not set.
|
||||||
|
]]
|
||||||
|
local function defaultTableDebug(buffer, input)
|
||||||
|
buffer:writeRaw("{")
|
||||||
|
|
||||||
|
for key, value in pairs(input) do
|
||||||
|
buffer:write("[{:?}] = {:?}", key, value)
|
||||||
|
|
||||||
|
if next(input, key) ~= nil then
|
||||||
|
buffer:writeRaw(", ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
buffer:writeRaw("}")
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
The default implementation of __fmtDebug for tables with the extended option
|
||||||
|
set.
|
||||||
|
]]
|
||||||
|
local function defaultTableDebugExtended(buffer, input)
|
||||||
|
-- Special case for empty tables.
|
||||||
|
if next(input) == nil then
|
||||||
|
buffer:writeRaw("{}")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
buffer:writeLineRaw("{")
|
||||||
|
buffer:indent()
|
||||||
|
|
||||||
|
for key, value in pairs(input) do
|
||||||
|
buffer:writeLine("[{:?}] = {:#?},", key, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
buffer:unindent()
|
||||||
|
buffer:writeRaw("}")
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
The default debug representation for all types.
|
||||||
|
]]
|
||||||
|
local function debugImpl(buffer, value, extendedForm)
|
||||||
|
local valueType = typeof(value)
|
||||||
|
|
||||||
|
if valueType == "string" then
|
||||||
|
local formatted = string.format("%q", value)
|
||||||
|
buffer:writeRaw(formatted)
|
||||||
|
elseif valueType == "table" then
|
||||||
|
local valueMeta = getmetatable(value)
|
||||||
|
|
||||||
|
if valueMeta ~= nil and valueMeta.__fmtDebug ~= nil then
|
||||||
|
-- This type implement's the metamethod we made up to line up with
|
||||||
|
-- Rust's 'Debug' trait.
|
||||||
|
|
||||||
|
valueMeta.__fmtDebug(value, buffer, extendedForm)
|
||||||
|
else
|
||||||
|
if extendedForm then
|
||||||
|
defaultTableDebugExtended(buffer, value)
|
||||||
|
else
|
||||||
|
defaultTableDebug(buffer, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif valueType == "Instance" then
|
||||||
|
buffer:writeRaw(value:GetFullName())
|
||||||
|
else
|
||||||
|
buffer:writeRaw(tostring(value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Defines and implements the library's template syntax.
|
||||||
|
]]
|
||||||
|
local function writeFmt(buffer, template, ...)
|
||||||
|
local currentArg = 0
|
||||||
|
local i = 1
|
||||||
|
local len = #template
|
||||||
|
|
||||||
|
while i <= len do
|
||||||
|
local openBrace = template:find("{", i)
|
||||||
|
|
||||||
|
if openBrace == nil then
|
||||||
|
-- There are no remaining open braces in this string, so we can
|
||||||
|
-- write the rest of the string to the buffer.
|
||||||
|
|
||||||
|
buffer:writeRaw(template:sub(i))
|
||||||
|
break
|
||||||
|
else
|
||||||
|
-- We found an open brace! This could be:
|
||||||
|
-- - A literal '{', written as '{{'
|
||||||
|
-- - The beginning of an interpolation, like '{}'
|
||||||
|
-- - An error, if there's no matching '}'
|
||||||
|
|
||||||
|
local charAfterBrace = template:sub(openBrace + 1, openBrace + 1)
|
||||||
|
if charAfterBrace == "{" then
|
||||||
|
-- This is a literal brace, so we'll write everything up to this
|
||||||
|
-- point (including the first brace), and then skip over the
|
||||||
|
-- second brace.
|
||||||
|
|
||||||
|
buffer:writeRaw(template:sub(i, openBrace))
|
||||||
|
i = openBrace + 2
|
||||||
|
else
|
||||||
|
-- This SHOULD be an interpolation. We'll find our matching
|
||||||
|
-- brace and treat the contents as the formatting specifier.
|
||||||
|
|
||||||
|
-- If there were any unwritten characters before this
|
||||||
|
-- interpolation, write them to the buffer.
|
||||||
|
if openBrace - i > 0 then
|
||||||
|
buffer:writeRaw(template:sub(i, openBrace - 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
local closeBrace = template:find("}", openBrace + 1)
|
||||||
|
assert(closeBrace ~= nil, "Unclosed formatting specifier. Use '{{' to write an open brace.")
|
||||||
|
|
||||||
|
local formatSpecifier = template:sub(openBrace + 1, closeBrace - 1)
|
||||||
|
currentArg = currentArg + 1
|
||||||
|
local arg = select(currentArg, ...)
|
||||||
|
|
||||||
|
if formatSpecifier == "" then
|
||||||
|
-- This should use the equivalent of Rust's 'Display', ie
|
||||||
|
-- tostring and the __tostring metamethod.
|
||||||
|
|
||||||
|
buffer:writeRaw(tostring(arg))
|
||||||
|
elseif formatSpecifier == ":?" then
|
||||||
|
-- This should use the equivalent of Rust's 'Debug',
|
||||||
|
-- invented for this library as __fmtDebug.
|
||||||
|
|
||||||
|
debugImpl(buffer, arg, false)
|
||||||
|
elseif formatSpecifier == ":#?" then
|
||||||
|
-- This should use the equivlant of Rust's 'Debug' with the
|
||||||
|
-- 'alternate' (ie expanded) flag set.
|
||||||
|
|
||||||
|
debugImpl(buffer, arg, true)
|
||||||
|
else
|
||||||
|
error("unsupported format specifier " .. formatSpecifier, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
i = closeBrace + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function debugOutputBuffer()
|
||||||
|
local buffer = {}
|
||||||
|
local startOfLine = true
|
||||||
|
local indentLevel = 0
|
||||||
|
local indentation = ""
|
||||||
|
|
||||||
|
function buffer:writeLine(template, ...)
|
||||||
|
writeFmt(self, template, ...)
|
||||||
|
self:nextLine()
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:writeLineRaw(value)
|
||||||
|
self:writeRaw(value)
|
||||||
|
self:nextLine()
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:write(template, ...)
|
||||||
|
return writeFmt(self, template, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:writeRaw(value)
|
||||||
|
if #value > 0 then
|
||||||
|
if startOfLine and #indentation > 0 then
|
||||||
|
startOfLine = false
|
||||||
|
table.insert(self, indentation)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self, value)
|
||||||
|
startOfLine = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:nextLine()
|
||||||
|
table.insert(self, "\n")
|
||||||
|
startOfLine = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:indent()
|
||||||
|
indentLevel = indentLevel + 1
|
||||||
|
indentation = string.rep(" ", indentLevel)
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:unindent()
|
||||||
|
indentLevel = math.max(0, indentLevel - 1)
|
||||||
|
indentation = string.rep(" ", indentLevel)
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:finish()
|
||||||
|
return table.concat(self, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fmt(template, ...)
|
||||||
|
local buffer = debugOutputBuffer()
|
||||||
|
writeFmt(buffer, template, ...)
|
||||||
|
return buffer:finish()
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Wrap the given object in a type that implements the given function as its
|
||||||
|
Debug implementation, and forwards __tostring to the type's underlying
|
||||||
|
tostring implementation.
|
||||||
|
]]
|
||||||
|
local function debugify(object, fmtFunc)
|
||||||
|
return setmetatable({}, {
|
||||||
|
__fmtDebug = function(_, ...)
|
||||||
|
return fmtFunc(object, ...)
|
||||||
|
end,
|
||||||
|
__tostring = function()
|
||||||
|
return tostring(object)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
debugOutputBuffer = debugOutputBuffer,
|
||||||
|
fmt = fmt,
|
||||||
|
debugify = debugify,
|
||||||
|
}
|
||||||
75
plugin/http/Error.lua
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
local Error = {}
|
||||||
|
Error.__index = Error
|
||||||
|
|
||||||
|
Error.Kind = {
|
||||||
|
HttpNotEnabled = {
|
||||||
|
message = "Rojo requires HTTP access, which is not enabled.\n"
|
||||||
|
.. "Check your game settings, located in the 'Home' tab of Studio.",
|
||||||
|
},
|
||||||
|
ConnectFailed = {
|
||||||
|
message = "Couldn't connect to the Rojo server.\n"
|
||||||
|
.. "Make sure the server is running — use 'rojo serve' to run it!",
|
||||||
|
},
|
||||||
|
Timeout = {
|
||||||
|
message = "HTTP request timed out.",
|
||||||
|
},
|
||||||
|
Unknown = {
|
||||||
|
message = "Unknown HTTP error: {{message}}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(Error.Kind, {
|
||||||
|
__index = function(_, key)
|
||||||
|
error(("%q is not a valid member of Http.Error.Kind"):format(tostring(key)), 2)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
function Error.new(type, extraMessage)
|
||||||
|
extraMessage = extraMessage or ""
|
||||||
|
local message = type.message:gsub("{{message}}", extraMessage)
|
||||||
|
|
||||||
|
local err = {
|
||||||
|
type = type,
|
||||||
|
message = message,
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(err, Error)
|
||||||
|
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
|
||||||
|
function Error:__tostring()
|
||||||
|
return self.message
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
This method shouldn't have to exist. Ugh.
|
||||||
|
]]
|
||||||
|
function Error.fromRobloxErrorString(message)
|
||||||
|
local lower = message:lower()
|
||||||
|
|
||||||
|
if lower:find("^http requests are not enabled") then
|
||||||
|
return Error.new(Error.Kind.HttpNotEnabled)
|
||||||
|
end
|
||||||
|
|
||||||
|
if lower:find("^httperror: timedout") then
|
||||||
|
return Error.new(Error.Kind.Timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
if lower:find("^httperror: connectfail") then
|
||||||
|
return Error.new(Error.Kind.ConnectFailed)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Error.new(Error.Kind.Unknown, message)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Error.fromResponse(response)
|
||||||
|
local lower = (response.body or ""):lower()
|
||||||
|
if response.code == 408 or response.code == 504 or lower:find("timed? ?out") then
|
||||||
|
return Error.new(Error.Kind.Timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Error.new(Error.Kind.Unknown, string.format("%s: %s", tostring(response.code), tostring(response.body)))
|
||||||
|
end
|
||||||
|
|
||||||
|
return Error
|
||||||
34
plugin/http/Response.lua
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
local HttpService = game:GetService("HttpService")
|
||||||
|
|
||||||
|
local stringTemplate = [[
|
||||||
|
Http.Response {
|
||||||
|
code: %d
|
||||||
|
body: %s
|
||||||
|
}]]
|
||||||
|
|
||||||
|
local Response = {}
|
||||||
|
Response.__index = Response
|
||||||
|
|
||||||
|
function Response:__tostring()
|
||||||
|
return stringTemplate:format(self.code, self.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Response.fromRobloxResponse(response)
|
||||||
|
local self = {
|
||||||
|
body = response.Body,
|
||||||
|
code = response.StatusCode,
|
||||||
|
headers = response.Headers,
|
||||||
|
}
|
||||||
|
|
||||||
|
return setmetatable(self, Response)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Response:isSuccess()
|
||||||
|
return self.code >= 200 and self.code < 300
|
||||||
|
end
|
||||||
|
|
||||||
|
function Response:json()
|
||||||
|
return HttpService:JSONDecode(self.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Response
|
||||||
71
plugin/http/init.lua
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
local HttpService = game:GetService("HttpService")
|
||||||
|
|
||||||
|
local Promise = require(script.Parent.Promise)
|
||||||
|
local Log = require(script.Parent.Log)
|
||||||
|
|
||||||
|
local HttpError = require(script.Error)
|
||||||
|
local HttpResponse = require(script.Response)
|
||||||
|
|
||||||
|
local lastRequestId = 0
|
||||||
|
|
||||||
|
local Http = {}
|
||||||
|
|
||||||
|
Http.Error = HttpError
|
||||||
|
Http.Response = HttpResponse
|
||||||
|
|
||||||
|
local function performRequest(requestParams)
|
||||||
|
local requestId = lastRequestId + 1
|
||||||
|
lastRequestId = requestId
|
||||||
|
|
||||||
|
Log.trace("HTTP {}({}) {}", requestParams.Method, requestId, requestParams.Url)
|
||||||
|
|
||||||
|
if requestParams.Body ~= nil then
|
||||||
|
Log.trace("{}", requestParams.Body)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Promise.new(function(resolve, reject)
|
||||||
|
coroutine.wrap(function()
|
||||||
|
local success, response = pcall(function()
|
||||||
|
return HttpService:RequestAsync(requestParams)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
Log.trace("Request {} success, response {:#?}", requestId, response)
|
||||||
|
local httpResponse = HttpResponse.fromRobloxResponse(response)
|
||||||
|
if httpResponse:isSuccess() then
|
||||||
|
resolve(httpResponse)
|
||||||
|
else
|
||||||
|
reject(HttpError.fromResponse(httpResponse))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Log.trace("Request {} failure: {:?}", requestId, response)
|
||||||
|
reject(HttpError.fromRobloxErrorString(response))
|
||||||
|
end
|
||||||
|
end)()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Http.get(url)
|
||||||
|
return performRequest({
|
||||||
|
Url = url,
|
||||||
|
Method = "GET",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Http.post(url, body)
|
||||||
|
return performRequest({
|
||||||
|
Url = url,
|
||||||
|
Method = "POST",
|
||||||
|
Body = body,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Http.jsonEncode(object)
|
||||||
|
return HttpService:JSONEncode(object)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Http.jsonDecode(source)
|
||||||
|
return HttpService:JSONDecode(source)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Http
|
||||||
5
plugin/http/init.spec.lua
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
return function()
|
||||||
|
it("should load", function()
|
||||||
|
require(script.Parent)
|
||||||
|
end)
|
||||||
|
end
|
||||||
60
plugin/log/init.lua
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
local Fmt = require(script.Parent.Fmt)
|
||||||
|
|
||||||
|
local Level = {
|
||||||
|
Error = 0,
|
||||||
|
Warning = 1,
|
||||||
|
Info = 2,
|
||||||
|
Debug = 3,
|
||||||
|
Trace = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function getLogLevel()
|
||||||
|
return Level.Info
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addTags(tag, message)
|
||||||
|
return tag .. message:gsub("\n", "\n" .. tag)
|
||||||
|
end
|
||||||
|
|
||||||
|
local TRACE_TAG = (" "):rep(15) .. "[Rojo-Trace] "
|
||||||
|
local INFO_TAG = (" "):rep(15) .. "[Rojo-Info] "
|
||||||
|
local DEBUG_TAG = (" "):rep(15) .. "[Rojo-Debug] "
|
||||||
|
local WARN_TAG = "[Rojo-Warn] "
|
||||||
|
|
||||||
|
local Log = {}
|
||||||
|
|
||||||
|
Log.Level = Level
|
||||||
|
|
||||||
|
function Log.setLogLevelThunk(thunk)
|
||||||
|
getLogLevel = thunk
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.trace(template, ...)
|
||||||
|
if getLogLevel() >= Level.Trace then
|
||||||
|
print(addTags(TRACE_TAG, Fmt.fmt(template, ...)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.info(template, ...)
|
||||||
|
if getLogLevel() >= Level.Info then
|
||||||
|
print(addTags(INFO_TAG, Fmt.fmt(template, ...)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.debug(template, ...)
|
||||||
|
if getLogLevel() >= Level.Debug then
|
||||||
|
print(addTags(DEBUG_TAG, Fmt.fmt(template, ...)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.warn(template, ...)
|
||||||
|
if getLogLevel() >= Level.Warning then
|
||||||
|
warn(addTags(WARN_TAG, Fmt.fmt(template, ...)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.error(template, ...)
|
||||||
|
error(Fmt.fmt(template, ...))
|
||||||
|
end
|
||||||
|
|
||||||
|
return Log
|
||||||
5
plugin/log/init.spec.lua
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
return function()
|
||||||
|
it("should load", function()
|
||||||
|
require(script.Parent)
|
||||||
|
end)
|
||||||
|
end
|
||||||
558
plugin/rbx_dom_lua/EncodedValue.lua
Normal file
@@ -0,0 +1,558 @@
|
|||||||
|
local base64 = require(script.Parent.base64)
|
||||||
|
|
||||||
|
local function identity(...)
|
||||||
|
return ...
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpackDecoder(f)
|
||||||
|
return function(value)
|
||||||
|
return f(unpack(value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serializeFloat(value)
|
||||||
|
-- TODO: Figure out a better way to serialize infinity and NaN, neither of
|
||||||
|
-- which fit into JSON.
|
||||||
|
if value == math.huge or value == -math.huge then
|
||||||
|
return 999999999 * math.sign(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
local ALL_AXES = { "X", "Y", "Z" }
|
||||||
|
local ALL_FACES = { "Right", "Top", "Back", "Left", "Bottom", "Front" }
|
||||||
|
|
||||||
|
local EncodedValue = {}
|
||||||
|
|
||||||
|
local types
|
||||||
|
types = {
|
||||||
|
Attributes = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local output = {}
|
||||||
|
|
||||||
|
for key, value in pairs(pod) do
|
||||||
|
local ok, result = EncodedValue.decode(value)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
output[key] = result
|
||||||
|
else
|
||||||
|
local warning = ("Could not decode attribute value of type %q: %s"):format(
|
||||||
|
typeof(value),
|
||||||
|
tostring(result)
|
||||||
|
)
|
||||||
|
warn(warning)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end,
|
||||||
|
toPod = function(roblox)
|
||||||
|
local output = {}
|
||||||
|
|
||||||
|
for key, value in pairs(roblox) do
|
||||||
|
local ok, result = EncodedValue.encodeNaive(value)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
output[key] = result
|
||||||
|
else
|
||||||
|
local warning = ("Could not encode attribute value of type %q: %s"):format(
|
||||||
|
typeof(value),
|
||||||
|
tostring(result)
|
||||||
|
)
|
||||||
|
warn(warning)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Axes = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local axes = {}
|
||||||
|
|
||||||
|
for index, axisName in ipairs(pod) do
|
||||||
|
axes[index] = Enum.Axis[axisName]
|
||||||
|
end
|
||||||
|
|
||||||
|
return Axes.new(unpack(axes))
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
local json = {}
|
||||||
|
|
||||||
|
for _, axis in ipairs(ALL_AXES) do
|
||||||
|
if roblox[axis] then
|
||||||
|
table.insert(json, axis)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return json
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
BinaryString = {
|
||||||
|
fromPod = base64.decode,
|
||||||
|
toPod = base64.encode,
|
||||||
|
},
|
||||||
|
|
||||||
|
Bool = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
BrickColor = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return BrickColor.new(pod)
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return roblox.Number
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
CFrame = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local pos = pod.position
|
||||||
|
local orient = pod.orientation
|
||||||
|
|
||||||
|
--stylua: ignore
|
||||||
|
return CFrame.new(
|
||||||
|
pos[1], pos[2], pos[3],
|
||||||
|
orient[1][1], orient[1][2], orient[1][3],
|
||||||
|
orient[2][1], orient[2][2], orient[2][3],
|
||||||
|
orient[3][1], orient[3][2], orient[3][3]
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = roblox:GetComponents()
|
||||||
|
|
||||||
|
return {
|
||||||
|
position = { x, y, z },
|
||||||
|
orientation = {
|
||||||
|
{ r00, r01, r02 },
|
||||||
|
{ r10, r11, r12 },
|
||||||
|
{ r20, r21, r22 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Color3 = {
|
||||||
|
fromPod = unpackDecoder(Color3.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return { roblox.r, roblox.g, roblox.b }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Color3uint8 = {
|
||||||
|
fromPod = unpackDecoder(Color3.fromRGB),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
math.round(roblox.R * 255),
|
||||||
|
math.round(roblox.G * 255),
|
||||||
|
math.round(roblox.B * 255),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
ColorSequence = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local keypoints = {}
|
||||||
|
|
||||||
|
for index, keypoint in ipairs(pod.keypoints) do
|
||||||
|
keypoints[index] = ColorSequenceKeypoint.new(keypoint.time, types.Color3.fromPod(keypoint.color))
|
||||||
|
end
|
||||||
|
|
||||||
|
return ColorSequence.new(keypoints)
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
local keypoints = {}
|
||||||
|
|
||||||
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
|
keypoints[index] = {
|
||||||
|
time = keypoint.Time,
|
||||||
|
color = types.Color3.toPod(keypoint.Value),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
keypoints = keypoints,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Content = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
Enum = {
|
||||||
|
fromPod = identity,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
-- FIXME: More robust handling of enums
|
||||||
|
if typeof(roblox) == "number" then
|
||||||
|
return roblox
|
||||||
|
else
|
||||||
|
return roblox.Value
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Faces = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local faces = {}
|
||||||
|
|
||||||
|
for index, faceName in ipairs(pod) do
|
||||||
|
faces[index] = Enum.NormalId[faceName]
|
||||||
|
end
|
||||||
|
|
||||||
|
return Faces.new(unpack(faces))
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
local pod = {}
|
||||||
|
|
||||||
|
for _, face in ipairs(ALL_FACES) do
|
||||||
|
if roblox[face] then
|
||||||
|
table.insert(pod, face)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return pod
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Float32 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = serializeFloat,
|
||||||
|
},
|
||||||
|
|
||||||
|
Float64 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = serializeFloat,
|
||||||
|
},
|
||||||
|
|
||||||
|
Font = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return Font.new(
|
||||||
|
pod.family,
|
||||||
|
if pod.weight ~= nil then Enum.FontWeight[pod.weight] else nil,
|
||||||
|
if pod.style ~= nil then Enum.FontStyle[pod.style] else nil
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
family = roblox.Family,
|
||||||
|
weight = roblox.Weight.Name,
|
||||||
|
style = roblox.Style.Name,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Int32 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
Int64 = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
MaterialColors = {
|
||||||
|
fromPod = function(pod: { [string]: { number } })
|
||||||
|
local real = {}
|
||||||
|
for name, color in pod do
|
||||||
|
real[Enum.Material[name]] = Color3.fromRGB(color[1], color[2], color[3])
|
||||||
|
end
|
||||||
|
return real
|
||||||
|
end,
|
||||||
|
toPod = function(roblox: { [Enum.Material]: Color3 })
|
||||||
|
local pod = {}
|
||||||
|
for material, color in roblox do
|
||||||
|
pod[material.Name] = {
|
||||||
|
math.round(math.clamp(color.R, 0, 1) * 255),
|
||||||
|
math.round(math.clamp(color.G, 0, 1) * 255),
|
||||||
|
math.round(math.clamp(color.B, 0, 1) * 255),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return pod
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
NumberRange = {
|
||||||
|
fromPod = unpackDecoder(NumberRange.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return { roblox.Min, roblox.Max }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
NumberSequence = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local keypoints = {}
|
||||||
|
|
||||||
|
for index, keypoint in ipairs(pod.keypoints) do
|
||||||
|
keypoints[index] = NumberSequenceKeypoint.new(keypoint.time, keypoint.value, keypoint.envelope)
|
||||||
|
end
|
||||||
|
|
||||||
|
return NumberSequence.new(keypoints)
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
local keypoints = {}
|
||||||
|
|
||||||
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
|
keypoints[index] = {
|
||||||
|
time = keypoint.Time,
|
||||||
|
value = keypoint.Value,
|
||||||
|
envelope = keypoint.Envelope,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
keypoints = keypoints,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
PhysicalProperties = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
if pod == "Default" then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
return PhysicalProperties.new(
|
||||||
|
pod.density,
|
||||||
|
pod.friction,
|
||||||
|
pod.elasticity,
|
||||||
|
pod.frictionWeight,
|
||||||
|
pod.elasticityWeight
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
if roblox == nil then
|
||||||
|
return "Default"
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
density = roblox.Density,
|
||||||
|
friction = roblox.Friction,
|
||||||
|
elasticity = roblox.Elasticity,
|
||||||
|
frictionWeight = roblox.FrictionWeight,
|
||||||
|
elasticityWeight = roblox.ElasticityWeight,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Ray = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return Ray.new(types.Vector3.fromPod(pod.origin), types.Vector3.fromPod(pod.direction))
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
origin = types.Vector3.toPod(roblox.Origin),
|
||||||
|
direction = types.Vector3.toPod(roblox.Direction),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Rect = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return Rect.new(types.Vector2.fromPod(pod[1]), types.Vector2.fromPod(pod[2]))
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
types.Vector2.toPod(roblox.Min),
|
||||||
|
types.Vector2.toPod(roblox.Max),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Ref = {
|
||||||
|
fromPod = function(_)
|
||||||
|
error("Ref cannot be decoded on its own")
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(_)
|
||||||
|
error("Ref can not be encoded on its own")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Region3 = {
|
||||||
|
fromPod = function(_)
|
||||||
|
error("Region3 is not implemented")
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(_)
|
||||||
|
error("Region3 is not implemented")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Region3int16 = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return Region3int16.new(types.Vector3int16.fromPod(pod[1]), types.Vector3int16.fromPod(pod[2]))
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
types.Vector3int16.toPod(roblox.Min),
|
||||||
|
types.Vector3int16.toPod(roblox.Max),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
SharedString = {
|
||||||
|
fromPod = function(_pod)
|
||||||
|
error("SharedString is not supported")
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(_roblox)
|
||||||
|
error("SharedString is not supported")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
String = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
UDim = {
|
||||||
|
fromPod = unpackDecoder(UDim.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return { roblox.Scale, roblox.Offset }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
UDim2 = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
return UDim2.new(types.UDim.fromPod(pod[1]), types.UDim.fromPod(pod[2]))
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
types.UDim.toPod(roblox.X),
|
||||||
|
types.UDim.toPod(roblox.Y),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Tags = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector2 = {
|
||||||
|
fromPod = unpackDecoder(Vector2.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
serializeFloat(roblox.X),
|
||||||
|
serializeFloat(roblox.Y),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector2int16 = {
|
||||||
|
fromPod = unpackDecoder(Vector2int16.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return { roblox.X, roblox.Y }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector3 = {
|
||||||
|
fromPod = unpackDecoder(Vector3.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return {
|
||||||
|
serializeFloat(roblox.X),
|
||||||
|
serializeFloat(roblox.Y),
|
||||||
|
serializeFloat(roblox.Z),
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vector3int16 = {
|
||||||
|
fromPod = unpackDecoder(Vector3int16.new),
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
return { roblox.X, roblox.Y, roblox.Z }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
types.OptionalCFrame = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
if pod == nil then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
return types.CFrame.fromPod(pod)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
toPod = function(roblox)
|
||||||
|
if roblox == nil then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
return types.CFrame.toPod(roblox)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
function EncodedValue.decode(encodedValue)
|
||||||
|
local ty, value = next(encodedValue)
|
||||||
|
|
||||||
|
if ty == nil then
|
||||||
|
-- If the encoded pair is empty, assume it is an unoccupied optional value
|
||||||
|
return true, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local typeImpl = types[ty]
|
||||||
|
if typeImpl == nil then
|
||||||
|
return false, "Couldn't decode value " .. tostring(ty)
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, typeImpl.fromPod(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
function EncodedValue.encode(rbxValue, propertyType)
|
||||||
|
assert(propertyType ~= nil, "Property type descriptor is required")
|
||||||
|
|
||||||
|
local typeImpl = types[propertyType]
|
||||||
|
if typeImpl == nil then
|
||||||
|
return false, ("Missing encoder for property type %q"):format(propertyType)
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, {
|
||||||
|
[propertyType] = typeImpl.toPod(rbxValue),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local propertyTypeRenames = {
|
||||||
|
number = "Float64",
|
||||||
|
boolean = "Bool",
|
||||||
|
string = "String",
|
||||||
|
}
|
||||||
|
|
||||||
|
function EncodedValue.encodeNaive(rbxValue)
|
||||||
|
local propertyType = typeof(rbxValue)
|
||||||
|
if propertyTypeRenames[propertyType] ~= nil then
|
||||||
|
propertyType = propertyTypeRenames[propertyType]
|
||||||
|
end
|
||||||
|
|
||||||
|
return EncodedValue.encode(rbxValue, propertyType)
|
||||||
|
end
|
||||||
|
|
||||||
|
return EncodedValue
|
||||||
28
plugin/rbx_dom_lua/Error.lua
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
local Error = {}
|
||||||
|
Error.__index = Error
|
||||||
|
|
||||||
|
Error.Kind = {
|
||||||
|
UnknownProperty = "UnknownProperty",
|
||||||
|
PropertyNotReadable = "PropertyNotReadable",
|
||||||
|
PropertyNotWritable = "PropertyNotWritable",
|
||||||
|
Roblox = "Roblox",
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(Error.Kind, {
|
||||||
|
__index = function(_, key)
|
||||||
|
error(("%q is not a valid member of Error.Kind"):format(tostring(key)), 2)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
function Error.new(kind, extra)
|
||||||
|
return setmetatable({
|
||||||
|
kind = kind,
|
||||||
|
extra = extra,
|
||||||
|
}, Error)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Error:__tostring()
|
||||||
|
return ("Error(%s: %s)"):format(self.kind, tostring(self.extra))
|
||||||
|
end
|
||||||
|
|
||||||
|
return Error
|
||||||
104
plugin/rbx_dom_lua/PropertyDescriptor.lua
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
local Error = require(script.Parent.Error)
|
||||||
|
local customProperties = require(script.Parent.customProperties)
|
||||||
|
|
||||||
|
-- A wrapper around a property descriptor from the reflection database with some
|
||||||
|
-- extra convenience methods.
|
||||||
|
--
|
||||||
|
-- The aim of this API is to facilitate looking up a property once, then reading
|
||||||
|
-- from it or writing to it multiple times. It's also useful when a consumer
|
||||||
|
-- wants to check additional constraints on the property before trying to use
|
||||||
|
-- it, like scriptability.
|
||||||
|
local PropertyDescriptor = {}
|
||||||
|
PropertyDescriptor.__index = PropertyDescriptor
|
||||||
|
|
||||||
|
local function get(container, key)
|
||||||
|
return container[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set(container, key, value)
|
||||||
|
container[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
||||||
|
local key, value = next(data.DataType)
|
||||||
|
|
||||||
|
return setmetatable({
|
||||||
|
-- The meanings of the key and value in DataType differ when the type of
|
||||||
|
-- the property is Enum. When the property is of type Enum, the key is
|
||||||
|
-- the name of the type:
|
||||||
|
--
|
||||||
|
-- { Enum = "<name of enum>" }
|
||||||
|
--
|
||||||
|
-- When the property is not of type Enum, the value is the name of the
|
||||||
|
-- type:
|
||||||
|
--
|
||||||
|
-- { Value = "<data type>" }
|
||||||
|
dataType = key == "Enum" and key or value,
|
||||||
|
|
||||||
|
scriptability = data.Scriptability,
|
||||||
|
className = className,
|
||||||
|
name = propertyName,
|
||||||
|
}, PropertyDescriptor)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PropertyDescriptor:read(instance)
|
||||||
|
if self.scriptability == "ReadWrite" or self.scriptability == "Read" then
|
||||||
|
local success, value = xpcall(get, debug.traceback, instance, self.name)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
return success, value
|
||||||
|
else
|
||||||
|
return false, Error.new(Error.Kind.Roblox, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.scriptability == "Custom" then
|
||||||
|
if customProperties[self.className] == nil then
|
||||||
|
local fullName = ("%s.%s"):format(instance.className, self.name)
|
||||||
|
return false, Error.new(Error.Kind.PropertyNotReadable, fullName)
|
||||||
|
end
|
||||||
|
|
||||||
|
local interface = customProperties[self.className][self.name]
|
||||||
|
|
||||||
|
return interface.read(instance, self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.scriptability == "None" or self.scriptability == "Write" then
|
||||||
|
local fullName = ("%s.%s"):format(instance.className, self.name)
|
||||||
|
|
||||||
|
return false, Error.new(Error.Kind.PropertyNotReadable, fullName)
|
||||||
|
end
|
||||||
|
|
||||||
|
error(("Internal error: unexpected value of 'scriptability': %s"):format(tostring(self.scriptability)), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PropertyDescriptor:write(instance, value)
|
||||||
|
if self.scriptability == "ReadWrite" or self.scriptability == "Write" then
|
||||||
|
local success, err = xpcall(set, debug.traceback, instance, self.name, value)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
return success
|
||||||
|
else
|
||||||
|
return false, Error.new(Error.Kind.Roblox, err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.scriptability == "Custom" then
|
||||||
|
if customProperties[self.className] == nil then
|
||||||
|
local fullName = ("%s.%s"):format(instance.className, self.name)
|
||||||
|
return false, Error.new(Error.Kind.PropertyNotWritable, fullName)
|
||||||
|
end
|
||||||
|
|
||||||
|
local interface = customProperties[self.className][self.name]
|
||||||
|
|
||||||
|
return interface.write(instance, self.name, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.scriptability == "None" or self.scriptability == "Read" then
|
||||||
|
local fullName = ("%s.%s"):format(instance.className, self.name)
|
||||||
|
|
||||||
|
return false, Error.new(Error.Kind.PropertyNotWritable, fullName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return PropertyDescriptor
|
||||||
553
plugin/rbx_dom_lua/allValues.json
Normal file
@@ -0,0 +1,553 @@
|
|||||||
|
{
|
||||||
|
"Attributes": {
|
||||||
|
"value": {
|
||||||
|
"Attributes": {
|
||||||
|
"TestBool": {
|
||||||
|
"Bool": true
|
||||||
|
},
|
||||||
|
"TestBrickColor": {
|
||||||
|
"BrickColor": 24
|
||||||
|
},
|
||||||
|
"TestColor3": {
|
||||||
|
"Color3": [
|
||||||
|
1.0,
|
||||||
|
0.5,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestNumber": {
|
||||||
|
"Float64": 1337.0
|
||||||
|
},
|
||||||
|
"TestRect": {
|
||||||
|
"Rect": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
2.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.0,
|
||||||
|
4.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestString": {
|
||||||
|
"String": "Test"
|
||||||
|
},
|
||||||
|
"TestUDim": {
|
||||||
|
"UDim": [
|
||||||
|
1.0,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestUDim2": {
|
||||||
|
"UDim2": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.0,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestVector2": {
|
||||||
|
"Vector2": [
|
||||||
|
1.0,
|
||||||
|
2.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestVector3": {
|
||||||
|
"Vector3": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "Attributes"
|
||||||
|
},
|
||||||
|
"Axes": {
|
||||||
|
"value": {
|
||||||
|
"Axes": [
|
||||||
|
"X",
|
||||||
|
"Y",
|
||||||
|
"Z"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Axes"
|
||||||
|
},
|
||||||
|
"BinaryString": {
|
||||||
|
"value": {
|
||||||
|
"BinaryString": "SGVsbG8h"
|
||||||
|
},
|
||||||
|
"ty": "BinaryString"
|
||||||
|
},
|
||||||
|
"Bool": {
|
||||||
|
"value": {
|
||||||
|
"Bool": true
|
||||||
|
},
|
||||||
|
"ty": "Bool"
|
||||||
|
},
|
||||||
|
"BrickColor": {
|
||||||
|
"value": {
|
||||||
|
"BrickColor": 1004
|
||||||
|
},
|
||||||
|
"ty": "BrickColor"
|
||||||
|
},
|
||||||
|
"CFrame": {
|
||||||
|
"value": {
|
||||||
|
"CFrame": {
|
||||||
|
"position": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
],
|
||||||
|
"orientation": [
|
||||||
|
[
|
||||||
|
4.0,
|
||||||
|
5.0,
|
||||||
|
6.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
7.0,
|
||||||
|
8.0,
|
||||||
|
9.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10.0,
|
||||||
|
11.0,
|
||||||
|
12.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "CFrame"
|
||||||
|
},
|
||||||
|
"Color3": {
|
||||||
|
"value": {
|
||||||
|
"Color3": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Color3"
|
||||||
|
},
|
||||||
|
"Color3uint8": {
|
||||||
|
"value": {
|
||||||
|
"Color3uint8": [
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
255
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Color3uint8"
|
||||||
|
},
|
||||||
|
"ColorSequence": {
|
||||||
|
"value": {
|
||||||
|
"ColorSequence": {
|
||||||
|
"keypoints": [
|
||||||
|
{
|
||||||
|
"time": 0.0,
|
||||||
|
"color": [
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1.0,
|
||||||
|
"color": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "ColorSequence"
|
||||||
|
},
|
||||||
|
"Content": {
|
||||||
|
"value": {
|
||||||
|
"Content": "rbxassetid://12345"
|
||||||
|
},
|
||||||
|
"ty": "Content"
|
||||||
|
},
|
||||||
|
"Enum": {
|
||||||
|
"value": {
|
||||||
|
"Enum": 1234
|
||||||
|
},
|
||||||
|
"ty": "Enum"
|
||||||
|
},
|
||||||
|
"Faces": {
|
||||||
|
"value": {
|
||||||
|
"Faces": [
|
||||||
|
"Right",
|
||||||
|
"Top",
|
||||||
|
"Back",
|
||||||
|
"Left",
|
||||||
|
"Bottom",
|
||||||
|
"Front"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Faces"
|
||||||
|
},
|
||||||
|
"Float32": {
|
||||||
|
"value": {
|
||||||
|
"Float32": 15.0
|
||||||
|
},
|
||||||
|
"ty": "Float32"
|
||||||
|
},
|
||||||
|
"Float64": {
|
||||||
|
"value": {
|
||||||
|
"Float64": 15123.0
|
||||||
|
},
|
||||||
|
"ty": "Float64"
|
||||||
|
},
|
||||||
|
"Font": {
|
||||||
|
"value": {
|
||||||
|
"Font": {
|
||||||
|
"family": "rbxasset://fonts/families/SourceSansPro.json",
|
||||||
|
"weight": "Regular",
|
||||||
|
"style": "Normal",
|
||||||
|
"cachedFaceId": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "Font"
|
||||||
|
},
|
||||||
|
"Int32": {
|
||||||
|
"value": {
|
||||||
|
"Int32": 6014
|
||||||
|
},
|
||||||
|
"ty": "Int32"
|
||||||
|
},
|
||||||
|
"Int64": {
|
||||||
|
"value": {
|
||||||
|
"Int64": 23491023
|
||||||
|
},
|
||||||
|
"ty": "Int64"
|
||||||
|
},
|
||||||
|
"MaterialColors": {
|
||||||
|
"value": {
|
||||||
|
"MaterialColors": {
|
||||||
|
"Grass": [
|
||||||
|
106,
|
||||||
|
127,
|
||||||
|
63
|
||||||
|
],
|
||||||
|
"Slate": [
|
||||||
|
63,
|
||||||
|
127,
|
||||||
|
107
|
||||||
|
],
|
||||||
|
"Concrete": [
|
||||||
|
127,
|
||||||
|
102,
|
||||||
|
63
|
||||||
|
],
|
||||||
|
"Brick": [
|
||||||
|
138,
|
||||||
|
86,
|
||||||
|
62
|
||||||
|
],
|
||||||
|
"Sand": [
|
||||||
|
143,
|
||||||
|
126,
|
||||||
|
95
|
||||||
|
],
|
||||||
|
"WoodPlanks": [
|
||||||
|
139,
|
||||||
|
109,
|
||||||
|
79
|
||||||
|
],
|
||||||
|
"Rock": [
|
||||||
|
102,
|
||||||
|
108,
|
||||||
|
111
|
||||||
|
],
|
||||||
|
"Glacier": [
|
||||||
|
101,
|
||||||
|
176,
|
||||||
|
234
|
||||||
|
],
|
||||||
|
"Snow": [
|
||||||
|
195,
|
||||||
|
199,
|
||||||
|
218
|
||||||
|
],
|
||||||
|
"Sandstone": [
|
||||||
|
137,
|
||||||
|
90,
|
||||||
|
71
|
||||||
|
],
|
||||||
|
"Mud": [
|
||||||
|
58,
|
||||||
|
46,
|
||||||
|
36
|
||||||
|
],
|
||||||
|
"Basalt": [
|
||||||
|
30,
|
||||||
|
30,
|
||||||
|
37
|
||||||
|
],
|
||||||
|
"Ground": [
|
||||||
|
102,
|
||||||
|
92,
|
||||||
|
59
|
||||||
|
],
|
||||||
|
"CrackedLava": [
|
||||||
|
232,
|
||||||
|
156,
|
||||||
|
74
|
||||||
|
],
|
||||||
|
"Asphalt": [
|
||||||
|
115,
|
||||||
|
123,
|
||||||
|
107
|
||||||
|
],
|
||||||
|
"Cobblestone": [
|
||||||
|
132,
|
||||||
|
123,
|
||||||
|
90
|
||||||
|
],
|
||||||
|
"Ice": [
|
||||||
|
129,
|
||||||
|
194,
|
||||||
|
224
|
||||||
|
],
|
||||||
|
"LeafyGrass": [
|
||||||
|
115,
|
||||||
|
132,
|
||||||
|
74
|
||||||
|
],
|
||||||
|
"Salt": [
|
||||||
|
198,
|
||||||
|
189,
|
||||||
|
181
|
||||||
|
],
|
||||||
|
"Limestone": [
|
||||||
|
206,
|
||||||
|
173,
|
||||||
|
148
|
||||||
|
],
|
||||||
|
"Pavement": [
|
||||||
|
148,
|
||||||
|
148,
|
||||||
|
140
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "MaterialColors"
|
||||||
|
},
|
||||||
|
"NumberRange": {
|
||||||
|
"value": {
|
||||||
|
"NumberRange": [
|
||||||
|
-36.0,
|
||||||
|
94.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "NumberRange"
|
||||||
|
},
|
||||||
|
"NumberSequence": {
|
||||||
|
"value": {
|
||||||
|
"NumberSequence": {
|
||||||
|
"keypoints": [
|
||||||
|
{
|
||||||
|
"time": 0.0,
|
||||||
|
"value": 5.0,
|
||||||
|
"envelope": 2.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1.0,
|
||||||
|
"value": 22.0,
|
||||||
|
"envelope": 0.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "NumberSequence"
|
||||||
|
},
|
||||||
|
"OptionalCFrame-None": {
|
||||||
|
"value": {
|
||||||
|
"OptionalCFrame": null
|
||||||
|
},
|
||||||
|
"ty": "OptionalCFrame"
|
||||||
|
},
|
||||||
|
"OptionalCFrame-Some": {
|
||||||
|
"value": {
|
||||||
|
"OptionalCFrame": {
|
||||||
|
"position": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"orientation": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "OptionalCFrame"
|
||||||
|
},
|
||||||
|
"PhysicalProperties-Custom": {
|
||||||
|
"value": {
|
||||||
|
"PhysicalProperties": {
|
||||||
|
"density": 0.5,
|
||||||
|
"friction": 1.0,
|
||||||
|
"elasticity": 0.0,
|
||||||
|
"frictionWeight": 50.0,
|
||||||
|
"elasticityWeight": 25.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "PhysicalProperties"
|
||||||
|
},
|
||||||
|
"PhysicalProperties-Default": {
|
||||||
|
"value": {
|
||||||
|
"PhysicalProperties": "Default"
|
||||||
|
},
|
||||||
|
"ty": "PhysicalProperties"
|
||||||
|
},
|
||||||
|
"Ray": {
|
||||||
|
"value": {
|
||||||
|
"Ray": {
|
||||||
|
"origin": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
],
|
||||||
|
"direction": [
|
||||||
|
4.0,
|
||||||
|
5.0,
|
||||||
|
6.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "Ray"
|
||||||
|
},
|
||||||
|
"Rect": {
|
||||||
|
"value": {
|
||||||
|
"Rect": [
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
5.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Rect"
|
||||||
|
},
|
||||||
|
"Region3int16": {
|
||||||
|
"value": {
|
||||||
|
"Region3int16": [
|
||||||
|
[
|
||||||
|
-10,
|
||||||
|
-5,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
15
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Region3int16"
|
||||||
|
},
|
||||||
|
"String": {
|
||||||
|
"value": {
|
||||||
|
"String": "Hello, world!"
|
||||||
|
},
|
||||||
|
"ty": "String"
|
||||||
|
},
|
||||||
|
"Tags": {
|
||||||
|
"value": {
|
||||||
|
"Tags": [
|
||||||
|
"foo",
|
||||||
|
"con'fusion?!",
|
||||||
|
"bar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Tags"
|
||||||
|
},
|
||||||
|
"UDim": {
|
||||||
|
"value": {
|
||||||
|
"UDim": [
|
||||||
|
1.0,
|
||||||
|
32
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "UDim"
|
||||||
|
},
|
||||||
|
"UDim2": {
|
||||||
|
"value": {
|
||||||
|
"UDim2": [
|
||||||
|
[
|
||||||
|
-1.0,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
-100
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "UDim2"
|
||||||
|
},
|
||||||
|
"Vector2": {
|
||||||
|
"value": {
|
||||||
|
"Vector2": [
|
||||||
|
-50.0,
|
||||||
|
50.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector2"
|
||||||
|
},
|
||||||
|
"Vector2int16": {
|
||||||
|
"value": {
|
||||||
|
"Vector2int16": [
|
||||||
|
-300,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector2int16"
|
||||||
|
},
|
||||||
|
"Vector3": {
|
||||||
|
"value": {
|
||||||
|
"Vector3": [
|
||||||
|
-300.0,
|
||||||
|
0.0,
|
||||||
|
1500.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector3"
|
||||||
|
},
|
||||||
|
"Vector3int16": {
|
||||||
|
"value": {
|
||||||
|
"Vector3int16": [
|
||||||
|
60,
|
||||||
|
37,
|
||||||
|
-450
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Vector3int16"
|
||||||
|
}
|
||||||
|
}
|
||||||
139
plugin/rbx_dom_lua/base64.lua
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
-- Thanks to Tiffany352 for this base64 implementation!
|
||||||
|
|
||||||
|
local floor = math.floor
|
||||||
|
local char = string.char
|
||||||
|
|
||||||
|
local function encodeBase64(str)
|
||||||
|
local out = {}
|
||||||
|
local nOut = 0
|
||||||
|
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
local strLen = #str
|
||||||
|
|
||||||
|
-- 3 octets become 4 hextets
|
||||||
|
for i = 1, strLen - 2, 3 do
|
||||||
|
local b1, b2, b3 = str:byte(i, i + 3)
|
||||||
|
local word = b3 + b2 * 256 + b1 * 256 * 256
|
||||||
|
|
||||||
|
local h4 = word % 64 + 1
|
||||||
|
word = floor(word / 64)
|
||||||
|
local h3 = word % 64 + 1
|
||||||
|
word = floor(word / 64)
|
||||||
|
local h2 = word % 64 + 1
|
||||||
|
word = floor(word / 64)
|
||||||
|
local h1 = word % 64 + 1
|
||||||
|
|
||||||
|
out[nOut + 1] = alphabet:sub(h1, h1)
|
||||||
|
out[nOut + 2] = alphabet:sub(h2, h2)
|
||||||
|
out[nOut + 3] = alphabet:sub(h3, h3)
|
||||||
|
out[nOut + 4] = alphabet:sub(h4, h4)
|
||||||
|
nOut = nOut + 4
|
||||||
|
end
|
||||||
|
|
||||||
|
local remainder = strLen % 3
|
||||||
|
|
||||||
|
if remainder == 2 then
|
||||||
|
-- 16 input bits -> 3 hextets (2 full, 1 partial)
|
||||||
|
local b1, b2 = str:byte(-2, -1)
|
||||||
|
-- partial is 4 bits long, leaving 2 bits of zero padding ->
|
||||||
|
-- offset = 4
|
||||||
|
local word = b2 * 4 + b1 * 4 * 256
|
||||||
|
|
||||||
|
local h3 = word % 64 + 1
|
||||||
|
word = floor(word / 64)
|
||||||
|
local h2 = word % 64 + 1
|
||||||
|
word = floor(word / 64)
|
||||||
|
local h1 = word % 64 + 1
|
||||||
|
|
||||||
|
out[nOut + 1] = alphabet:sub(h1, h1)
|
||||||
|
out[nOut + 2] = alphabet:sub(h2, h2)
|
||||||
|
out[nOut + 3] = alphabet:sub(h3, h3)
|
||||||
|
out[nOut + 4] = "="
|
||||||
|
elseif remainder == 1 then
|
||||||
|
-- 8 input bits -> 2 hextets (2 full, 1 partial)
|
||||||
|
local b1 = str:byte(-1, -1)
|
||||||
|
-- partial is 2 bits long, leaving 4 bits of zero padding ->
|
||||||
|
-- offset = 16
|
||||||
|
local word = b1 * 16
|
||||||
|
|
||||||
|
local h2 = word % 64 + 1
|
||||||
|
word = floor(word / 64)
|
||||||
|
local h1 = word % 64 + 1
|
||||||
|
|
||||||
|
out[nOut + 1] = alphabet:sub(h1, h1)
|
||||||
|
out[nOut + 2] = alphabet:sub(h2, h2)
|
||||||
|
out[nOut + 3] = "="
|
||||||
|
out[nOut + 4] = "="
|
||||||
|
end
|
||||||
|
-- if the remainder is 0, then no work is needed
|
||||||
|
|
||||||
|
return table.concat(out, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function decodeBase64(str)
|
||||||
|
local out = {}
|
||||||
|
local nOut = 0
|
||||||
|
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
local strLen = #str
|
||||||
|
local acc = 0
|
||||||
|
local nAcc = 0
|
||||||
|
|
||||||
|
local alphabetLut = {}
|
||||||
|
for i = 1, #alphabet do
|
||||||
|
alphabetLut[alphabet:sub(i, i)] = i - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 4 hextets become 3 octets
|
||||||
|
for i = 1, strLen do
|
||||||
|
local ch = str:sub(i, i)
|
||||||
|
local byte = alphabetLut[ch]
|
||||||
|
if byte then
|
||||||
|
acc = acc * 64 + byte
|
||||||
|
nAcc = nAcc + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if nAcc == 4 then
|
||||||
|
local b3 = acc % 256
|
||||||
|
acc = floor(acc / 256)
|
||||||
|
local b2 = acc % 256
|
||||||
|
acc = floor(acc / 256)
|
||||||
|
local b1 = acc % 256
|
||||||
|
|
||||||
|
out[nOut + 1] = char(b1)
|
||||||
|
out[nOut + 2] = char(b2)
|
||||||
|
out[nOut + 3] = char(b3)
|
||||||
|
nOut = nOut + 3
|
||||||
|
nAcc = 0
|
||||||
|
acc = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if nAcc == 3 then
|
||||||
|
-- 3 hextets -> 16 bit output
|
||||||
|
acc = acc * 64
|
||||||
|
acc = floor(acc / 256)
|
||||||
|
local b2 = acc % 256
|
||||||
|
acc = floor(acc / 256)
|
||||||
|
local b1 = acc % 256
|
||||||
|
|
||||||
|
out[nOut + 1] = char(b1)
|
||||||
|
out[nOut + 2] = char(b2)
|
||||||
|
elseif nAcc == 2 then
|
||||||
|
-- 2 hextets -> 8 bit output
|
||||||
|
acc = acc * 64
|
||||||
|
acc = floor(acc / 256)
|
||||||
|
acc = acc * 64
|
||||||
|
acc = floor(acc / 256)
|
||||||
|
local b1 = acc % 256
|
||||||
|
|
||||||
|
out[nOut + 1] = char(b1)
|
||||||
|
elseif nAcc == 1 then
|
||||||
|
error("Base64 has invalid length")
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(out, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
decode = decodeBase64,
|
||||||
|
encode = encodeBase64,
|
||||||
|
}
|
||||||