mirror of
https://github.com/factoriotools/factorio-docker.git
synced 2025-07-12 20:15:35 +02:00
Compare commits
1229 Commits
0.14.16
...
add-permis
Author | SHA1 | Date | |
---|---|---|---|
b4073aebac | |||
e8adbf55c1 | |||
533789470f | |||
5b6e0cde8b | |||
00038b5184 | |||
72c3590cd6 | |||
23942e3117 | |||
15d38ea739 | |||
9464758a7b | |||
50f04fb096 | |||
15d31c9a2e | |||
8784845385 | |||
9f6c781331 | |||
8a718705b7 | |||
60277e89f1 | |||
4562f675ea | |||
22d02c42fa | |||
6b3dd77a54 | |||
6d3be9aef1 | |||
bf97066b9c | |||
c0c235a28d | |||
d5db1b1281 | |||
0c0349b5d6 | |||
0c1b2f4164 | |||
b36cd87194 | |||
e82cead5f4 | |||
d9ff25fa10 | |||
b7fdff9b94 | |||
04f20de96c | |||
fcd2c2e40e | |||
f02c02f38e | |||
b4be6993c6 | |||
a2c11ade50 | |||
b334d27cf0 | |||
ede09dc402 | |||
0929686ad3 | |||
317d3731e8 | |||
080a70f6be | |||
83bffe4b13 | |||
14df05cd6d | |||
c0e1bae277 | |||
370059f2f6 | |||
7e8c89c02a | |||
1e52dc48c9 | |||
14aa945717 | |||
57bbf46196 | |||
090e771c0c | |||
9e89a7930e | |||
20e6176a85 | |||
e5e1993c97 | |||
6e0ea49985 | |||
f757e538b7 | |||
a94f8a0d4e | |||
2b76185f94 | |||
3c45811e15 | |||
8990021fa1 | |||
50e5c868e5 | |||
89aebaba4b | |||
d19c97c075 | |||
19e5dc7532 | |||
6a3b160d76 | |||
a3d5456e58 | |||
8d5096e28b | |||
a1135b6a55 | |||
192f686b6b | |||
66ce43c0ca | |||
ebadd1d8b8 | |||
c9989d3a07 | |||
2dfaac7ec4 | |||
c2d7c5696e | |||
f14bedbc63 | |||
d91c45fa62 | |||
2092e86aec | |||
ecb89b0c11 | |||
42a08a95d4 | |||
61d52eaa7c | |||
6916fcb150 | |||
8c9dfd004e | |||
867635db16 | |||
b7413607c4 | |||
5d6f70b285 | |||
a24e42b281 | |||
d1142e3408 | |||
4eb53b6e48 | |||
f986f1c870 | |||
bc760a9599 | |||
d153e30f1c | |||
cc5061d339 | |||
14ae422463 | |||
88ca95a1ae | |||
64980b9db3 | |||
85f8011105 | |||
cc8d89df84 | |||
956ade4f0b | |||
049d34ea9a | |||
d01580b5a5 | |||
25a283ad79 | |||
b172b16ff4 | |||
eb150cff0d | |||
b20d93e229 | |||
9562212254 | |||
6060c7b722 | |||
06baf6a186 | |||
6eb0cd96d8 | |||
bbbfef343c | |||
44a58b056c | |||
e37bcf1ed0 | |||
3defd74c46 | |||
dde2761a8c | |||
0ec91c3391 | |||
f9040c7450 | |||
c9957b7d76 | |||
89ae20012e | |||
d137f32c2f | |||
4f3c5f2e93 | |||
f7e9dea263 | |||
748dec4ad5 | |||
3ce89a714b | |||
a8ecb01f4c | |||
6692bb514d | |||
2d805feb4f | |||
bc2dc44cad | |||
04085ef23d | |||
fbb787e535 | |||
2ba59b4f95 | |||
c269289a96 | |||
ee0388e163 | |||
a1cb15b709 | |||
890489fb15 | |||
c756ffe827 | |||
4474b52473 | |||
84a808102f | |||
016c5cf3b5 | |||
159b14bd8e | |||
c1b0f6ca83 | |||
965fef7ac2 | |||
88fed07073 | |||
4ab4c889f4 | |||
bc28f4a917 | |||
d792589c56 | |||
486c0f1d0a | |||
80d8761520 | |||
f4e0e640c5 | |||
51b601b8b6 | |||
1d88d18b38 | |||
7d085d7fe5 | |||
630c5a1864 | |||
98d3e475d3 | |||
bd76f1c541 | |||
afde97243d | |||
81b476ac97 | |||
30e56a197c | |||
effd07f84c | |||
23d40f36de | |||
250a90de53 | |||
89f601e5ce | |||
73fe111bea | |||
18b455ba43 | |||
1c5e98f289 | |||
0a3d923c5b | |||
19f52a6ce0 | |||
f6461d99be | |||
f5a9224756 | |||
79eebfb0f9 | |||
b75bce7f91 | |||
ef449c4763 | |||
42c2d0f0b9 | |||
e2f732a61c | |||
d53f7900d8 | |||
abf3166142 | |||
f255b89a5e | |||
fd717ee1b1 | |||
551b748297 | |||
87403fdb1b | |||
882ad42893 | |||
f9c256e0e4 | |||
8f0e42ec25 | |||
6b92576644 | |||
2f51b1ae60 | |||
21fbb3f294 | |||
d5e3f1580b | |||
e3c1e34fe7 | |||
0fc0e18cb0 | |||
d634a84e0f | |||
ba5670692e | |||
ceea4e5dcb | |||
0f5d60d61c | |||
9171b1fd5d | |||
ced1340b11 | |||
d7e6952db8 | |||
24218ec31a | |||
5491f01acb | |||
3e8c2f5cb7 | |||
d306b82fbe | |||
bd1ba3cf9d | |||
dfd442dfda | |||
f262181b4e | |||
f7282c516c | |||
4fe8b3d0bd | |||
69e7d8ee55 | |||
d90480712d | |||
d74b635be5 | |||
0b5695e55a | |||
34bf552c2c | |||
bd048b90e2 | |||
771eb142f0 | |||
492b0378a3 | |||
2ac5f259fa | |||
5bf1b045e1 | |||
b09c5ab001 | |||
c6bf7fef1d | |||
626351232a | |||
db41eaad31 | |||
02695f0f00 | |||
5aef1b9f6f | |||
2f28ee6626 | |||
9912dbd9bb | |||
420fecc788 | |||
67bea4df99 | |||
b12e652e4d | |||
d0cfb691ec | |||
88757eb5d1 | |||
e365e2cd5e | |||
d7db07e265 | |||
e5aac5fc9e | |||
0f969e517a | |||
96d38bc3e8 | |||
4e43fdd73b | |||
b20ff166e7 | |||
e0165b4b2d | |||
bbfbb86308 | |||
0aae38e6a0 | |||
991d3e2765 | |||
e4ce42d9f3 | |||
157f9484db | |||
a8326cd88a | |||
1ebd9ced8d | |||
723160de54 | |||
cc96cf2294 | |||
79e22b8db5 | |||
5a03444010 | |||
43ce3dc8e1 | |||
97e1433fde | |||
f6aec0a546 | |||
039a8626ba | |||
634e7267a9 | |||
d1e5ac742e | |||
9eec6b9dd5 | |||
c8b079caf0 | |||
bce3ff95ba | |||
faa84dbfa6 | |||
8003142235 | |||
9863a4cd74 | |||
c5d668ac09 | |||
87a4ba87e5 | |||
9bdbd90322 | |||
4812b5b970 | |||
65e3f84c4f | |||
02cc66c3ba | |||
c09257d28a | |||
769617edc4 | |||
d6154359d9 | |||
765d2bb23d | |||
50d60680e0 | |||
0d5f77f0da | |||
0a56912cfd | |||
136329f45e | |||
f3dc109e7e | |||
f84fee3624 | |||
118f149aee | |||
60165855ff | |||
89b9851edb | |||
f2a5d6d075 | |||
5e018ca2ae | |||
0160889854 | |||
bc9276737b | |||
5e3943362b | |||
3778d6bb66 | |||
fbf430373d | |||
a495ac72cd | |||
17e84504cf | |||
cea55cda67 | |||
b0ad3fa036 | |||
1a6e902c6b | |||
dadf88d5e3 | |||
b065ecb257 | |||
f0b1ff342f | |||
9a563a35b8 | |||
75543ebbe6 | |||
d17a6e648e | |||
b96fb634b1 | |||
9248c57549 | |||
8641b6d34b | |||
aefd5339c6 | |||
504aa3a05a | |||
ccc7f5624a | |||
0336fbe3cf | |||
452aab7ffb | |||
a953759b96 | |||
f61abdc8b5 | |||
bb4d2d0154 | |||
f44f690b6a | |||
eadada76b9 | |||
45eb323d17 | |||
cad67f5d0b | |||
18fe336fb8 | |||
a7c9b3e1c8 | |||
2026cce01a | |||
7b7f47f464 | |||
1f83234834 | |||
bc06b94f38 | |||
3abd2d69fc | |||
63fc1066dc | |||
7f4e005b1a | |||
5222d392b1 | |||
be8c1b28a3 | |||
e5a70d4ada | |||
31448a0c06 | |||
6d2fcd5696 | |||
65b909310c | |||
e8fca26f7a | |||
6ce248eacb | |||
a683f67f86 | |||
b253a086cc | |||
36ecbb3de8 | |||
3bd6929ee7 | |||
cb4f36fc91 | |||
06433d2ba1 | |||
31a0c78ba6 | |||
29f5222c35 | |||
13ebf33867 | |||
3947458623 | |||
cb6eb6e519 | |||
9896b03e0a | |||
5176be56db | |||
72fdb9fc42 | |||
b0b62d5ba7 | |||
4f73862221 | |||
5b83699e5c | |||
511520c459 | |||
99db93d6f8 | |||
24825309c3 | |||
db25aab203 | |||
d10ec1e309 | |||
14851be061 | |||
1212e00fa3 | |||
c537410871 | |||
068b96ae2b | |||
9c3ec3c817 | |||
229f3d7fda | |||
248dab3db1 | |||
c25e2c8a1d | |||
4a84743920 | |||
cc4f8b192d | |||
acd9c559fb | |||
ff7074822b | |||
99100f8495 | |||
939827eb81 | |||
7c1406b38c | |||
5f7a32e492 | |||
a11cbbbf41 | |||
dac8e07016 | |||
63fcb292a8 | |||
2a313aa6d6 | |||
b5a6059386 | |||
5904849dbc | |||
1987d7b43a | |||
54b1a97074 | |||
ec1aef13d6 | |||
bc9ec68e87 | |||
51762ba589 | |||
dffc09f0ff | |||
cc7f772024 | |||
cef0016bf4 | |||
5b5a658116 | |||
3e3dfb7c05 | |||
e150272201 | |||
228cc517e0 | |||
850bb9aef0 | |||
caafc2cbef | |||
3f3fa0a008 | |||
af51b87df7 | |||
78b45f2cca | |||
257229118f | |||
6aadd6053a | |||
db54b89dae | |||
64cad3e345 | |||
c88e79fd5b | |||
d0610c47d6 | |||
851507c3ef | |||
3e5afe32ab | |||
2359a1c5d8 | |||
d99ef45240 | |||
2b26cd1b69 | |||
7dddaf62bd | |||
3f034d0754 | |||
7720e32081 | |||
65049c8120 | |||
c94ab998b4 | |||
6e4ec92a95 | |||
7dcac4cdd7 | |||
cc70895c33 | |||
89b54579e0 | |||
c1e1a1c4ca | |||
429c88f290 | |||
074bf11633 | |||
5765088f7e | |||
316f790530 | |||
c91852a196 | |||
9ba7c5c8d6 | |||
f2a4223c7b | |||
2a5e5b48ef | |||
57e979bca1 | |||
88ca3293a6 | |||
018269377a | |||
846e102ac8 | |||
4551f6ee03 | |||
09db1e2141 | |||
cb9a9812bd | |||
d03e4063e7 | |||
21c5c85ac3 | |||
a10642d3eb | |||
2856c1cf76 | |||
3e736968d1 | |||
ac6d647ddd | |||
02439750d1 | |||
3efae7b161 | |||
49a258c830 | |||
3ffa3610ef | |||
56984b2ef1 | |||
653c87a32e | |||
07bd20120c | |||
bd7347b42c | |||
cfbef2aedb | |||
8436dfe8a0 | |||
82892d8ad8 | |||
34132ddbd6 | |||
b52af78919 | |||
13964c7a50 | |||
e5e5dc27d2 | |||
8ad8f81eed | |||
b0e855bb8a | |||
31a60e976b | |||
c108d48f00 | |||
fa741f635f | |||
5258ed7843 | |||
d31d7f5ae6 | |||
0efbfb5c2d | |||
57f867aa80 | |||
ded7d36fa8 | |||
93822d930c | |||
5474bb842d | |||
ff432200f0 | |||
310214eb46 | |||
bc3b08e470 | |||
1ef555ab24 | |||
c8c6cb62ea | |||
ac21591ee9 | |||
bef01206d1 | |||
a7131499e4 | |||
2a2092755c | |||
7d50408b87 | |||
9a7174ddaf | |||
fffc38615f | |||
7b31cc044a | |||
0331df2774 | |||
1405a9be04 | |||
b630de0efa | |||
a2aec76419 | |||
c112d3969f | |||
624c42a831 | |||
8c9adbbbc6 | |||
e057dd5b22 | |||
a5b2e042a9 | |||
9960cc856d | |||
1bb5f2610e | |||
8ee8fafe38 | |||
ab33e69fb1 | |||
d14fb33575 | |||
3f4e823fbc | |||
e439c21591 | |||
c7bb5393ad | |||
54a9b4c5c4 | |||
775ed4b228 | |||
5550f9b707 | |||
0c924152a7 | |||
cb36026261 | |||
ef6bbde78d | |||
44d4898862 | |||
46b94cb19f | |||
e47431c7af | |||
d03fb43532 | |||
bd6d956518 | |||
afd636fc3f | |||
a2849b016a | |||
4e261043cd | |||
4c47dbc4ee | |||
07264e626f | |||
64a1f8d9bf | |||
15a2d493f8 | |||
3777041d3e | |||
ccd0707d21 | |||
a5b03e6625 | |||
03b4d9eb5e | |||
af48272100 | |||
cc63e10cd1 | |||
f90cd27bcd | |||
1ab01d0277 | |||
66c11b1a82 | |||
49055cfe1b | |||
ab818efda4 | |||
6da1250b5b | |||
deaaf83967 | |||
9691472b84 | |||
236b27d94e | |||
457ab79b5b | |||
235a2abb60 | |||
c52f649b35 | |||
52167abffa | |||
f20fe0ad85 | |||
1cd2015cc4 | |||
c4b2d30366 | |||
5c81134430 | |||
7d220042a9 | |||
e61a3aa622 | |||
773005b320 | |||
51185620dd | |||
c24b7bc41b | |||
5301aff1b1 | |||
502f13c1cd | |||
fe3d0d65bc | |||
4bc30e718c | |||
994a091595 | |||
c754e64e47 | |||
a2726704ed | |||
f4e8b30e90 | |||
07dcd690bd | |||
4bf909979a | |||
7ec79a6e1b | |||
6a1317cb7a | |||
15efa9caec | |||
41b2c9b743 | |||
4e4810aea3 | |||
d667a247e1 | |||
ce1f980525 | |||
120ee1bbdb | |||
5cba3991ef | |||
412c16e379 | |||
82635a9cff | |||
c495fc38a4 | |||
b580447de4 | |||
2290034ecf | |||
300300e7ec | |||
179be082d9 | |||
efcc29cfed | |||
1b7decd8ce | |||
b38750e4ac | |||
bfb8b7fd95 | |||
8b5d4e06df | |||
33060cc4fb | |||
e1beb32f81 | |||
2c1d1cd985 | |||
39fd391e9b | |||
a5d6466f0b | |||
c6b15d89c1 | |||
7c9011dcee | |||
b5af3130e2 | |||
e307b1d537 | |||
8c0ddf99ce | |||
d7b79c567a | |||
3e62698b4b | |||
d4870219f6 | |||
01194242b9 | |||
4255475b18 | |||
4ff8179912 | |||
db14988396 | |||
cd29f2003b | |||
cd2d89a7bc | |||
2d9f646728 | |||
9e96ca5aa8 | |||
5c947846d1 | |||
edd0c95d9b | |||
6651289d6e | |||
784626b689 | |||
47ce1706db | |||
460ba0187d | |||
6d496def35 | |||
9f8f6da0d2 | |||
71c75671d5 | |||
5a66d1a8df | |||
a48b9d107d | |||
e5f34134f7 | |||
5d997bf1c7 | |||
7a08cbb04c | |||
40cc26ca02 | |||
e8705a44b8 | |||
fe9875343f | |||
06729a6097 | |||
95766e90c9 | |||
4afbed52c8 | |||
fd7b8cd839 | |||
9c92ac9342 | |||
1b6c8adf28 | |||
65c5833fd9 | |||
c949351391 | |||
6ae5795d38 | |||
29ee60236e | |||
7028eb72f6 | |||
dfeb95d614 | |||
f8c62978ee | |||
28598a42a3 | |||
4ba56ee010 | |||
d2a02e8a66 | |||
5b24f8ac74 | |||
e3466a9800 | |||
e6f66a2100 | |||
18153f3f46 | |||
62e1a9f266 | |||
be7ecddb0d | |||
43599d0c23 | |||
6fb4cbc600 | |||
fe0e8f9b91 | |||
f76e88cea1 | |||
cbaa20cdea | |||
b3c134d396 | |||
9bdb79b494 | |||
77d83b4152 | |||
25d4edf14f | |||
e16b111c0b | |||
09e1ad2045 | |||
2efa39084f | |||
77edb05109 | |||
2eea2a25e4 | |||
98ea8a27c1 | |||
956af65a2b | |||
b5c1dff7b4 | |||
ee5b023911 | |||
045ef7a214 | |||
bbc2d42302 | |||
0173eab54c | |||
f868aa16ad | |||
5cd0c40975 | |||
778d982c6d | |||
9d2c38df65 | |||
b2a4195e13 | |||
18ccac666d | |||
830d042e4b | |||
a0868ba653 | |||
7002778ab5 | |||
44371283b6 | |||
d421adab67 | |||
b0cedbbe1b | |||
010b96e0b0 | |||
621ccf4282 | |||
1204b97fca | |||
aef16e83a7 | |||
605ca74c0e | |||
e34180ca51 | |||
0db1dde077 | |||
e8aeb461a9 | |||
46f24b0b7a | |||
6d92825f56 | |||
55fdc2e0de | |||
57c9f59f46 | |||
8f15b73d7b | |||
ecc54400ba | |||
eea1d550f2 | |||
730b2c334b | |||
288f080ebc | |||
2ffac3c4a8 | |||
88fed15bb4 | |||
968fb8a3bd | |||
9d642d8fb0 | |||
4c3818b27c | |||
a6d480d695 | |||
fce556160a | |||
4727f62a8f | |||
64cbf48082 | |||
dd6bc76827 | |||
b7108c64cb | |||
2f53d7e03c | |||
f97eee5f83 | |||
0e1808c3f5 | |||
629a3fade3 | |||
3f88bcbf52 | |||
fb36aaa0d9 | |||
e22ce8989b | |||
29f3e54adf | |||
148de89c52 | |||
4fc8da6581 | |||
e6290605c7 | |||
662372a1ba | |||
82f6bdf865 | |||
c9f4510195 | |||
6602c7cf7f | |||
8be7b47c07 | |||
717275198e | |||
d68ec4fdd4 | |||
47464e0b9b | |||
23fdd4ed09 | |||
e7a6bddc1f | |||
3d9e2999b6 | |||
7b33b9f6e8 | |||
066c61fb85 | |||
303fbb5d97 | |||
3f4fd9e348 | |||
504ebfde23 | |||
f982370229 | |||
51d3845af7 | |||
889c10bd48 | |||
46f397a9bb | |||
988aa08a73 | |||
ff6f4b6cd0 | |||
ea426ca0f4 | |||
4ecb1dcb6b | |||
3ef8c5309e | |||
70c402b9dd | |||
2b434ff08f | |||
49c5dff898 | |||
35d22efc67 | |||
f1b75ac034 | |||
fbb550916c | |||
1d8be250e9 | |||
d90f45bd41 | |||
2b201fea5b | |||
b0a64e5939 | |||
ef24dad0bf | |||
69a52f2c19 | |||
902ad53bd3 | |||
bd2af80a52 | |||
de9fb52d48 | |||
c0157e19ee | |||
8e7bca222a | |||
3e7c79ab74 | |||
979326ebf6 | |||
d5ac333364 | |||
e941e9a2c8 | |||
6fece0c9e3 | |||
10d000408c | |||
d59fd0844a | |||
324920cdf8 | |||
2c88f5e982 | |||
26cec3ae93 | |||
7897de5080 | |||
b474afc1b7 | |||
795ec7dc55 | |||
03f171e34c | |||
313faf6fa5 | |||
17109c97c1 | |||
e62f5e799a | |||
47a7b8312f | |||
3cdf39d04b | |||
b840e86d21 | |||
a9047cae58 | |||
bf128afdb4 | |||
f5ffae1cb4 | |||
31b6768882 | |||
41eca9edf6 | |||
86169c9dad | |||
f36e58058b | |||
1a12761739 | |||
7f43739481 | |||
33e33a4db7 | |||
2d54d50951 | |||
86c8270d99 | |||
5109bc1b40 | |||
5e36f8bad8 | |||
d1258008c1 | |||
275faa1fcc | |||
87cf6c051a | |||
3d8cdfcc1d | |||
d3ef74b10d | |||
b41aad4dc6 | |||
24ac45dbce | |||
8286ab58ac | |||
c2d8509aa8 | |||
b94db83033 | |||
cc23bb396e | |||
1b2fdb298f | |||
ab277975ae | |||
80d8866242 | |||
4ec18b2d15 | |||
93a3011cd3 | |||
15cacdc3da | |||
927bcb89a0 | |||
4ceff25fc2 | |||
5d072db9cc | |||
216b225681 | |||
18f3943eaf | |||
2725534b2b | |||
df35d57455 | |||
9adc8ce4bc | |||
6272bb0018 | |||
a989b500b0 | |||
66bef56cd2 | |||
10f891cabf | |||
9a7f01f8bf | |||
877219f9c9 | |||
843bcbff21 | |||
a7786f03ed | |||
754663df49 | |||
9ac04bcff8 | |||
66d4652302 | |||
5ddb9ab472 | |||
9e72f0509a | |||
914b12adf4 | |||
dc3c040d7c | |||
6a9a596cd8 | |||
eb22dac840 | |||
b48bb4c134 | |||
cf5af1aae1 | |||
d7f0fe16d7 | |||
27c1426109 | |||
a86ac6cafe | |||
70e08f10f3 | |||
c28cea29a4 | |||
e21fc20eb2 | |||
c75b7fa830 | |||
387383d3e8 | |||
c6c94ee180 | |||
b2908081b4 | |||
e53c2adc54 | |||
d6da776148 | |||
30e5f6dd2d | |||
b69fe57291 | |||
b99c1044ca | |||
b37af9bf93 | |||
185ee256ff | |||
6020015574 | |||
a520b87fc8 | |||
e3abfbbe9b | |||
6a135a06bc | |||
14fb9ecf35 | |||
ffef5899e9 | |||
4449e13675 | |||
189395d5c8 | |||
4c1ea766f4 | |||
9ce9a63cfc | |||
8d16422fcb | |||
628c7a34aa | |||
eafc933ca5 | |||
0175ddbca9 | |||
d994449808 | |||
4e63b2654e | |||
abe6054b27 | |||
4fc35c3cdb | |||
081b0c244f | |||
a331033196 | |||
1e3d6d7886 | |||
2474da335e | |||
648b81edf8 | |||
25326b9073 | |||
cae2d3b226 | |||
e13768aa12 | |||
78f00b9f17 | |||
aedc8a29a4 | |||
7168f2927c | |||
7b19c3bfa5 | |||
bffa336cb2 | |||
06a76e4156 | |||
107b902191 | |||
9e4888fe8a | |||
aa5e51680f | |||
590adff3a0 | |||
c60794de39 | |||
a871fe689d | |||
739ed46afe | |||
dfa067e2e9 | |||
7113c03076 | |||
2095404c2a | |||
dff6b849f8 | |||
5c616483f6 | |||
856066ccf1 | |||
ce039016e0 | |||
6f20f17472 | |||
44a28d5c70 | |||
f9b2d6bb9f | |||
bb06a73144 | |||
3eed964b32 | |||
0eb64bc190 | |||
9834ca2842 | |||
6b2307635e | |||
6f4f179276 | |||
ebd6da4b97 | |||
8ee7c360c5 | |||
5f7f1f145c | |||
6b58cb1909 | |||
d6d039d4a6 | |||
02d2c449d3 | |||
8091c3a9f9 | |||
cb5bc74e4b | |||
1ce5c5db7e | |||
e2e7cc7798 | |||
487d345480 | |||
7a276adb3c | |||
7a5f3471d6 | |||
eac273e2d6 | |||
768eaac7b2 | |||
67a0420f06 | |||
b2248d1e8c | |||
60a124f22c | |||
76ee77b674 | |||
a6f0b66f9b | |||
596eec12b6 | |||
e30e313a55 | |||
9528901b95 | |||
16c0f35c97 | |||
623cd6ac87 | |||
ca3a66ca2f | |||
bc74c61fc0 | |||
893124cf43 | |||
92130e3b34 | |||
1194fde627 | |||
6ae4c0d569 | |||
11d8626ccd | |||
d1d5e405cb | |||
1c1e374346 | |||
046a632860 | |||
d80ae622fd | |||
07786290c4 | |||
1e5f88ffd9 | |||
672101f0f7 | |||
7b9d0f514e | |||
7e40c62ff6 | |||
37fbaa38cf | |||
b52a39fe03 | |||
4528be2297 | |||
36bb311baa | |||
78289a5930 | |||
250d5e464d | |||
70e61be5b6 | |||
e935200821 | |||
add31ebe53 | |||
8e20e6696b | |||
126669ca7a | |||
67aaa41cd8 | |||
a61f05709e | |||
0b5c4e144a | |||
95c45b7a39 | |||
5dbb82debe | |||
d85de90142 | |||
216ab0a7d5 | |||
e9c698cbc8 | |||
8d0eef4e2c | |||
110a598ca8 | |||
2f0ce132f2 | |||
96eff95f47 | |||
6c5282b855 | |||
d195fdcdfc | |||
d9a5fac17c | |||
4ec6f41635 | |||
eb0a7410eb | |||
01aeb3b1aa | |||
91f28e34a2 | |||
7f9f640eff | |||
2d99b36f7e | |||
5e71044728 | |||
52dd896f3c | |||
3a5ce81d2e | |||
ad707ee2ad | |||
61fd28bb36 | |||
892a684f98 | |||
a0cedba383 | |||
893c1b6214 | |||
0d79b49393 | |||
9d3f88ed6b | |||
a0338cc7b3 | |||
ed1fe2ba82 | |||
46769a042d | |||
0a5893578f | |||
59b35f3ac0 | |||
8138589baf | |||
2b1115c19a | |||
b6276d7b51 | |||
82a15c8f34 | |||
0fbcfbf857 | |||
dc89065e68 | |||
bd5a44dce5 | |||
26584556a3 | |||
3a70062289 | |||
c3fe70f783 | |||
7a5732bbcd | |||
a496ba060b | |||
7e78a4236f | |||
0c5ff73adb | |||
9ad1889aeb | |||
2a23a3fe91 | |||
cfcbb035a7 | |||
10f8b0acb6 | |||
c7309c1e5c | |||
c845acc09f | |||
8bfe8b0261 | |||
2c7cfc6c65 | |||
1250578039 | |||
97165cfdde | |||
c6f144aff4 | |||
481246c449 | |||
97cdaa825a | |||
b36c299358 | |||
4e09965476 | |||
5340267672 | |||
061d0c6c37 | |||
84af48c346 | |||
2ca5f1368c | |||
bc0ef1b51e | |||
b2fbd707f8 | |||
a88071d1aa | |||
a1f590270b | |||
b0b5775365 | |||
c988684348 | |||
75e98e0543 | |||
0bc55d51a8 | |||
c50665c37f | |||
81c9d50e7a | |||
c3631942e8 | |||
8e072e8c24 | |||
c260815680 | |||
fd4680704f | |||
b46eefee58 | |||
76a76765bb | |||
d6331587b4 | |||
dfbd6baff6 | |||
4872cf89f6 | |||
99c0355d12 | |||
7b351c473a | |||
b839d7d339 | |||
40974755b6 | |||
548f9b27d0 | |||
be99942271 | |||
f19f731662 | |||
eea4c90fa5 | |||
ce161daf33 | |||
a3d7c55f68 | |||
736dbea3b3 | |||
aeb549b3b5 | |||
2906b59721 | |||
abdce572de | |||
ca7fc9ff13 | |||
ebcca5251c | |||
b72b906fdb | |||
b643a13e88 | |||
6499b0974e | |||
c9052c3296 | |||
832e64511d | |||
a3890f37fa | |||
4089f8d59a | |||
4026ec5359 | |||
9e93a34d6e | |||
05d697afac | |||
d9a5604448 | |||
128e15db7c | |||
71e9179722 | |||
e3a97f3448 | |||
5506d33475 | |||
0314266309 | |||
16a85d32e9 | |||
d36578a471 | |||
9f8453645a | |||
eb4814f16c | |||
c85ce5e75a | |||
d4a5f0f019 | |||
8222bd1b45 | |||
de26a241af | |||
ea4843498b | |||
009548446f | |||
a48b23201b | |||
e9962a431b | |||
2f41a0d660 | |||
53c953707b | |||
b53d98cb2a | |||
833de3cc46 | |||
d94543bbcd | |||
7074d5284b | |||
c119b077c8 | |||
479d7e2fe1 | |||
1247c62b13 | |||
a7f765a070 | |||
1c8b9fd5b9 | |||
1ddcbec2c5 | |||
2d82e0a19c | |||
6320dd04df | |||
1ea5ddfbee | |||
067ac0ac14 | |||
036a15ce1e | |||
003f0cdbe6 | |||
43fafdcfe5 | |||
667bd97388 | |||
62da6f5f56 | |||
47ca759742 | |||
e12e303d2d | |||
ebf0afa524 | |||
1b5a9d5b9e | |||
3404976818 | |||
ba68274a7b | |||
ee509835db | |||
ee3385959d | |||
0ac68ac31a | |||
552520136d | |||
2b63d0ef0e | |||
390c76906b | |||
5bc0e128f1 | |||
0fdac9cebb | |||
86ec899b06 | |||
81edeb0183 | |||
08090e70a0 | |||
16602ba40b | |||
16b6a5b253 | |||
44f5ffd6b4 | |||
8fd01ef07f | |||
6b8c0f01ab | |||
284e7f00b0 | |||
abe0c82a8e | |||
f90f3c7569 | |||
9dca3590ae | |||
1e7c8bc8b7 | |||
4ed57b0c20 | |||
88258a263e | |||
f3df464856 | |||
3a007b377f | |||
ddf7f99a2b | |||
8a3df67b52 | |||
14b11329d7 | |||
2d79854697 | |||
65658b10d6 | |||
be4c89d937 | |||
cb52eb3dff | |||
322fe61b13 | |||
3e42118a04 | |||
ee861acee5 | |||
f69732d4f8 | |||
86914046e0 | |||
f7c2b144ce | |||
a2179f3f61 | |||
3cc553c786 | |||
0b3a7bd84c | |||
a28d8315aa | |||
14d1af4d9e | |||
b8c0e42baa | |||
148c01eab0 | |||
99c79f7e64 | |||
11f92f34d8 | |||
186af111d9 | |||
b6f14800ab | |||
4a5504cdb6 | |||
0e8cb6c26c | |||
b950fe99c6 | |||
6ed85052ff | |||
30425c8d66 | |||
522a3a2e93 | |||
e37cdcc508 | |||
7babfe47ff | |||
bfd2c5f600 | |||
3fc2e97f7e | |||
d141764f44 | |||
4a816ad5f9 | |||
d1aa470d25 | |||
70e936c7e3 | |||
f17e773885 | |||
c8af86eaef | |||
dcac7d0c46 | |||
b6d1748deb | |||
e1e8ff07e6 | |||
601ee80a47 | |||
fd4ed925b2 | |||
2d1f625219 | |||
7d5e58d4a0 | |||
c535246dac | |||
16959ea374 | |||
8f6446f088 | |||
65a32b3cbf | |||
7ca5892e5e | |||
327fd1c925 | |||
323718b631 | |||
8049134ed5 | |||
0a35d754ec | |||
cf60f77d53 | |||
65315ccfa8 | |||
e7ac99c855 | |||
7798e07daf | |||
993ca2631c | |||
a0551876fa | |||
74a32021de | |||
545297c443 | |||
a9fc7f32d9 | |||
fd2f656e57 | |||
d99ff47bbc | |||
b056148d75 | |||
dfb8b32a10 | |||
78c79d8fe9 | |||
aa587d172c | |||
a4f5d9a10a | |||
09be8bf68a | |||
f589953648 | |||
46d525bcf7 | |||
115dc3b1a9 | |||
310cb82749 | |||
308f7cad10 | |||
9044045dac | |||
a1e701df66 | |||
87141b2835 | |||
a6476af82f | |||
12ec73be53 | |||
ef4c0132d4 | |||
764b214723 | |||
dcd07982a8 | |||
7880bd9f65 | |||
27fc3b97bc | |||
ce1320100b | |||
3ea1619384 | |||
52bd19356f | |||
bc55163f5c | |||
5a4e0d8ebf | |||
361bb94857 | |||
048d12ef75 | |||
4d8c94a6c8 | |||
fa1af6c03d | |||
d8b20e5531 | |||
bdb51f5020 | |||
87d1c4b0a3 | |||
89ef301d3d | |||
0960c7c371 | |||
cc496ccaa0 | |||
f9e62fe90d | |||
94494299ee | |||
677dc983e8 | |||
8588fdd6fd | |||
eb22619e44 | |||
6f33dd8444 | |||
43af3aaf33 |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: "/docker"
|
||||
schedule:
|
||||
interval: "daily"
|
29
.github/workflows/docker-build.yml
vendored
Normal file
29
.github/workflows/docker-build.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Docker build & push
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- buildinfo.json
|
||||
# workaround for #526
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: build and push all images
|
||||
if: ${{ env.DOCKER_USERNAME != '' && env.DOCKER_PASSWORD != '' }}
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: |
|
||||
./build.py --push-tags --multiarch --both
|
28
.github/workflows/docker-description.yml
vendored
Normal file
28
.github/workflows/docker-description.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
name: Docker Hub Description
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- README.md
|
||||
# workaround for #526
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docker-description:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v4.0.2
|
||||
if: ${{ env.DOCKER_USERNAME != '' && env.DOCKER_PASSWORD != '' }}
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: factoriotools/factorio
|
32
.github/workflows/lint.yml
vendored
Normal file
32
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: 'Linter'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: shellcheck
|
||||
uses: reviewdog/action-shellcheck@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
reporter: github-pr-review
|
||||
|
||||
hadolint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: hadolint
|
||||
uses: reviewdog/action-hadolint@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
reporter: github-pr-review
|
21
.github/workflows/update.yml
vendored
Normal file
21
.github/workflows/update.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
name: Check Update
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'schedule' || (github.event_name == 'schedule' && github.repository == 'factoriotools/factorio-docker')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.REPO_TOKEN }}
|
||||
|
||||
- name: Run update script
|
||||
run: ./update.sh
|
||||
shell: bash
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# IDE
|
||||
.idea
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
14
.hadolint.yaml
Normal file
14
.hadolint.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
ignored:
|
||||
# ignore apt version pinning
|
||||
- DL3008
|
||||
# ignore pip version pinning
|
||||
- DL3013
|
||||
# ignore apk version pinning
|
||||
- DL3018
|
||||
# ignore pipefail cause Balena/resin.io images do not work with it
|
||||
- DL4006
|
||||
# ignore false positive regex
|
||||
- SC1083
|
||||
- SC2086
|
||||
# ignore as need for debug
|
||||
- SC2005
|
126
CLAUDE.md
Normal file
126
CLAUDE.md
Normal file
@ -0,0 +1,126 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a Docker image for running a Factorio headless server. It provides automated builds for multiple Factorio versions (stable and experimental) and supports both AMD64 and ARM64 architectures.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Key Components
|
||||
|
||||
1. **Docker Image Build System**
|
||||
- `build.py` - Unified Python script that builds both regular and rootless Docker images from `buildinfo.json`
|
||||
- `docker/Dockerfile` - Main Dockerfile that creates the Factorio server image
|
||||
- `docker/Dockerfile.rootless` - Dockerfile for rootless variant (runs as UID 1000)
|
||||
- `buildinfo.json` - Contains version info, SHA256 checksums, and tags for all supported versions
|
||||
- Supports multi-architecture builds (linux/amd64, linux/arm64) using Docker buildx
|
||||
|
||||
2. **Automated Updates**
|
||||
- `update.sh` - Checks for new Factorio releases and updates `buildinfo.json`
|
||||
- Updates README.md with new version tags
|
||||
- Commits changes and tags releases automatically
|
||||
- Run by GitHub Actions to keep images up-to-date
|
||||
|
||||
3. **Container Scripts**
|
||||
- `docker/files/docker-entrypoint.sh` - Main entrypoint that configures and starts the server
|
||||
- `docker/files/docker-update-mods.sh` - Updates mods on server start
|
||||
- `docker/files/docker-dlc.sh` - Manages DLC (Space Age) activation
|
||||
- `docker/files/scenario.sh` - Alternative entrypoint for launching scenarios
|
||||
- `docker/files/players-online.sh` - Checks if players are online (for watchtower integration)
|
||||
|
||||
4. **RCON Client**
|
||||
- `docker/rcon/` - C source for RCON client, built during Docker image creation
|
||||
- Allows sending commands to the running server
|
||||
|
||||
## Common Development Commands
|
||||
|
||||
### Building Images
|
||||
|
||||
```bash
|
||||
# Build regular images locally (single architecture)
|
||||
python3 build.py
|
||||
|
||||
# Build rootless images only
|
||||
python3 build.py --rootless
|
||||
|
||||
# Build both regular and rootless images
|
||||
python3 build.py --both
|
||||
|
||||
# Build and push multi-architecture images (regular only)
|
||||
python3 build.py --multiarch --push-tags
|
||||
|
||||
# Build and push both regular and rootless multi-architecture images
|
||||
python3 build.py --multiarch --push-tags --both
|
||||
```
|
||||
|
||||
### Running the Container
|
||||
|
||||
```bash
|
||||
# Basic run command
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
factoriotools/factorio
|
||||
|
||||
# Using docker-compose
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
```bash
|
||||
# Lint Dockerfiles
|
||||
./lint.sh
|
||||
```
|
||||
|
||||
### Testing Updates
|
||||
|
||||
```bash
|
||||
# Check for new Factorio versions and update buildinfo.json
|
||||
./update.sh
|
||||
```
|
||||
|
||||
## Key Configuration
|
||||
|
||||
### Environment Variables
|
||||
- `LOAD_LATEST_SAVE` - Load the most recent save (default: true)
|
||||
- `GENERATE_NEW_SAVE` - Generate a new save if none exists (default: false)
|
||||
- `SAVE_NAME` - Name of the save file to load/create
|
||||
- `UPDATE_MODS_ON_START` - Update mods before starting (requires USERNAME/TOKEN)
|
||||
- `DLC_SPACE_AGE` - Enable/disable Space Age DLC (default: true)
|
||||
- `PORT` - UDP port for game server (default: 34197)
|
||||
- `RCON_PORT` - TCP port for RCON (default: 27015)
|
||||
|
||||
### Volume Structure
|
||||
All data is stored in a single volume mounted at `/factorio`:
|
||||
```
|
||||
/factorio/
|
||||
├── config/ # Server configuration files
|
||||
├── mods/ # Game modifications
|
||||
├── saves/ # Save games
|
||||
├── scenarios/ # Scenario files
|
||||
└── script-output/ # Script output directory
|
||||
```
|
||||
|
||||
## Version Management
|
||||
|
||||
The project maintains compatibility with multiple Factorio versions:
|
||||
- Latest experimental version gets the `latest` tag
|
||||
- Latest stable version gets the `stable` tag
|
||||
- Each version also gets specific tags (e.g., `2.0.55`, `2.0`, `2`)
|
||||
- Legacy versions back to 0.12 are supported
|
||||
|
||||
Version updates are automated via GitHub Actions that run `update.sh` periodically.
|
||||
|
||||
## Testing Changes
|
||||
|
||||
1. Modify `buildinfo.json` to test specific versions
|
||||
2. Run `python3 build.py` to build regular images locally
|
||||
- Use `python3 build.py --rootless` for rootless images
|
||||
- Use `python3 build.py --both` to build both variants
|
||||
3. Test the container with your local data volume
|
||||
4. For production changes, ensure `update.sh` handles version transitions correctly
|
408
PERMISSION_ISSUES_GUIDE.md
Normal file
408
PERMISSION_ISSUES_GUIDE.md
Normal file
@ -0,0 +1,408 @@
|
||||
# Factorio Docker Permission Issues - Solutions and Workarounds
|
||||
|
||||
This document provides comprehensive solutions and workarounds for permission-related issues in the Factorio Docker container, based on detailed analysis of issues #558, #556, #555, #549, #496, #501, #492, and #420.
|
||||
|
||||
## Table of Contents
|
||||
- [Root Cause Analysis](#root-cause-analysis)
|
||||
- [Critical Prerequisites](#critical-prerequisites)
|
||||
- [General Solutions](#general-solutions)
|
||||
- [Platform-Specific Issues](#platform-specific-issues)
|
||||
- [Docker System Requirements](#docker-system-requirements)
|
||||
- [Advanced Troubleshooting](#advanced-troubleshooting)
|
||||
- [Known Issues and Limitations](#known-issues-and-limitations)
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
Based on detailed investigation by maintainer @Fank and community reports, the permission issues stem from:
|
||||
|
||||
1. **Container Architecture Issues**:
|
||||
- No `USER` directive in Dockerfile despite creating a factorio user
|
||||
- Container starts as root and performs recursive `chown` on every start
|
||||
- The recursive `chown -R factorio:factorio /factorio` can be interrupted, leaving inconsistent permissions
|
||||
- Dynamic UID/GID mapping using PUID/PGID environment variables adds complexity
|
||||
|
||||
2. **Rootless Docker Complications**:
|
||||
- UID namespace remapping (e.g., container UID 845 → host UID 100844)
|
||||
- Rootless Docker daemons cannot change ownership of bind-mounted volumes
|
||||
- Different rootless implementations use different UID mappings
|
||||
|
||||
3. **Host System Dependencies**:
|
||||
- Older Docker versions (especially pre-20.x) have permission handling bugs
|
||||
- Some kernel versions have issues with user namespace operations
|
||||
- SELinux and AppArmor can interfere with volume permissions
|
||||
|
||||
## Critical Prerequisites
|
||||
|
||||
### Update Your System First!
|
||||
Many permission issues are caused by outdated system components:
|
||||
|
||||
```bash
|
||||
# For Ubuntu/Debian
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
|
||||
# Specifically update Docker to 27.x or newer
|
||||
# Follow: https://docs.docker.com/engine/install/ubuntu/#install-docker-engine
|
||||
```
|
||||
|
||||
**Important**: Multiple users reported that updating Docker resolved their "Operation not permitted" errors.
|
||||
|
||||
## General Solutions
|
||||
|
||||
### Solution A: Pre-create Directories with Correct Permissions
|
||||
```bash
|
||||
# Create the directory structure
|
||||
sudo mkdir -p /opt/factorio/{saves,mods,config,scenarios,script-output}
|
||||
|
||||
# Set ownership to factorio user (845:845)
|
||||
sudo chown -R 845:845 /opt/factorio
|
||||
|
||||
# Set appropriate permissions (note the 'u+rwx' for write access)
|
||||
sudo chmod -R u+rwx /opt/factorio
|
||||
```
|
||||
|
||||
### Solution B: Use the Rootless Docker Image (Recommended)
|
||||
The project now provides a rootless variant that runs as UID 1000, which avoids most permission issues:
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
factoriotools/factorio:latest-rootless
|
||||
```
|
||||
|
||||
**Benefits of rootless images**:
|
||||
- No `chown` operations on startup
|
||||
- No need to pre-create directories with specific permissions
|
||||
- Works seamlessly with rootless Docker installations
|
||||
- Avoids the recursive permission changes that can be interrupted
|
||||
|
||||
**Available rootless tags**:
|
||||
- `latest-rootless`
|
||||
- `stable-rootless`
|
||||
- `2.0.55-rootless` (or any specific version with `-rootless` suffix)
|
||||
|
||||
## Platform-Specific Issues and Solutions
|
||||
|
||||
### NixOS with Rootless Docker
|
||||
|
||||
**Problem**: Permission denied errors when creating directories, even after setting ownership to 845:845. Files show ownership by UID 100844 instead of 845.
|
||||
|
||||
**Solutions**:
|
||||
1. **Find and use your actual rootless Docker user ID**:
|
||||
```bash
|
||||
# Method 1: Check your user ID
|
||||
id -u
|
||||
|
||||
# Method 2: Check existing Docker volumes for the UID Docker is using
|
||||
ls -lan /path/to/other/docker/volumes
|
||||
|
||||
# Common rootless Docker UIDs:
|
||||
# - 100999 (NixOS default)
|
||||
# - 100844 (as reported in issue #558)
|
||||
# - 1000 (some configurations)
|
||||
|
||||
# Apply the correct ownership
|
||||
sudo chown -R 100999:100999 ./factorio
|
||||
```
|
||||
|
||||
2. **Configure NixOS Docker properly**:
|
||||
```nix
|
||||
# In configuration.nix
|
||||
virtualisation.docker.rootless = {
|
||||
enable = true;
|
||||
setSocketVariable = true;
|
||||
};
|
||||
```
|
||||
|
||||
3. **Port Mapping Issues**: Rootless Docker on NixOS has issues with userland-proxy that can cause random port assignments. Consider using host networking if possible.
|
||||
|
||||
### macOS with Colima
|
||||
|
||||
**Problem**: `copy_file` permission denied errors, even with correct ownership. Permission errors when running docker-dlc.sh.
|
||||
|
||||
**Solutions**:
|
||||
1. **Set broader permissions before mounting**:
|
||||
```bash
|
||||
# Create directory structure
|
||||
mkdir -p ./factorio-server/{saves,mods,config,scenarios}
|
||||
|
||||
# Set ownership AND permissions
|
||||
sudo chown -R 845:845 ./factorio-server
|
||||
sudo chmod -R 775 ./factorio-server
|
||||
```
|
||||
|
||||
2. **Use Docker Desktop instead of Colima** if the issues persist, as it has better macOS integration
|
||||
|
||||
3. **Specify PUID/PGID explicitly**:
|
||||
```yaml
|
||||
environment:
|
||||
- PUID=502 # Common macOS user ID
|
||||
- PGID=20 # Common macOS staff group
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
**Problem**: Cannot remove temporary locale files due to Windows-Linux permission translation. Errors like "Permission denied trying to remove /factorio/temp/currently-playing/locale/de".
|
||||
|
||||
**Solutions**:
|
||||
1. **Use WSL2 backend** for Docker Desktop (required for proper Linux filesystem semantics)
|
||||
|
||||
2. **Store volumes in WSL2 filesystem** instead of Windows filesystem:
|
||||
```bash
|
||||
# Inside WSL2 terminal
|
||||
mkdir -p ~/factorio
|
||||
chmod -R 777 ~/factorio
|
||||
```
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml - use WSL2 path
|
||||
volumes:
|
||||
- ~/factorio:/factorio
|
||||
```
|
||||
|
||||
3. **Avoid Windows drive mounts** (like `W:\docker\factorio`) as they have inherent permission translation issues
|
||||
|
||||
4. **Add :Z flag for SELinux context** (some Windows Docker setups benefit from this):
|
||||
```yaml
|
||||
volumes:
|
||||
- ~/factorio:/factorio:Z
|
||||
```
|
||||
|
||||
### Synology NAS
|
||||
|
||||
**Problem**: Permission denied when accessing mounted volumes. Error: "filesystem error: status: Permission denied [/factorio/saves]".
|
||||
|
||||
**Solutions**:
|
||||
1. **Create and set permissions via SSH**:
|
||||
```bash
|
||||
# SSH into Synology
|
||||
sudo mkdir -p /volume1/docker/factorio
|
||||
sudo chown -R 845:845 /volume1/docker/factorio
|
||||
sudo chmod -R u+rwx /volume1/docker/factorio # Important: u+rwx for write access
|
||||
```
|
||||
|
||||
2. **Use the correct volume path in your container**:
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /volume1/docker/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=always \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
3. **Check DSM Docker permissions** - ensure the Docker package has proper permissions to the shared folder
|
||||
|
||||
## Docker System Requirements
|
||||
|
||||
### Minimum Docker Version
|
||||
Based on community reports, these Docker versions are known to work:
|
||||
- **Docker 27.4.1** - Confirmed working
|
||||
- **Docker 20.x+** - Generally stable
|
||||
- **Docker 19.x and below** - Known permission issues
|
||||
|
||||
**Check your Docker version**:
|
||||
```bash
|
||||
docker --version
|
||||
# If below 20.x, update immediately!
|
||||
```
|
||||
|
||||
### "Operation not permitted" at Util.cpp:81
|
||||
This specific error is often caused by:
|
||||
1. **Outdated Docker version** - Update Docker first!
|
||||
2. **Outdated kernel** - Run system updates
|
||||
3. **Missing kernel capabilities** - Check Docker daemon configuration
|
||||
|
||||
## Docker Compose Best Practices
|
||||
|
||||
### Basic Configuration
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
factorio:
|
||||
image: factoriotools/factorio:stable
|
||||
container_name: factorio
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
- "27015:27015/tcp"
|
||||
volumes:
|
||||
- ./factorio:/factorio
|
||||
restart: unless-stopped
|
||||
stdin_open: true # For interactive console
|
||||
tty: true
|
||||
```
|
||||
|
||||
### Advanced Configuration for Permission Issues
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
factorio:
|
||||
image: factoriotools/factorio:stable
|
||||
container_name: factorio
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
- "27015:27015/tcp"
|
||||
volumes:
|
||||
- ./factorio:/factorio:Z # :Z for SELinux systems
|
||||
restart: unless-stopped
|
||||
# user: "845:845" # WARNING: This might break the entrypoint script
|
||||
environment:
|
||||
- PUID=845
|
||||
- PGID=845
|
||||
- UPDATE_MODS_ON_START=false # Disable if having permission issues
|
||||
```
|
||||
|
||||
### Rootless Docker Configuration
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
factorio:
|
||||
image: factoriotools/factorio:latest-rootless
|
||||
container_name: factorio
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
- "27015:27015/tcp"
|
||||
volumes:
|
||||
- ./factorio:/factorio
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=1000 # Rootless default
|
||||
- PGID=1000
|
||||
```
|
||||
|
||||
## Advanced Troubleshooting
|
||||
|
||||
### Step-by-Step Diagnosis
|
||||
|
||||
1. **Check Current Ownership**:
|
||||
```bash
|
||||
ls -lan ./factorio
|
||||
# Look for UIDs like 845, 1000, 100844, 100999
|
||||
```
|
||||
|
||||
2. **Verify Docker User Mapping**:
|
||||
```bash
|
||||
# Check what user the container is running as
|
||||
docker exec factorio id
|
||||
|
||||
# Check file ownership inside container
|
||||
docker exec factorio ls -lan /factorio
|
||||
```
|
||||
|
||||
3. **Test Without Volume Mount** (isolates host permission issues):
|
||||
```bash
|
||||
docker run --rm -it factoriotools/factorio:stable
|
||||
# If this works, the issue is with your host volume permissions
|
||||
```
|
||||
|
||||
4. **Check Security Modules**:
|
||||
```bash
|
||||
# SELinux (Fedora, RHEL, CentOS)
|
||||
getenforce
|
||||
# If "Enforcing", try adding :Z to volume mount
|
||||
|
||||
# AppArmor (Ubuntu, Debian)
|
||||
sudo apparmor_status | grep docker
|
||||
```
|
||||
|
||||
5. **Debug the Entrypoint Script**:
|
||||
```bash
|
||||
# Run with debug output
|
||||
docker run --rm -it \
|
||||
-e DEBUG=true \
|
||||
-v ./factorio:/factorio \
|
||||
factoriotools/factorio:stable
|
||||
```
|
||||
|
||||
### Common Error Messages and Solutions
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| `Util.cpp:81: Operation not permitted` | Outdated Docker/kernel | Update Docker and system packages |
|
||||
| `chown: Operation not permitted` | Rootless Docker | Use rootless Docker UID for ownership |
|
||||
| `Permission denied [/factorio/saves]` | Wrong directory permissions | `chmod -R u+rwx` on host directory |
|
||||
| `Couldn't create lock file /factorio/.lock` | Container can't write to volume | Check volume mount and permissions |
|
||||
| `Map version X cannot be loaded` | Version mismatch | Use correct Docker image version |
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
### Interrupted chown Operations
|
||||
The container performs `chown -R factorio:factorio /factorio` on every start. If the container is killed during this operation:
|
||||
- Files will have inconsistent ownership
|
||||
- Some files owned by 845, others by different UIDs
|
||||
- Solution: Let the container complete startup before stopping
|
||||
|
||||
### Rootless Docker Port Mapping
|
||||
**Issue #496**: Rootless Docker with userland-proxy causes random port assignments instead of the configured 34197.
|
||||
- **Workaround**: Use host networking mode if possible
|
||||
- **Note**: This is a Docker limitation, not specific to this image
|
||||
|
||||
### Map Version Compatibility
|
||||
**Problem**: "Map version 2.0.23-0 cannot be loaded because it is higher than the game version".
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Use a version that matches or exceeds your save
|
||||
docker pull factoriotools/factorio:2.0.23
|
||||
# Or always use latest for newest features
|
||||
docker pull factoriotools/factorio:latest
|
||||
```
|
||||
|
||||
## Recommended Approach
|
||||
|
||||
### For New Installations
|
||||
1. **Update your system first** - Many issues are caused by old Docker versions
|
||||
2. **Try the rootless image first** - It avoids most permission issues entirely
|
||||
3. **Pre-create directories** with correct permissions if using the standard image
|
||||
4. **Test without volumes** first to ensure the image works
|
||||
|
||||
### For Existing Installations with Issues
|
||||
1. **Stop the container** and let it shut down cleanly
|
||||
2. **Backup your data** before making changes
|
||||
3. **Check Docker version** - update if below 20.x
|
||||
4. **Fix permissions** using the platform-specific solution
|
||||
5. **Consider rootless variant** for easier permission management
|
||||
|
||||
### Best Practices
|
||||
- **Let the container start fully** before stopping (avoid interrupted chown)
|
||||
- **Use named volumes** instead of bind mounts when possible
|
||||
- **Monitor first startup** to ensure permissions are set correctly
|
||||
- **Keep Docker updated** to avoid known bugs
|
||||
|
||||
## Community Solutions
|
||||
|
||||
### Proposed Improvements (from @Fank)
|
||||
1. **Add USER directive** in Dockerfile after creating directories
|
||||
2. **Optimize chown logic** to only run when ownership is wrong
|
||||
3. **Implement fixuid** for better UID/GID mapping
|
||||
4. **Add health checks** to ensure permissions are correct before starting
|
||||
|
||||
### Alternative Images
|
||||
Some users have tried other Factorio Docker images (e.g., goofball222/factorio) but report the same Util.cpp:81 errors, suggesting this is a broader ecosystem issue related to Docker versions and system configurations.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Platform | Common UID | Recommended Approach |
|
||||
|----------|-----------|---------------------|
|
||||
| Standard Docker | 845 | Update Docker, use `chown 845:845` |
|
||||
| Rootless Docker (NixOS) | 100999, 100844 | Find actual UID, chown to that |
|
||||
| macOS (Docker Desktop) | 502 (user), 20 (staff) | Use PUID/PGID env vars |
|
||||
| Windows | N/A | Use WSL2 filesystem |
|
||||
| Synology NAS | varies | Check DSM user, ensure Docker has folder access |
|
||||
|
||||
## Getting Help
|
||||
|
||||
If these solutions don't work:
|
||||
1. **Update everything first** (Docker, kernel, system packages)
|
||||
2. **Provide full details** when reporting issues:
|
||||
- Docker version (`docker --version`)
|
||||
- OS and version
|
||||
- Full error messages
|
||||
- Output of `ls -lan` on your volume
|
||||
3. **Try the rootless image** as an alternative
|
||||
4. **Check issue #558** for ongoing discussions
|
||||
|
||||
Remember: The vast majority of permission issues are resolved by updating Docker to version 20.x or newer!
|
551
README.md
551
README.md
@ -1,2 +1,549 @@
|
||||
# docker_factorio_server
|
||||
Factorio Server in Docker
|
||||
# Factorio [](https://hub.docker.com/r/factoriotools/factorio/) [](https://hub.docker.com/r/factoriotools/factorio/) [](https://hub.docker.com/r/factoriotools/factorio/)
|
||||
|
||||
> [!NOTE]
|
||||
> Support for ARM is experimental. Expect crashes and lag if you try to run this on a raspberry pi.
|
||||
|
||||
[中文](./README_zh_CN.md)
|
||||
|
||||
<!-- start autogeneration tags -->
|
||||
* `latest, 2.0.60`
|
||||
* `2, 2.0, 2.0.55, stable, stable-2.0.55`
|
||||
* `2.0.59`
|
||||
* `stable-1.1.110, 1, 1.1, 1.1.110`
|
||||
* `1.0.0, 1.0`
|
||||
* `0.17.79, 0.17`
|
||||
* `0.16.51, 0.16`
|
||||
* `0.15.40, 0.15`
|
||||
* `0.14.23, 0.14`
|
||||
* `0.13.20, 0.13`
|
||||
* `0.12.35, 0.12`
|
||||
<!-- end autogeneration tags -->
|
||||
|
||||
## Tag descriptions
|
||||
|
||||
* `latest` - most up-to-date version (may be experimental).
|
||||
* `stable` - version declared stable on [factorio.com](https://www.factorio.com) ([FFF-435 Since 2.0 versions gets released as experimental first, once stable it will be marked as stable](https://factorio.com/blog/post/fff-435)).
|
||||
* `0.x` - latest version in a branch.
|
||||
* `0.x.y` - a specific version.
|
||||
* `0.x-z` - incremental fix for that version.
|
||||
|
||||
## What is Factorio?
|
||||
|
||||
[Factorio](https://www.factorio.com) is a game in which you build and maintain factories.
|
||||
|
||||
You will be mining resources, researching technologies, building infrastructure, automating production and fighting enemies. Use your imagination to design your factory, combine simple elements into ingenious structures, apply management skills to keep it working and finally protect it from the creatures who don't really like you.
|
||||
|
||||
The game is very stable and optimized for building massive factories. You can create your own maps, write mods in Lua or play with friends via Multiplayer.
|
||||
|
||||
NOTE: This is only the server. The full game is available at [Factorio.com](https://www.factorio.com), [Steam](https://store.steampowered.com/app/427520/), [GOG.com](https://www.gog.com/game/factorio) and [Humble Bundle](https://www.humblebundle.com/store/factorio).
|
||||
|
||||
## Usage
|
||||
|
||||
### Quick Start
|
||||
|
||||
Run the server to create the necessary folder structure and configuration files. For this example data is stored in `/opt/factorio`.
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /opt/factorio
|
||||
sudo chown 845:845 /opt/factorio
|
||||
sudo docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
For those new to Docker, here is an explanation of the options:
|
||||
|
||||
* `-d` - Run as a daemon ("detached").
|
||||
* `-p` - Expose ports.
|
||||
* `-v` - Mount `/opt/factorio` on the local file system to `/factorio` in the container.
|
||||
* `--restart` - Restart the server if it crashes and at system start
|
||||
* `--name` - Name the container "factorio" (otherwise it has a funny random name).
|
||||
|
||||
The `chown` command is needed because in 0.16+, we no longer run the game server as root for security reasons, but rather as a 'factorio' user with user id 845. The host must therefore allow these files to be written by that user.
|
||||
|
||||
Check the logs to see what happened:
|
||||
|
||||
```shell
|
||||
docker logs factorio
|
||||
```
|
||||
|
||||
Stop the server:
|
||||
|
||||
```shell
|
||||
docker stop factorio
|
||||
```
|
||||
|
||||
Now there's a `server-settings.json` file in the folder `/opt/factorio/config`. Modify this to your liking and restart the server:
|
||||
|
||||
```shell
|
||||
docker start factorio
|
||||
```
|
||||
|
||||
Try to connect to the server. Check the logs if it isn't working.
|
||||
|
||||
### Console
|
||||
|
||||
To issue console commands to the server, start the server in interactive mode with `-it`. Open the console with `docker attach` and then type commands.
|
||||
|
||||
```shell
|
||||
docker run -d -it \
|
||||
--name factorio \
|
||||
factoriotools/factorio
|
||||
docker attach factorio
|
||||
```
|
||||
|
||||
### RCON (2.0.18+)
|
||||
|
||||
Alternativly (e.g. for scripting) the RCON connection can be used to send commands to the running factorio server.
|
||||
This does not require the RCON connection to be exposed.
|
||||
|
||||
```shell
|
||||
docker exec factorio rcon /h
|
||||
```
|
||||
|
||||
### Upgrading
|
||||
|
||||
Before upgrading backup the save. It's easy to make a save in the client.
|
||||
|
||||
Ensure `-v` was used to run the server so the save is outside of the Docker container. The `docker rm` command completely destroys the container, which includes the save if it isn't stored in a data volume.
|
||||
|
||||
Delete the container and refresh the image:
|
||||
|
||||
```shell
|
||||
docker stop factorio
|
||||
docker rm factorio
|
||||
docker pull factoriotools/factorio
|
||||
```
|
||||
|
||||
Now run the server as before. In about a minute the new version of Factorio should be up and running, complete with saves and config!
|
||||
|
||||
### Saves
|
||||
|
||||
A new map named `_autosave1.zip` is generated the first time the server is started. The `map-gen-settings.json` and `map-settings.json` files in `/opt/factorio/config` are used for the map settings. On subsequent runs the newest save is used.
|
||||
|
||||
To load an old save stop the server and run the command `touch oldsave.zip`. This resets the date. Then restart the server. Another option is to delete all saves except one.
|
||||
|
||||
To generate a new map stop the server, delete all of the saves and restart the server.
|
||||
|
||||
#### Specify a save directly (0.17.79-2+)
|
||||
|
||||
You can specify a specific save to load by configuring the server through a set of environment variables:
|
||||
|
||||
To load an existing save set `SAVE_NAME` to the name of your existing save file located within the `saves` directory, without the `.zip` extension:
|
||||
|
||||
```shell
|
||||
sudo docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
-e LOAD_LATEST_SAVE=false \
|
||||
-e SAVE_NAME=replaceme \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
To generate a new map set `GENERATE_NEW_SAVE=true` and specify `SAVE_NAME`:
|
||||
|
||||
```shell
|
||||
sudo docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
-e LOAD_LATEST_SAVE=false \
|
||||
-e GENERATE_NEW_SAVE=true \
|
||||
-e SAVE_NAME=replaceme \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
To generate a new map with a specific preset (e.g., death-world):
|
||||
|
||||
```shell
|
||||
sudo docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
-e LOAD_LATEST_SAVE=false \
|
||||
-e GENERATE_NEW_SAVE=true \
|
||||
-e SAVE_NAME=replaceme \
|
||||
-e PRESET=death-world \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
### Mods
|
||||
|
||||
Copy mods into the mods folder and restart the server.
|
||||
|
||||
As of 0.17 a new environment variable was added ``UPDATE_MODS_ON_START`` which if set to ``true`` will cause the mods get to updated on server start. If set a valid [Factorio Username and Token](https://www.factorio.com/profile) must be supplied or else the server will not start. They can either be set as docker secrets, environment variables, or pulled from the server-settings.json file.
|
||||
|
||||
**Note:** When using the Space Age DLC, the built-in mods (`elevated-rails`, `quality`, and `space-age`) are automatically skipped during mod updates to prevent conflicts. These mods are included with the DLC and should not be downloaded separately.
|
||||
|
||||
### Scenarios
|
||||
|
||||
If you want to launch a scenario from a clean start (not from a saved map) you'll need to start the docker image from an alternate entrypoint. To do this, use the example entrypoint file stored in the /factorio/entrypoints directory in the volume, and launch the image with the following syntax. Note that this is the normal syntax with the addition of the --entrypoint setting AND the additional argument at the end, which is the name of the Scenario in the Scenarios folder.
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
--entrypoint "/scenario.sh" \
|
||||
factoriotools/factorio \
|
||||
MyScenarioName
|
||||
```
|
||||
|
||||
### Converting Scenarios to Regular Maps
|
||||
|
||||
If you would like to export your scenario to a saved map, you can use the example entrypoint similar to the Scenario usage above. Factorio will run once, converting the Scenario to a saved Map in your saves directory. A restart of the docker image using the standard options will then load that map, just as if the scenario were just started by the Scenarios example noted above.
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
--entrypoint "/scenario2map.sh" \
|
||||
factoriotools/factorio
|
||||
MyScenarioName
|
||||
```
|
||||
|
||||
### RCON
|
||||
|
||||
Set the RCON password in the `rconpw` file. A random password is generated if `rconpw` doesn't exist.
|
||||
|
||||
To change the password, stop the server, modify `rconpw`, and restart the server.
|
||||
|
||||
To "disable" RCON don't expose port 27015, i.e. start the server without `-p 27015:27015/tcp`. RCON is still running, but nobody can to connect to it.
|
||||
|
||||
### Whitelisting (0.15.3+)
|
||||
|
||||
Create file `config/server-whitelist.json` and add the whitelisted users.
|
||||
|
||||
```json
|
||||
[
|
||||
"you",
|
||||
"friend"
|
||||
]
|
||||
```
|
||||
|
||||
### Banlisting (0.17.1+)
|
||||
|
||||
Create file `config/server-banlist.json` and add the banlisted users.
|
||||
|
||||
```json
|
||||
[
|
||||
"bad_person",
|
||||
"other_bad_person"
|
||||
]
|
||||
```
|
||||
|
||||
### Adminlisting (0.17.1+)
|
||||
|
||||
Create file `config/server-adminlist.json` and add the adminlisted users.
|
||||
|
||||
```json
|
||||
[
|
||||
"you",
|
||||
"friend"
|
||||
]
|
||||
```
|
||||
|
||||
### Customize configuration files (0.17.x+)
|
||||
|
||||
Out-of-the box, factorio does not support environment variables inside the configuration files. A workaround is the usage of `envsubst` which generates the configuration files dynamically during startup from environment variables set in docker-compose:
|
||||
|
||||
Example which replaces the server-settings.json:
|
||||
|
||||
```yaml
|
||||
factorio_1:
|
||||
image: factoriotools/factorio
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
volumes:
|
||||
- /opt/factorio:/factorio
|
||||
- ./server-settings.json:/server-settings.json
|
||||
environment:
|
||||
- INSTANCE_NAME=Your Instance's Name
|
||||
- INSTANCE_DESC=Your Instance's Description
|
||||
entrypoint: /bin/sh -c "mkdir -p /factorio/config && envsubst < /server-settings.json > /factorio/config/server-settings.json && exec /docker-entrypoint.sh"
|
||||
```
|
||||
|
||||
The `server-settings.json` file may then contain the variable references like this:
|
||||
|
||||
```json
|
||||
"name": "${INSTANCE_NAME}",
|
||||
"description": "${INSTANCE_DESC}",
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
These are the environment variables which can be specified at container run time.
|
||||
|
||||
| Variable Name | Description | Default | Available in |
|
||||
|----------------------|----------------------------------------------------------------------|----------------|--------------|
|
||||
| GENERATE_NEW_SAVE | Generate a new save if one does not exist before starting the server | false | 0.17+ |
|
||||
| LOAD_LATEST_SAVE | Load latest when true. Otherwise load SAVE_NAME | true | 0.17+ |
|
||||
| PORT | UDP port the server listens on | 34197 | 0.15+ |
|
||||
| BIND | IP address (v4 or v6) the server listens on (IP\[:PORT]) | | 0.15+ |
|
||||
| RCON_PORT | TCP port the rcon server listens on | 27015 | 0.15+ |
|
||||
| SAVE_NAME | Name to use for the save file | _autosave1 | 0.17+ |
|
||||
| PRESET | Map generation preset when GENERATE_NEW_SAVE is true | | 0.17+ |
|
||||
| TOKEN | factorio.com token | | 0.17+ |
|
||||
| UPDATE_MODS_ON_START | If mods should be updated before starting the server | | 0.17+ |
|
||||
| USERNAME | factorio.com username | | 0.17+ |
|
||||
| CONSOLE_LOG_LOCATION | Saves the console log to the specifies location | | |
|
||||
| DLC_SPACE_AGE | Enables or disables the mods for DLC Space Age in mod-list.json[^1] | true | 2.0.8+ |
|
||||
| MODS | Mod directory to use | /factorio/mods | 2.0.8+ |
|
||||
|
||||
**Note:** All environment variables are compared as strings
|
||||
|
||||
#### PRESET Values
|
||||
|
||||
The `PRESET` environment variable is used when generating a new map (when `GENERATE_NEW_SAVE=true`). It corresponds to Factorio's built-in map generation presets. Common values include:
|
||||
|
||||
- `default` - Normal settings
|
||||
- `rich-resources` - Resources are more abundant
|
||||
- `marathon` - Recipes and technologies are more expensive
|
||||
- `death-world` - Biters are more aggressive and numerous
|
||||
- `death-world-marathon` - Combines death-world and marathon settings
|
||||
- `rail-world` - Resources are further apart, encouraging train usage
|
||||
- `ribbon-world` - Map height is limited for a unique challenge
|
||||
|
||||
If PRESET is not specified or left empty, the map will be generated using the settings from `map-gen-settings.json` and `map-settings.json` without a preset.
|
||||
|
||||
## Container Details
|
||||
|
||||
The philosophy is to [keep it simple](http://wiki.c2.com/?KeepItSimple).
|
||||
|
||||
* The server should bootstrap itself.
|
||||
* Prefer configuration files over environment variables.
|
||||
* Use one volume for data.
|
||||
|
||||
### Volumes
|
||||
|
||||
To keep things simple, the container uses a single volume mounted at `/factorio`. This volume stores configuration, mods, and saves.
|
||||
|
||||
The files in this volume should be owned by the factorio user, uid 845.
|
||||
|
||||
```text
|
||||
factorio
|
||||
|-- config
|
||||
| |-- map-gen-settings.json
|
||||
| |-- map-settings.json
|
||||
| |-- rconpw
|
||||
| |-- server-adminlist.json
|
||||
| |-- server-banlist.json
|
||||
| |-- server-settings.json
|
||||
| `-- server-whitelist.json
|
||||
|-- mods
|
||||
| `-- fancymod.zip
|
||||
`-- saves
|
||||
`-- _autosave1.zip
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
[Docker Compose](https://docs.docker.com/compose/install/) is an easy way to run Docker containers.
|
||||
|
||||
* docker-engine >= 1.10.0 is required
|
||||
* docker-compose >=1.6.0 is required
|
||||
|
||||
First get a [docker-compose.yml](https://github.com/factoriotools/factorio-docker/blob/master/docker/docker-compose.yml) file. To get it from this repository:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/factoriotools/factorio-docker.git
|
||||
cd factorio-docker/docker
|
||||
```
|
||||
|
||||
Or make your own:
|
||||
|
||||
```yaml
|
||||
version: '2'
|
||||
services:
|
||||
factorio:
|
||||
image: factoriotools/factorio
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
- "27015:27015/tcp"
|
||||
volumes:
|
||||
- /opt/factorio:/factorio
|
||||
```
|
||||
|
||||
Now cd to the directory with docker-compose.yml and run:
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /opt/factorio
|
||||
sudo chown 845:845 /opt/factorio
|
||||
sudo docker-compose up -d
|
||||
```
|
||||
|
||||
### Ports
|
||||
|
||||
* `34197/udp` - Game server (required). This can be changed with the `PORT` environment variable.
|
||||
* `27015/tcp` - RCON (optional).
|
||||
|
||||
## LAN Games
|
||||
|
||||
Ensure the `lan` setting in server-settings.json is `true`.
|
||||
|
||||
```json
|
||||
"visibility":
|
||||
{
|
||||
"public": false,
|
||||
"lan": true
|
||||
},
|
||||
```
|
||||
|
||||
Start the container with the `--network=host` option so clients can automatically find LAN games. Refer to the Quick Start to create the `/opt/factorio` directory.
|
||||
|
||||
```shell
|
||||
sudo docker run -d \
|
||||
--network=host \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
## Deploy to other plaforms
|
||||
|
||||
### Vagrant
|
||||
|
||||
[Vagrant](https://www.vagrantup.com/) is a easy way to setup a virtual machine (VM) to run Docker. The [Factorio Vagrant box repository](https://github.com/dtandersen/factorio-lan-vagrant) contains a sample Vagrantfile.
|
||||
|
||||
For LAN games the VM needs an internal IP in order for clients to connect. One way to do this is with a public network. The VM uses DHCP to acquire an IP address. The VM must also forward port 34197.
|
||||
|
||||
```ruby
|
||||
config.vm.network "public_network"
|
||||
config.vm.network "forwarded_port", guest: 34197, host: 34197
|
||||
```
|
||||
|
||||
### Amazon Web Services (AWS) Deployment
|
||||
|
||||
If you're looking for a simple way to deploy this to the Amazon Web Services Cloud, check out the [Factorio Server Deployment (CloudFormation) repository](https://github.com/m-chandler/factorio-spot-pricing). This repository contains a CloudFormation template that will get you up and running in AWS in a matter of minutes. Optionally it uses Spot Pricing so the server is very cheap, and you can easily turn it off when not in use.
|
||||
|
||||
## Using a reverse proxy
|
||||
|
||||
If you need to use a reverse proxy you can use the following nginx snippet:
|
||||
|
||||
```
|
||||
stream {
|
||||
server {
|
||||
listen 34197 udp reuseport;
|
||||
proxy_pass my.upstream.host:34197;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your factorio host uses multiple IP addresses (very common with IPv6), you might additionally need to bind Factorio to a single IP (otherwise the UDP proxy might get confused with IP mismatches). To do that pass the `BIND` envvar to the container: `docker run --network=host -e BIND=2a02:1234::5678 ...`
|
||||
|
||||
## Rootless Docker Support (Experimental)
|
||||
|
||||
> **Note**: Rootless support is currently experimental. Please report any issues you encounter.
|
||||
|
||||
If you're experiencing permission issues or want better security, consider using the rootless images. These images are designed to work seamlessly with rootless Docker installations and avoid common permission problems.
|
||||
|
||||
### What are Rootless Images?
|
||||
|
||||
The rootless images differ from regular images in several ways:
|
||||
- Run as UID 1000 (non-root) by default
|
||||
- No dynamic UID/GID mapping (PUID/PGID not supported)
|
||||
- No runtime chown operations
|
||||
- All directories created with open permissions during build
|
||||
|
||||
### Rootless Image Tags
|
||||
|
||||
Each regular tag has a corresponding rootless version with the `-rootless` suffix:
|
||||
- `latest-rootless` (experimental)
|
||||
- `stable-rootless` (experimental)
|
||||
- `2.0.55-rootless` (experimental)
|
||||
|
||||
### Quick Start with Rootless
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v ~/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio:stable-rootless
|
||||
```
|
||||
|
||||
Key differences:
|
||||
- No `chown` command needed
|
||||
- No PUID/PGID environment variables
|
||||
- Runs as UID 1000 by default
|
||||
- No permission issues with volumes
|
||||
|
||||
### When to Use Rootless Images
|
||||
|
||||
Consider using rootless images if you:
|
||||
- Are running Docker in rootless mode
|
||||
- Experience permission issues with volume mounts
|
||||
- Want to avoid containers running as root
|
||||
- Don't need dynamic UID/GID mapping via PUID/PGID
|
||||
|
||||
### Limitations
|
||||
|
||||
- PUID/PGID environment variables are not supported
|
||||
- Fixed to UID 1000 (may not match your host user)
|
||||
- Experimental feature - may have undiscovered issues
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Permission Issues
|
||||
|
||||
If you're experiencing permission errors such as:
|
||||
- `chown: Operation not permitted`
|
||||
- `Permission denied [/factorio/saves]`
|
||||
- `Util.cpp:81: Operation not permitted`
|
||||
- Files owned by unexpected UIDs (like 100844 instead of 845)
|
||||
|
||||
Please refer to our comprehensive [Permission Issues Guide](./PERMISSION_ISSUES_GUIDE.md) for detailed solutions. Common fixes include:
|
||||
- **Updating Docker** to version 20.x or newer (this resolves many issues)
|
||||
- **Using the rootless image** variants (e.g., `factoriotools/factorio:stable-rootless`)
|
||||
- **Setting correct ownership** for your specific Docker configuration
|
||||
|
||||
### My server is listed in the server browser, but nobody can connect
|
||||
|
||||
Check the logs. If there is the line `Own address is RIGHT IP:WRONG PORT`, then this could be caused by the Docker proxy. If the the IP and port is correct it's probably a port forwarding or firewall issue instead.
|
||||
|
||||
By default, Docker routes traffic through a proxy. The proxy changes the source UDP port, so the wrong port is detected. See the forum post *[Incorrect port detected for docker hosted server](https://forums.factorio.com/viewtopic.php?f=49&t=35255)* for details.
|
||||
|
||||
To fix the incorrect port, start the Docker service with the `--userland-proxy=false` switch. Docker will route traffic with iptables rules instead of a proxy. Add the switch to the `DOCKER_OPTS` environment variable or `ExecStart` in the Docker systemd service definition. The specifics vary by operating system.
|
||||
|
||||
### When I run a server on a port besides 34197 nobody can connect from the server browser
|
||||
|
||||
Use the `PORT` environment variable to start the server on the a different port, .e.g. `docker run -e "PORT=34198"`. This changes the source port on the packets used for port detection. `-p 34198:34197` works fine for private servers, but the server browser detects the wrong port.
|
||||
|
||||
## Contributors
|
||||
|
||||
* [dtandersen](https://github.com/dtandersen) - Maintainer
|
||||
* [Fank](https://github.com/Fankserver) - Programmer of the Factorio watchdog that keeps the version up-to-date.
|
||||
* [SuperSandro2000](https://github.com/supersandro2000) - CI Guy, Maintainer and runner of the Factorio watchdog. Contributed version updates and wrote the Travis scripts.
|
||||
* [DBendit](https://github.com/DBendit/docker_factorio_server) - Coded admin list, ban list support and contributed version updates
|
||||
* [Zopanix](https://github.com/zopanix/docker_factorio_server) - Original Author
|
||||
* [Rfvgyhn](https://github.com/Rfvgyhn/docker-factorio) - Coded randomly generated RCON password
|
||||
* [gnomus](https://github.com/gnomus/docker_factorio_server) - Coded white listing support
|
||||
* [bplein](https://github.com/bplein/docker_factorio_server) - Coded scenario support
|
||||
* [jaredledvina](https://github.com/jaredledvina/docker_factorio_server) - Contributed version updates
|
||||
* [carlbennett](https://github.com/carlbennett) - Contributed version updates and bugfixes
|
||||
|
||||
[^1]: Space Age mods can also be individually enabled by using their name separated by space.
|
||||
Example 1: Enable all by using `true`
|
||||
Example 2: Enable all by listing the mod names `space-age elevated-rails quality`
|
||||
Example 3: Enable only Elevated rails `elevated-rails`
|
||||
|
490
README_zh_CN.md
Normal file
490
README_zh_CN.md
Normal file
@ -0,0 +1,490 @@
|
||||
# Factorio [](https://hub.docker.com/r/factoriotools/factorio/) [](https://hub.docker.com/r/factoriotools/factorio/) [](https://hub.docker.com/r/factoriotools/factorio/)
|
||||
|
||||
> [!注意]
|
||||
> ARM 架构支持是实验性的。如果你尝试在 Raspberry Pi 上运行,可能会遇到崩溃和延迟问题。
|
||||
|
||||
[English](./README.md)
|
||||
|
||||
<!-- start autogeneration tags -->
|
||||
* `2`, `2.0`, `2.0.55`, `latest`, `stable`, `stable-2.0.55`
|
||||
* `2.0.54`
|
||||
* `2.0.53`
|
||||
* `2.0.52`
|
||||
* `2.0.51`
|
||||
* `2.0.50`
|
||||
* `2.0.49`
|
||||
* `2.0.48`
|
||||
* `2.0`, `2.0.47`, `stable-2.0.47`
|
||||
* `2.0.46`
|
||||
* `2.0.45`
|
||||
* `2.0.44`
|
||||
* `2.0`, `2.0.43`, `stable-2.0.43`
|
||||
* `2.0`, `2.0.42`, `stable-2.0.42`
|
||||
* `2.0`, `2.0.41`, `stable-2.0.41`
|
||||
* `2.0.40`
|
||||
* `2.0`, `2.0.39`, `stable-2.0.39`
|
||||
* `2.0.38`
|
||||
* `2.0.37`
|
||||
* `2.0.36`
|
||||
* `2.0.35`
|
||||
* `2.0.34`
|
||||
* `2.0.33`
|
||||
* `2.0`, `2.0.32`, `stable-2.0.32`
|
||||
* `2.0.31`
|
||||
* `2.0`, `2.0.30`, `stable-2.0.30`
|
||||
* `2.0.29`
|
||||
* `2.0`, `2.0.28`, `stable-2.0.28`
|
||||
* `2.0.27`
|
||||
* `2.0.26`
|
||||
* `2.0.25`
|
||||
* `2.0.24`
|
||||
* `2.0`, `2.0.23`, `stable-2.0.23`
|
||||
* `2.0.22`
|
||||
* `2.0`, `2.0.21`, `stable-2.0.21`
|
||||
* `2.0`, `2.0.20`, `stable-2.0.20`
|
||||
* `2.0.19`
|
||||
* `2.0.18`
|
||||
* `2.0.17`
|
||||
* `2.0.16`
|
||||
* `2.0`, `2.0.15`, `stable-2.0.15`
|
||||
* `2.0`, `2.0.14`, `stable-2.0.14`
|
||||
* `2.0`, `2.0.13`, `stable-2.0.13`
|
||||
* `1`, `1.1`, `1.1.110`, `stable-1.1.110`
|
||||
* `1.0`, `1.0.0`
|
||||
* `0.17`, `0.17.79`
|
||||
* `0.16`, `0.16.51`
|
||||
* `0.15`, `0.15.40`
|
||||
* `0.14`, `0.14.23`
|
||||
* `0.13`, `0.13.20`
|
||||
* `0.12`, `0.12.35`<!-- end autogeneration tags -->
|
||||
|
||||
## 标签描述
|
||||
|
||||
* `latest` - 最新版本(可能含有实验性功能)。
|
||||
* `stable` - 在 [factorio.com](https://www.factorio.com) 上声明为稳定的版本([FFF-435 自 2.0 版本起,版本首先作为实验版发布,一旦稳定就会被标记为稳定版](https://factorio.com/blog/post/fff-435))。
|
||||
* `0.x` - 某个分支上的最新版本。
|
||||
* `0.x.y` - 具体的版本。
|
||||
* `0.x-z` - 该版本的增量修复。
|
||||
|
||||
## 什么是 Factorio?
|
||||
|
||||
[Factorio](https://www.factorio.com) 是一款建造和维护工厂的游戏。
|
||||
|
||||
在游戏中,你将挖掘资源、研发科技、建设基础设施、自动化生产并与敌人战斗。发挥你的想象力来设计工厂,将简单的元素组合成巧妙的结构,运用管理技能保持其正常运转,最后保护它不受那些不太喜欢你的生物的侵害。
|
||||
|
||||
游戏非常稳定,并为建造大规模工厂进行了优化。你可以创建自己的地图,用 Lua 编写模组,或通过多人游戏与朋友一起游戏。
|
||||
|
||||
**注意**:这仅仅是服务端。完整游戏可在 [Factorio.com](https://www.factorio.com)、[Steam](https://store.steampowered.com/app/427520/)、[GOG.com](https://www.gog.com/game/factorio) 和 [Humble Bundle](https://www.humblebundle.com/store/factorio) 获得。
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 快速开始
|
||||
|
||||
运行服务端以创建必要的文件夹结构和配置文件。在这个例子中,数据存储在 `/opt/factorio`。
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /opt/factorio
|
||||
sudo chown 845:845 /opt/factorio
|
||||
sudo docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
对于 Docker 新手,这里解释一下选项:
|
||||
|
||||
* `-d` - 以守护进程方式运行("分离"模式)。
|
||||
* `-p` - 暴露端口。
|
||||
* `-v` - 将本地文件系统的 `/opt/factorio` 挂载到容器中的 `/factorio`。
|
||||
* `--restart` - 如果服务端崩溃或系统启动时重启服务端。
|
||||
* `--name` - 将容器命名为 "factorio"(否则它会有一个有趣的随机名称)。
|
||||
|
||||
需要 `chown` 命令是因为在 0.16+ 版本中,出于安全原因,我们不再以 root 身份运行游戏服务端,而是以用户 ID 为 845 的 'factorio' 用户身份运行。因此主机必须允许该用户写入这些文件。
|
||||
|
||||
检查日志以查看发生了什么:
|
||||
|
||||
```shell
|
||||
docker logs factorio
|
||||
```
|
||||
|
||||
停止服务端:
|
||||
|
||||
```shell
|
||||
docker stop factorio
|
||||
```
|
||||
|
||||
现在在 `/opt/factorio/config` 文件夹中有一个 `server-settings.json` 文件。根据你的喜好修改它并重启服务端:
|
||||
|
||||
```shell
|
||||
docker start factorio
|
||||
```
|
||||
|
||||
尝试连接到服务端。如果无法正常工作,请检查日志。
|
||||
|
||||
### 控制台
|
||||
|
||||
要向服务端发出控制台命令,请使用 `-it` 以交互模式启动服务端。使用 `docker attach` 打开控制台,然后输入命令。
|
||||
|
||||
```shell
|
||||
docker run -d -it \
|
||||
--name factorio \
|
||||
factoriotools/factorio
|
||||
docker attach factorio
|
||||
```
|
||||
|
||||
### RCON (2.0.18+)
|
||||
|
||||
或者(例如用于脚本),可以使用 RCON 连接向正在运行的 factorio 服务端发送命令。
|
||||
这不需要暴露 RCON 连接。
|
||||
|
||||
```shell
|
||||
docker exec factorio rcon /h
|
||||
```
|
||||
|
||||
### 更新
|
||||
|
||||
在升级服务端之前,请备份存档。在客户端中制作存档很容易。
|
||||
|
||||
确保在运行服务端时使用了 `-v` 参数,这样存档就在 Docker 容器外部。`docker rm` 命令会完全销毁容器,如果存档没有存储在数据卷中,也会包括存档。
|
||||
|
||||
删除容器并刷新镜像:
|
||||
|
||||
```shell
|
||||
docker stop factorio
|
||||
docker rm factorio
|
||||
docker pull factoriotools/factorio
|
||||
```
|
||||
|
||||
现在像之前一样运行服务端。大约一分钟后,新版本的 Factorio 应该就会运行起来,完整保留存档和配置!
|
||||
|
||||
### 存档
|
||||
|
||||
服务端首次启动时会生成一个名为 `_autosave1.zip` 的新地图。使用 `/opt/factorio/config` 中的 `map-gen-settings.json` 和 `map-settings.json` 文件作为地图设置。在后续运行中使用最新的存档。
|
||||
|
||||
要加载旧存档,请停止服务端并运行命令 `touch oldsave.zip`。这会重置日期。然后重启服务端。另一个选择是删除除一个存档外的所有存档。
|
||||
|
||||
要生成新地图,请停止服务端,删除所有存档并重启服务端。
|
||||
|
||||
#### 直接指定存档(0.17.79-2+)
|
||||
|
||||
你可以通过一组环境变量配置服务端来指定要加载的特定存档:
|
||||
|
||||
要加载现有存档,请将 `SAVE_NAME` 设置为位于 `saves` 目录中的现有存档文件名,不包含 `.zip` 扩展名:
|
||||
|
||||
```shell
|
||||
sudo docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
-e LOAD_LATEST_SAVE=false \
|
||||
-e SAVE_NAME=replaceme \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
要生成新地图,请设置 `GENERATE_NEW_SAVE=true` 并指定 `SAVE_NAME`:
|
||||
|
||||
```shell
|
||||
sudo docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
-e LOAD_LATEST_SAVE=false \
|
||||
-e GENERATE_NEW_SAVE=true \
|
||||
-e SAVE_NAME=replaceme \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
### Mods-模组
|
||||
|
||||
将模组复制到 mods 文件夹中并重启服务端。
|
||||
|
||||
从 0.17 版本开始,添加了一个新的环境变量 `UPDATE_MODS_ON_START`,如果设置为 `true`,将在服务端启动时更新模组。如果设置了此选项,必须提供有效的 [Factorio 用户名和令牌](https://www.factorio.com/profile),否则服务端将不会启动。它们可以设置为 docker secrets、环境变量,或从 server-settings.json 文件中获取。
|
||||
|
||||
### Scenarios-场景
|
||||
|
||||
如果你想从全新开始启动场景(而不是从保存的地图),你需要从备用入口点启动 docker 镜像。为此,请使用存储在卷中 /factorio/entrypoints 目录中的示例入口点文件,并使用以下语法启动镜像。请注意,这是正常语法,添加了 --entrypoint 设置和末尾的附加参数,这是 Scenarios 文件夹中场景的名称。
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
--entrypoint "/scenario.sh" \
|
||||
factoriotools/factorio \
|
||||
MyScenarioName
|
||||
```
|
||||
|
||||
### 将场景转换为常规地图
|
||||
|
||||
如果你想将场景导出为保存的地图,可以使用类似于上述场景用法的示例入口点。Factorio 将运行一次,将场景转换为 saves 目录中的保存地图。然后使用标准选项重启 docker 镜像将加载该地图,就像上述场景示例刚启动的场景一样。
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
--entrypoint "/scenario2map.sh" \
|
||||
factoriotools/factorio
|
||||
MyScenarioName
|
||||
```
|
||||
|
||||
### RCON
|
||||
|
||||
在 `rconpw` 文件中设置 RCON 密码。如果 `rconpw` 不存在,将生成随机密码。
|
||||
|
||||
要更改密码,请停止服务端,修改 `rconpw`,然后重启服务端。
|
||||
|
||||
要"禁用" RCON,请不要暴露端口 27015,即不使用 `-p 27015:27015/tcp` 启动服务端。RCON 仍在运行,但没有人可以连接到它。
|
||||
|
||||
|
||||
### 白名单 (0.15.3+)
|
||||
|
||||
创建文件 `config/server-whitelist.json` 并添加白名单用户。
|
||||
|
||||
```json
|
||||
[
|
||||
"you",
|
||||
"friend"
|
||||
]
|
||||
```
|
||||
|
||||
### 黑名单 (0.17.1+)
|
||||
|
||||
创建文件 `config/server-banlist.json` 并添加黑名单用户。
|
||||
|
||||
```json
|
||||
[
|
||||
"bad_person",
|
||||
"other_bad_person"
|
||||
]
|
||||
```
|
||||
|
||||
### 管理员列表 (0.17.1+)
|
||||
|
||||
创建文件 `config/server-adminlist.json` 并添加管理员用户。
|
||||
|
||||
```json
|
||||
[
|
||||
"you",
|
||||
"friend"
|
||||
]
|
||||
```
|
||||
|
||||
### 自定义配置文件 (0.17.x+)
|
||||
|
||||
开箱即用的 factorio 不支持配置文件中的环境变量。一个解决方法是使用 `envsubst`,它在启动期间从 docker-compose 中设置的环境变量动态生成配置文件:
|
||||
|
||||
替换 server-settings.json 的示例:
|
||||
|
||||
```yaml
|
||||
factorio_1:
|
||||
image: factoriotools/factorio
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
volumes:
|
||||
- /opt/factorio:/factorio
|
||||
- ./server-settings.json:/server-settings.json
|
||||
environment:
|
||||
- INSTANCE_NAME=Your Instance's Name
|
||||
- INSTANCE_DESC=Your Instance's Description
|
||||
entrypoint: /bin/sh -c "mkdir -p /factorio/config && envsubst < /server-settings.json > /factorio/config/server-settings.json && exec /docker-entrypoint.sh"
|
||||
```
|
||||
|
||||
然后 `server-settings.json` 文件可能包含这样的变量引用:
|
||||
|
||||
```json
|
||||
"name": "${INSTANCE_NAME}",
|
||||
"description": "${INSTANCE_DESC}",
|
||||
```
|
||||
|
||||
### 环境变量
|
||||
|
||||
这些是可以在容器运行时指定的环境变量。
|
||||
|
||||
| 变量名 | 描述 | 默认值 | 可用版本 |
|
||||
|---------------------|----------------------------------------------------------------|----------------|--------------|
|
||||
| GENERATE_NEW_SAVE | 如果在启动服务端之前不存在存档,则生成新存档 | false | 0.17+ |
|
||||
| LOAD_LATEST_SAVE | 为 true 时加载最新存档。否则加载 SAVE_NAME | true | 0.17+ |
|
||||
| PORT | 服务端监听的 UDP 端口 | 34197 | 0.15+ |
|
||||
| BIND | 服务端监听的 IP 地址(v4 或 v6)(IP\[:PORT]) | | 0.15+ |
|
||||
| RCON_PORT | rcon 服务端监听的 TCP 端口 | 27015 | 0.15+ |
|
||||
| SAVE_NAME | 存档文件使用的名称 | _autosave1 | 0.17+ |
|
||||
| TOKEN | factorio.com 令牌 | | 0.17+ |
|
||||
| UPDATE_MODS_ON_START| 是否在启动服务端之前更新模组 | | 0.17+ |
|
||||
| USERNAME | factorio.com 用户名 | | 0.17+ |
|
||||
| CONSOLE_LOG_LOCATION| 将控制台日志保存到指定位置 | | |
|
||||
| DLC_SPACE_AGE | 在 mod-list.json 中启用或禁用 DLC Space Age 的模组[^1] | true | 2.0.8+ |
|
||||
| MODS | 要使用的模组目录 | /factorio/mods | 2.0.8+ |
|
||||
|
||||
**注意**:所有环境变量都作为字符串进行比较
|
||||
|
||||
## 容器细节
|
||||
|
||||
理念是[保持简单](http://wiki.c2.com/?KeepItSimple)。
|
||||
|
||||
* 服务端应该自启动。
|
||||
* 优先使用配置文件而不是环境变量。
|
||||
* 使用一个数据卷。
|
||||
|
||||
### 数据卷
|
||||
|
||||
为了保持简单,容器使用挂载在 `/factorio` 的单个卷。此卷存储配置、模组和存档。
|
||||
|
||||
此卷中的文件应该由 factorio 用户拥有,uid 845。
|
||||
|
||||
```text
|
||||
factorio
|
||||
|-- config
|
||||
| |-- map-gen-settings.json
|
||||
| |-- map-settings.json
|
||||
| |-- rconpw
|
||||
| |-- server-adminlist.json
|
||||
| |-- server-banlist.json
|
||||
| |-- server-settings.json
|
||||
| `-- server-whitelist.json
|
||||
|-- mods
|
||||
| `-- fancymod.zip
|
||||
`-- saves
|
||||
`-- _autosave1.zip
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
[Docker Compose](https://docs.docker.com/compose/install/) 是运行 Docker 容器的简便方法。
|
||||
|
||||
* 需要 docker-engine >= 1.10.0
|
||||
* 需要 docker-compose >=1.6.0
|
||||
|
||||
首先获取一个 [docker-compose.yml](https://github.com/factoriotools/factorio-docker/blob/master/docker/docker-compose.yml) 文件。从此仓库获取:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/factoriotools/factorio-docker.git
|
||||
cd factorio-docker/docker
|
||||
```
|
||||
|
||||
或者创建你自己的:
|
||||
|
||||
```yaml
|
||||
version: '2'
|
||||
services:
|
||||
factorio:
|
||||
image: factoriotools/factorio
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
- "27015:27015/tcp"
|
||||
volumes:
|
||||
- /opt/factorio:/factorio
|
||||
```
|
||||
|
||||
现在 cd 到包含 docker-compose.yml 的目录并运行:
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /opt/factorio
|
||||
sudo chown 845:845 /opt/factorio
|
||||
sudo docker-compose up -d
|
||||
```
|
||||
|
||||
### 端口
|
||||
|
||||
* `34197/udp` - 游戏服务端(必需)。可以通过 `PORT` 环境变量更改。
|
||||
* `27015/tcp` - RCON(可选)。
|
||||
|
||||
## 局域网游戏
|
||||
|
||||
确保 server-settings.json 中的 `lan` 设置为 `true`。
|
||||
|
||||
```json
|
||||
"visibility":
|
||||
{
|
||||
"public": false,
|
||||
"lan": true
|
||||
},
|
||||
```
|
||||
|
||||
使用 `--network=host` 选项启动容器,以便客户端可以自动找到局域网游戏。参考快速入门来创建 `/opt/factorio` 目录。
|
||||
|
||||
```shell
|
||||
sudo docker run -d \
|
||||
--network=host \
|
||||
-p 34197:34197/udp \
|
||||
-p 27015:27015/tcp \
|
||||
-v /opt/factorio:/factorio \
|
||||
--name factorio \
|
||||
--restart=unless-stopped \
|
||||
factoriotools/factorio
|
||||
```
|
||||
|
||||
## 部署到其他平台
|
||||
|
||||
### Vagrant
|
||||
|
||||
[Vagrant](https://www.vagrantup.com/) 是设置虚拟机(VM)运行 Docker 的简便方法。[Factorio Vagrant box 仓库](https://github.com/dtandersen/factorio-lan-vagrant)包含一个示例 Vagrantfile。
|
||||
|
||||
对于局域网游戏,VM 需要内部 IP 以便客户端连接。一种方法是使用公共网络。VM 使用 DHCP 获取 IP 地址。VM 还必须转发端口 34197。
|
||||
|
||||
```ruby
|
||||
config.vm.network "public_network"
|
||||
config.vm.network "forwarded_port", guest: 34197, host: 34197
|
||||
```
|
||||
|
||||
### Amazon Web Services (AWS) 部署
|
||||
|
||||
如果你正在寻找一种简单的方法将此部署到 Amazon Web Services 云,请查看 [Factorio Server Deployment (CloudFormation) 仓库](https://github.com/m-chandler/factorio-spot-pricing)。此仓库包含一个 CloudFormation 模板,可以让你在几分钟内在 AWS 上运行起来。它可选择使用 Spot Pricing,因此服务端非常便宜,你可以在不使用时轻松关闭它。
|
||||
|
||||
## 使用反向代理
|
||||
|
||||
如果你需要使用反向代理,可以使用以下 nginx 片段:
|
||||
|
||||
```
|
||||
stream {
|
||||
server {
|
||||
listen 34197 udp reuseport;
|
||||
proxy_pass my.upstream.host:34197;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果你的 factorio 主机使用多个 IP 地址(IPv6 非常常见),你可能还需要将 Factorio 绑定到单个 IP(否则 UDP 代理可能会因 IP 不匹配而混乱)。要做到这一点,将 `BIND` 环境变量传递给容器:`docker run --network=host -e BIND=2a02:1234::5678 ...`
|
||||
|
||||
## 疑难解答
|
||||
|
||||
### 我的服务端在服务端浏览器中列出,但没有人可以连接
|
||||
|
||||
检查日志。如果有一行显示 `Own address is RIGHT IP:WRONG PORT`,那么这可能是由 Docker 代理引起的。如果 IP 和端口是正确的,可能是端口转发或防火墙问题。
|
||||
|
||||
默认情况下,Docker 通过代理路由流量。代理更改源 UDP 端口,因此检测到错误的端口。有关详细信息,请参阅论坛帖子 *[docker 托管服务端检测到错误端口](https://forums.factorio.com/viewtopic.php?f=49&t=35255)*。
|
||||
|
||||
为了修复错误端口,使用 `--userland-proxy=false` 开关启动 Docker 服务。Docker 将使用 iptables 规则而不是代理路由流量。将开关添加到 `DOCKER_OPTS` 环境变量或 Docker systemd 服务定义中的 `ExecStart`。具体情况因操作系统而异。
|
||||
|
||||
### 当我在 34197 之外的端口上运行服务端时,没有人可以从服务端浏览器连接
|
||||
|
||||
使用 `PORT` 环境变量在不同端口上启动服务端,例如 `docker run -e "PORT=34198"`。这会更改用于端口检测的数据包的源端口。`-p 34198:34197` 对于私人服务端工作正常,但服务端浏览器检测到错误的端口。
|
||||
|
||||
## 贡献者
|
||||
|
||||
* [dtandersen](https://github.com/dtandersen) - 维护者
|
||||
* [Fank](https://github.com/Fankserver) - Factorio 监视程序的程序员,保持版本更新。
|
||||
* [SuperSandro2000](https://github.com/supersandro2000) - CI 负责人,维护者和 Factorio 监视程序的运行者。贡献版本更新并编写了 Travis 脚本。
|
||||
* [DBendit](https://github.com/DBendit/docker_factorio_server) - 编写了管理员列表、禁止列表支持并贡献版本更新
|
||||
* [Zopanix](https://github.com/zopanix/docker_factorio_server) - 原作者
|
||||
* [Rfvgyhn](https://github.com/Rfvgyhn/docker-factorio) - 编写了随机生成的 RCON 密码
|
||||
* [gnomus](https://github.com/gnomus/docker_factorio_server) - 编写了白名单支持
|
||||
* [bplein](https://github.com/bplein/docker_factorio_server) - 编写了场景支持
|
||||
* [jaredledvina](https://github.com/jaredledvina/docker_factorio_server) - 贡献版本更新
|
||||
* [carlbennett](https://github.com/carlbennett) - 贡献版本更新和错误修复
|
||||
|
||||
[^1]: Space Age 模组也可以通过使用它们的名称(用空格分隔)来单独启用。
|
||||
示例 1:使用 `true` 启用所有
|
||||
示例 2:通过列出模组名称启用所有 `space-age elevated-rails quality`
|
||||
示例 3:仅启用 Elevated rails `elevated-rails`
|
157
build.py
Executable file
157
build.py
Executable file
@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import argparse
|
||||
|
||||
|
||||
PLATFORMS = [
|
||||
"linux/arm64",
|
||||
"linux/amd64",
|
||||
]
|
||||
|
||||
|
||||
def create_builder(build_dir, builder_name, platform):
|
||||
check_exists_command = ["docker", "buildx", "inspect", builder_name]
|
||||
if subprocess.run(check_exists_command, stderr=subprocess.DEVNULL).returncode != 0:
|
||||
create_command = ["docker", "buildx", "create", "--platform", platform, "--name", builder_name]
|
||||
try:
|
||||
subprocess.run(create_command, cwd=build_dir, check=True)
|
||||
except subprocess.CalledProcessError:
|
||||
print("Creating builder failed")
|
||||
exit(1)
|
||||
|
||||
|
||||
def build_and_push_multiarch(build_dir, build_args, push, builder_suffix=""):
|
||||
builder_name = f"factoriotools{builder_suffix}-multiarch"
|
||||
platform = ",".join(PLATFORMS)
|
||||
create_builder(build_dir, builder_name, platform)
|
||||
build_command = ["docker", "buildx", "build", "--platform", platform, "--builder", builder_name] + build_args
|
||||
if push:
|
||||
build_command.append("--push")
|
||||
try:
|
||||
subprocess.run(build_command, cwd=build_dir, check=True)
|
||||
except subprocess.CalledProcessError:
|
||||
print(f"Build and push of {builder_suffix or 'regular'} image failed")
|
||||
exit(1)
|
||||
|
||||
|
||||
def build_singlearch(build_dir, build_args, image_type="regular"):
|
||||
build_command = ["docker", "build"] + build_args
|
||||
try:
|
||||
subprocess.run(build_command, cwd=build_dir, check=True)
|
||||
except subprocess.CalledProcessError:
|
||||
print(f"Build of {image_type} image failed")
|
||||
exit(1)
|
||||
|
||||
|
||||
def push_singlearch(tags):
|
||||
for tag in tags:
|
||||
try:
|
||||
subprocess.run(["docker", "push", f"factoriotools/factorio:{tag}"],
|
||||
check=True)
|
||||
except subprocess.CalledProcessError:
|
||||
print("Docker push failed")
|
||||
exit(1)
|
||||
|
||||
|
||||
def build_and_push(sha256, version, tags, push, multiarch, dockerfile="Dockerfile", builder_suffix=""):
|
||||
build_dir = tempfile.mktemp()
|
||||
shutil.copytree("docker", build_dir)
|
||||
build_args = ["-f", dockerfile, "--build-arg", f"VERSION={version}", "--build-arg", f"SHA256={sha256}", "."]
|
||||
for tag in tags:
|
||||
build_args.extend(["-t", f"factoriotools/factorio:{tag}"])
|
||||
|
||||
image_type = "rootless" if "rootless" in dockerfile.lower() else "regular"
|
||||
|
||||
if multiarch:
|
||||
build_and_push_multiarch(build_dir, build_args, push, builder_suffix)
|
||||
else:
|
||||
build_singlearch(build_dir, build_args, image_type)
|
||||
if push:
|
||||
push_singlearch(tags)
|
||||
|
||||
|
||||
def login():
|
||||
try:
|
||||
username = os.environ["DOCKER_USERNAME"]
|
||||
password = os.environ["DOCKER_PASSWORD"]
|
||||
subprocess.run(["docker", "login", "-u", username, "-p", password], check=True)
|
||||
except KeyError:
|
||||
print("Username and password need to be given")
|
||||
exit(1)
|
||||
except subprocess.CalledProcessError:
|
||||
print("Docker login failed")
|
||||
exit(1)
|
||||
|
||||
|
||||
def generate_rootless_tags(original_tags):
|
||||
"""Generate rootless-specific tags from original tags"""
|
||||
return [f"{tag}-rootless" for tag in original_tags]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Build Factorio Docker images')
|
||||
parser.add_argument('--push-tags', action='store_true', help='Push images to Docker Hub')
|
||||
parser.add_argument('--multiarch', action='store_true', help='Build multi-architecture images')
|
||||
parser.add_argument('--rootless', action='store_true', help='Build only rootless images')
|
||||
parser.add_argument('--both', action='store_true', help='Build both regular and rootless images')
|
||||
parser.add_argument('--only-stable-latest', action='store_true',
|
||||
help='Build only stable and latest versions (for rootless by default)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Default behavior: build regular images unless specified otherwise
|
||||
build_regular = not args.rootless or args.both
|
||||
build_rootless = args.rootless or args.both
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "buildinfo.json")) as file_handle:
|
||||
builddata = json.load(file_handle)
|
||||
|
||||
if args.push_tags:
|
||||
login()
|
||||
|
||||
# Filter versions if needed
|
||||
versions_to_build = []
|
||||
for version, buildinfo in sorted(builddata.items(), key=lambda item: item[0], reverse=True):
|
||||
if args.only_stable_latest or (build_rootless and not build_regular):
|
||||
# For rootless-only builds, default to stable/latest only
|
||||
if "stable" in buildinfo["tags"] or "latest" in buildinfo["tags"]:
|
||||
versions_to_build.append((version, buildinfo))
|
||||
else:
|
||||
versions_to_build.append((version, buildinfo))
|
||||
|
||||
# Build regular images
|
||||
if build_regular:
|
||||
print("Building regular images...")
|
||||
for version, buildinfo in versions_to_build:
|
||||
sha256 = buildinfo["sha256"]
|
||||
tags = buildinfo["tags"]
|
||||
build_and_push(sha256, version, tags, args.push_tags, args.multiarch)
|
||||
|
||||
# Build rootless images
|
||||
if build_rootless:
|
||||
print("Building rootless images...")
|
||||
# For rootless, only build stable and latest unless building both
|
||||
rootless_versions = []
|
||||
if not build_regular or args.only_stable_latest:
|
||||
for version, buildinfo in builddata.items():
|
||||
if "stable" in buildinfo["tags"] or "latest" in buildinfo["tags"]:
|
||||
rootless_versions.append((version, buildinfo))
|
||||
else:
|
||||
rootless_versions = versions_to_build
|
||||
|
||||
for version, buildinfo in rootless_versions:
|
||||
sha256 = buildinfo["sha256"]
|
||||
original_tags = buildinfo["tags"]
|
||||
rootless_tags = generate_rootless_tags(original_tags)
|
||||
build_and_push(sha256, version, rootless_tags, args.push_tags, args.multiarch,
|
||||
dockerfile="Dockerfile.rootless", builder_suffix="-rootless")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
104
build_legacy.sh
Executable file
104
build_legacy.sh
Executable file
@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
set -eoux pipefail
|
||||
|
||||
if [[ -z ${1:-} && -n ${CI:-} ]]; then
|
||||
echo 'Usage: ./build.sh VERSION_SHORT'
|
||||
exit 1
|
||||
elif [[ ${CI:-} == true || -n ${1:-} ]]; then
|
||||
VERSION_SHORT="$1"
|
||||
else
|
||||
VERSION_SHORT=$(find . -maxdepth 1 -type d | sort | tail -1 | grep -o "[[0-9]].[[0-9]]*")
|
||||
EXTRA_TAG=latest
|
||||
fi
|
||||
|
||||
cd "$VERSION_SHORT" || exit 1
|
||||
|
||||
VERSION=$(grep -oP '[0-9]+\.[0-9]+\.[0-9]+' Dockerfile | head -1)
|
||||
DOCKER_REPO=factoriotools/factorio
|
||||
|
||||
BRANCH=${GITHUB_REF#refs/*/}
|
||||
|
||||
if [[ -n ${GITHUB_BASE_REF:-} ]]; then
|
||||
TAGS="-t $DOCKER_REPO:$GITHUB_BASE_REF"
|
||||
else
|
||||
if [[ -n ${CI:-} ]]; then
|
||||
# we are either on master or on a tag build
|
||||
if [[ ${BRANCH:-} == master || ${BRANCH:-} == "$VERSION" ]]; then
|
||||
TAGS="-t $DOCKER_REPO:$VERSION -t $DOCKER_REPO:$VERSION_SHORT"
|
||||
# we are on an incremental build of a tag
|
||||
elif [[ $VERSION == "${BRANCH%-*}" ]]; then
|
||||
TAGS="-t $DOCKER_REPO:$BRANCH -t $DOCKER_REPO:$VERSION -t $DOCKER_REPO:$VERSION_SHORT"
|
||||
# we build a other branch than master and exclude dependabot branches from tags cause the / is not supported by docker
|
||||
elif [[ -n ${BRANCH:-} && ! $BRANCH =~ "/" ]]; then
|
||||
TAGS="-t $DOCKER_REPO:$BRANCH"
|
||||
fi
|
||||
else
|
||||
# we are not in CI and tag version and version short
|
||||
TAGS="-t $DOCKER_REPO:$VERSION -t $DOCKER_REPO:$VERSION_SHORT"
|
||||
fi
|
||||
|
||||
if [[ -n ${EXTRA_TAG:-} ]]; then
|
||||
IFS=","
|
||||
for TAG in $EXTRA_TAG; do
|
||||
TAGS+=" -t $DOCKER_REPO:$TAG"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ${STABLE:-} == "$VERSION" ]]; then
|
||||
TAGS+=" -t $DOCKER_REPO:stable"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Travis gets rate limited by Docker HUB.
|
||||
if [[ ${CI:-} == true && -n ${DOCKER_PASSWORD:-} && -n ${DOCKER_USERNAME:-} ]]; then
|
||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
eval docker build . ${TAGS[@]:-}
|
||||
docker images
|
||||
|
||||
# remove -1 from incremental tag
|
||||
# eg before: 0.18.24-1, after 0.18.24
|
||||
if [[ ${BRANCH:-} ]]; then
|
||||
BRANCH_VERSION=${BRANCH%-*}
|
||||
fi
|
||||
|
||||
# only push when:
|
||||
# or we build a tag and we don't build a PR
|
||||
if [[ $VERSION == "${BRANCH_VERSION:-}" && ${GITHUB_BASE_REF:-} == "" ]] ||
|
||||
# or we are not in CI
|
||||
[[ -z ${CI:-} ]]; then
|
||||
|
||||
if [[ ${CI:-} == true ]]; then
|
||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
fi
|
||||
|
||||
# push a tag on a branch other than master except dependabot branches cause docker does not support /
|
||||
if [[ -n ${BRANCH:-} && $VERSION != "${BRANCH_VERSION:-}" && ${BRANCH:-} != "master" && ! ${BRANCH:-} =~ "/" ]]; then
|
||||
docker push "$DOCKER_REPO:$BRANCH"
|
||||
fi
|
||||
|
||||
# push an incremental tag
|
||||
# eg 0.18.24-1
|
||||
if [[ $VERSION == "${BRANCH_VERSION:-}" ]]; then
|
||||
docker push "$DOCKER_REPO:$BRANCH"
|
||||
fi
|
||||
|
||||
# only push on tags or when manually running the script
|
||||
if [[ -n ${BRANCH_VERSION:-} || -z ${CI:-} ]]; then
|
||||
docker push "$DOCKER_REPO:$VERSION"
|
||||
docker push "$DOCKER_REPO:$VERSION_SHORT"
|
||||
fi
|
||||
|
||||
if [[ -n ${EXTRA_TAG:-} ]]; then
|
||||
IFS=","
|
||||
for TAG in $EXTRA_TAG; do
|
||||
docker push "$DOCKER_REPO:$TAG"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ${STABLE:-} == "$VERSION" ]]; then
|
||||
docker push "$DOCKER_REPO:stable"
|
||||
fi
|
||||
fi
|
19
buildinfo.json
Normal file
19
buildinfo.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"2.0.55": {
|
||||
"sha256": "ef12a54d1556ae1f84ff99edc23706d13b7ad41f1c02d74ca1dfadf9448fcbae",
|
||||
"tags": [
|
||||
"stable",
|
||||
"stable-2.0.55",
|
||||
"2",
|
||||
"2.0",
|
||||
"2.0.55"
|
||||
]
|
||||
},
|
||||
"2.0.60": {
|
||||
"sha256": "69b5be1a867fd99524f9914dfee900a1ac386cf4e74c4a63768c05dc4d2b2b0b",
|
||||
"tags": [
|
||||
"latest",
|
||||
"2.0.60"
|
||||
]
|
||||
}
|
||||
}
|
38
docker-compose.yml
Normal file
38
docker-compose.yml
Normal file
@ -0,0 +1,38 @@
|
||||
version: "2"
|
||||
services:
|
||||
factorio:
|
||||
container_name: factorio
|
||||
image: factoriotools/factorio:stable
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
- "27015:27015/tcp"
|
||||
volumes:
|
||||
- ./data:/factorio
|
||||
environment:
|
||||
- UPDATE_MODS_ON_START=true
|
||||
|
||||
# Uncomment to enable autoupdate via watchtower
|
||||
#labels:
|
||||
# # Labels to allow watchtower autoupdate only if no players are online
|
||||
# - com.centurylinklabs.watchtower.enable=true
|
||||
# - com.centurylinklabs.watchtower.scope=factorio
|
||||
# - com.centurylinklabs.watchtower.lifecycle.pre-update="/players-online.sh"
|
||||
|
||||
# Uncomment the following files to use watchtower for updating the factorio container
|
||||
# Full documentation of watchtower: https://github.com/containrrr/watchtower
|
||||
#watchtower:
|
||||
# container_name: watchtower_factorio
|
||||
# image: containrrr/watchtower
|
||||
# restart: unless-stopped
|
||||
# volumes:
|
||||
# - /var/run/docker.sock:/var/run/docker.sock
|
||||
# environment:
|
||||
# # Only update containers which have the option 'watchtower.enable=true' set
|
||||
# - WATCHTOWER_TIMEOUT=30s
|
||||
# - WATCHTOWER_LABEL_ENABLE=true
|
||||
# - WATCHTOWER_POLL_INTERVAL=3600
|
||||
# - WATCHTOWER_LIFECYCLE_HOOKS=true
|
||||
# - WATCHTOWER_SCOPE=factorio
|
||||
# labels:
|
||||
# - com.centurylinklabs.watchtower.scope=factorio
|
97
docker/Dockerfile
Executable file
97
docker/Dockerfile
Executable file
@ -0,0 +1,97 @@
|
||||
# build rcon client
|
||||
FROM debian:stable-slim AS rcon-builder
|
||||
RUN apt-get -q update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install build-essential
|
||||
|
||||
WORKDIR /src
|
||||
COPY rcon/ /src
|
||||
RUN make
|
||||
|
||||
# build factorio image
|
||||
FROM debian:stable-slim
|
||||
LABEL maintainer="https://github.com/factoriotools/factorio-docker"
|
||||
|
||||
ARG USER=factorio
|
||||
ARG GROUP=factorio
|
||||
ARG PUID=845
|
||||
ARG PGID=845
|
||||
ARG BOX64_VERSION=v0.2.4
|
||||
|
||||
# optionally utilize a built-in map-gen-preset (see data/base/prototypes/map-gen-presets
|
||||
# if this is used, the preset will be used over any .json files supplied
|
||||
# vanilla factorio provides the following presets:
|
||||
# rich-resources, marathon, death-world, death-world-marathon, rail-world, ribbon-world, island
|
||||
# a modded factorio example for using this:
|
||||
# space-exploration
|
||||
ARG PRESET
|
||||
|
||||
# number of retries that curl will use when pulling the headless server tarball
|
||||
ARG CURL_RETRIES=8
|
||||
|
||||
ENV PORT=34197 \
|
||||
RCON_PORT=27015 \
|
||||
SAVES=/factorio/saves \
|
||||
PRESET="$PRESET" \
|
||||
CONFIG=/factorio/config \
|
||||
MODS=/factorio/mods \
|
||||
SCENARIOS=/factorio/scenarios \
|
||||
SCRIPTOUTPUT=/factorio/script-output \
|
||||
PUID="$PUID" \
|
||||
PGID="$PGID" \
|
||||
DLC_SPACE_AGE="true"
|
||||
|
||||
SHELL ["/bin/bash", "-eo", "pipefail", "-c"]
|
||||
|
||||
RUN apt-get -q update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install ca-certificates curl jq pwgen xz-utils procps gettext-base file --no-install-recommends \
|
||||
&& if [[ "$(uname -m)" == "aarch64" ]]; then \
|
||||
echo "installing ARM compatability layer" \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install unzip --no-install-recommends \
|
||||
&& curl -LO https://github.com/ptitSeb/box64/releases/download/${BOX64_VERSION}/box64-GENERIC_ARM-RelWithDebInfo.zip \
|
||||
&& unzip box64-GENERIC_ARM-RelWithDebInfo.zip -d /bin \
|
||||
&& rm -f box64-GENERIC_ARM-RelWithDebInfo.zip \
|
||||
&& chmod +x /bin/box64; \
|
||||
fi \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN addgroup --system --gid "$PGID" "$GROUP" \
|
||||
&& adduser --system --uid "$PUID" --gid "$PGID" --no-create-home --disabled-password --shell /bin/sh "$USER"
|
||||
|
||||
# version checksum of the archive to download
|
||||
ARG VERSION
|
||||
ARG SHA256
|
||||
|
||||
LABEL factorio.version=${VERSION}
|
||||
|
||||
ENV VERSION=${VERSION} \
|
||||
SHA256=${SHA256}
|
||||
|
||||
RUN set -ox pipefail \
|
||||
&& if [[ "${VERSION}" == "" ]]; then \
|
||||
echo "build-arg VERSION is required" \
|
||||
&& exit 1; \
|
||||
fi \
|
||||
&& if [[ "${SHA256}" == "" ]]; then \
|
||||
echo "build-arg SHA256 is required" \
|
||||
&& exit 1; \
|
||||
fi \
|
||||
&& archive="/tmp/factorio_headless_x64_$VERSION.tar.xz" \
|
||||
&& mkdir -p /opt /factorio \
|
||||
&& curl -sSL "https://www.factorio.com/get-download/$VERSION/headless/linux64" -o "$archive" --retry $CURL_RETRIES \
|
||||
&& echo "$SHA256 $archive" | sha256sum -c \
|
||||
|| (sha256sum "$archive" && file "$archive" && exit 1) \
|
||||
&& tar xf "$archive" --directory /opt \
|
||||
&& chmod ugo=rwx /opt/factorio \
|
||||
&& rm "$archive" \
|
||||
&& ln -s "$SCENARIOS" /opt/factorio/scenarios \
|
||||
&& ln -s "$SAVES" /opt/factorio/saves \
|
||||
&& mkdir -p /opt/factorio/config/ \
|
||||
&& chown -R "$USER":"$GROUP" /opt/factorio /factorio
|
||||
|
||||
COPY files/*.sh /
|
||||
COPY files/config.ini /opt/factorio/config/config.ini
|
||||
COPY --from=rcon-builder /src/rcon /bin/rcon
|
||||
|
||||
VOLUME /factorio
|
||||
EXPOSE $PORT/udp $RCON_PORT/tcp
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
91
docker/Dockerfile.rootless
Normal file
91
docker/Dockerfile.rootless
Normal file
@ -0,0 +1,91 @@
|
||||
# build rcon client
|
||||
FROM debian:stable-slim AS rcon-builder
|
||||
RUN apt-get -q update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install build-essential --no-install-recommends
|
||||
|
||||
WORKDIR /src
|
||||
COPY rcon/ /src
|
||||
RUN make
|
||||
|
||||
# build factorio image
|
||||
FROM debian:stable-slim
|
||||
LABEL maintainer="https://github.com/factoriotools/factorio-docker"
|
||||
|
||||
ARG BOX64_VERSION=v0.2.4
|
||||
|
||||
# optionally utilize a built-in map-gen-preset (see data/base/prototypes/map-gen-presets
|
||||
ARG PRESET
|
||||
|
||||
# number of retries that curl will use when pulling the headless server tarball
|
||||
ARG CURL_RETRIES=8
|
||||
|
||||
ENV PORT=34197 \
|
||||
RCON_PORT=27015 \
|
||||
SAVES=/factorio/saves \
|
||||
PRESET="$PRESET" \
|
||||
CONFIG=/factorio/config \
|
||||
MODS=/factorio/mods \
|
||||
SCENARIOS=/factorio/scenarios \
|
||||
SCRIPTOUTPUT=/factorio/script-output \
|
||||
DLC_SPACE_AGE="true"
|
||||
|
||||
SHELL ["/bin/bash", "-eo", "pipefail", "-c"]
|
||||
|
||||
RUN apt-get -q update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install ca-certificates curl jq pwgen xz-utils procps gettext-base --no-install-recommends \
|
||||
&& if [[ "$(uname -m)" == "aarch64" ]]; then \
|
||||
echo "installing ARM compatability layer" \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install unzip --no-install-recommends \
|
||||
&& curl -LO https://github.com/ptitSeb/box64/releases/download/${BOX64_VERSION}/box64-GENERIC_ARM-RelWithDebInfo.zip \
|
||||
&& unzip box64-GENERIC_ARM-RelWithDebInfo.zip -d /bin \
|
||||
&& rm -f box64-GENERIC_ARM-RelWithDebInfo.zip \
|
||||
&& chmod +x /bin/box64; \
|
||||
fi \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# version checksum of the archive to download
|
||||
ARG VERSION
|
||||
ARG SHA256
|
||||
|
||||
LABEL factorio.version=${VERSION}
|
||||
|
||||
ENV VERSION=${VERSION} \
|
||||
SHA256=${SHA256}
|
||||
|
||||
RUN set -ox pipefail \
|
||||
&& if [[ "${VERSION}" == "" ]]; then \
|
||||
echo "build-arg VERSION is required" \
|
||||
&& exit 1; \
|
||||
fi \
|
||||
&& if [[ "${SHA256}" == "" ]]; then \
|
||||
echo "build-arg SHA256 is required" \
|
||||
&& exit 1; \
|
||||
fi \
|
||||
&& archive="/tmp/factorio_headless_x64_$VERSION.tar.xz" \
|
||||
&& mkdir -p /opt /factorio \
|
||||
&& curl -sSL "https://www.factorio.com/get-download/$VERSION/headless/linux64" -o "$archive" --retry $CURL_RETRIES \
|
||||
&& echo "$SHA256 $archive" | sha256sum -c \
|
||||
|| (sha256sum "$archive" && file "$archive" && exit 1) \
|
||||
&& tar xf "$archive" --directory /opt \
|
||||
&& chmod ugo=rwx /opt/factorio \
|
||||
&& rm "$archive" \
|
||||
&& ln -s "$SCENARIOS" /opt/factorio/scenarios \
|
||||
&& ln -s "$SAVES" /opt/factorio/saves \
|
||||
&& mkdir -p /opt/factorio/config/
|
||||
|
||||
COPY files/*.sh /
|
||||
COPY files/docker-entrypoint-rootless.sh /docker-entrypoint.sh
|
||||
COPY files/config.ini /opt/factorio/config/config.ini
|
||||
COPY --from=rcon-builder /src/rcon /bin/rcon
|
||||
|
||||
# Make all scripts executable and set proper permissions for the factorio directory
|
||||
RUN chmod +x /*.sh \
|
||||
&& chmod -R 777 /opt/factorio /factorio
|
||||
|
||||
VOLUME /factorio
|
||||
EXPOSE $PORT/udp $RCON_PORT/tcp
|
||||
|
||||
# Run as non-root user (UID 1000 is common for the first user in rootless containers)
|
||||
USER 1000:1000
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
28
docker/docker-compose.yml
Normal file
28
docker/docker-compose.yml
Normal file
@ -0,0 +1,28 @@
|
||||
version: '2'
|
||||
services:
|
||||
factorio:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
# Check buildinfo.json for supported versions and SHAs
|
||||
# https://github.com/factoriotools/factorio-docker/blob/master/buildinfo.json
|
||||
- VERSION=2.0.55
|
||||
- SHA256=ef12a54d1556ae1f84ff99edc23706d13b7ad41f1c02d74ca1dfadf9448fcbae
|
||||
ports:
|
||||
- "34197:34197/udp"
|
||||
- "27015:27015/tcp"
|
||||
volumes:
|
||||
- /opt/factorio:/factorio
|
||||
# environment:
|
||||
# - PUID=1000
|
||||
# - PGID=1000
|
||||
# - UPDATE_MODS_ON_START=true
|
||||
# - USERNAME=FactorioUsername
|
||||
# - TOKEN=FactorioToken
|
||||
# - PORT=34198
|
||||
# - PRESET=deathworld
|
||||
# - ADDR=::1
|
||||
# # Uncomment the following line to enable the use of the host's network stack,
|
||||
# # which may be necessary for some setups like NAS or when using some proxy service like firewall rules.
|
||||
# extra_hosts:
|
||||
# - "host.docker.internal:host-gateway"
|
755
docker/files/config.ini
Normal file
755
docker/files/config.ini
Normal file
@ -0,0 +1,755 @@
|
||||
; version=8
|
||||
; This is INI file : https://en.wikipedia.org/wiki/INI_file#Format
|
||||
; Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
|
||||
[path]
|
||||
read-data=__PATH__executable__/../../data
|
||||
write-data=__PATH__executable__/../..
|
||||
|
||||
[general]
|
||||
locale=
|
||||
|
||||
[other]
|
||||
; Options: true, false
|
||||
; verbose-logging=false
|
||||
|
||||
; Options: true, false
|
||||
; log-saving-statistics=false
|
||||
|
||||
; autosave-interval=5
|
||||
|
||||
; autosave-slots=3
|
||||
|
||||
; In ticks
|
||||
; minimum-latency-in-multiplayer=0
|
||||
|
||||
; In seconds
|
||||
; multiplayer-initial-connection-timeout=10
|
||||
|
||||
; port=34197
|
||||
|
||||
; max-map-preview-chunk-side=64
|
||||
|
||||
; max-map-preview-threads=7
|
||||
|
||||
; In bytes
|
||||
; max-multiplayer-script-reload-size=1048576
|
||||
|
||||
; Options: true, false
|
||||
; enable-steam-networking=true
|
||||
|
||||
; proxy=
|
||||
|
||||
; proxy-username=
|
||||
|
||||
; proxy-password=
|
||||
|
||||
; Options: true, false
|
||||
; check-updates=true
|
||||
|
||||
; Options: true, false
|
||||
; enable-experimental-updates=false
|
||||
|
||||
; Options: true, false
|
||||
; enable-new-mods=true
|
||||
|
||||
; Options: true, false
|
||||
; use-mod-settings-per-save=true
|
||||
|
||||
; Options: true, false
|
||||
; disable-minimal-mode=false
|
||||
|
||||
; Options: true, false
|
||||
; disable-blueprint-storage=false
|
||||
|
||||
; Disables tracking which mod created/changed what prototype. Mainly for faster startup during development.
|
||||
;
|
||||
; Options: true, false
|
||||
; disable-prototype-history=false
|
||||
|
||||
; Print a warning for all prototype values that were not accessed.
|
||||
;
|
||||
; Options: true, false
|
||||
; check-unused-prototype-data=false
|
||||
|
||||
; Cache data stage prototype data for faster startup. Experimental.
|
||||
;
|
||||
; Options: true, false
|
||||
; cache-prototype-data=false
|
||||
|
||||
; Options: true, false
|
||||
; enable-razer-chroma-support=true
|
||||
|
||||
; Options: true, false
|
||||
; enable-logitech-led-support=true
|
||||
|
||||
; Options: true, false
|
||||
; enable-crash-log-uploading=true
|
||||
|
||||
; Options: true, false
|
||||
; enable-heap-validation=true
|
||||
|
||||
; Options: true, false
|
||||
; enable-threaded-message-pump=true
|
||||
|
||||
; Options: true, false
|
||||
; enable-taskbar-animation=true
|
||||
|
||||
; Does nothing on Windows
|
||||
;
|
||||
; Options: true, false
|
||||
; non-blocking-saving=false
|
||||
|
||||
; Related to MacOS
|
||||
;
|
||||
; Options: true, false
|
||||
; discard-mouse-events-when-accessibility-zoomed=false
|
||||
|
||||
; Options: true, false
|
||||
; enable-blueprint-storage-cloud-sync=false
|
||||
|
||||
; Options: true, false
|
||||
; force-enable-factorio-version-check=false
|
||||
|
||||
; Options: true, false
|
||||
; bring-window-to-top-on-click=true
|
||||
|
||||
; Options: fast, maximum
|
||||
; multiplayer-compression-level=fast
|
||||
|
||||
; Options: none, fast, maximum
|
||||
; autosave-compression-level=fast
|
||||
|
||||
; Socket to host RCON on when lauching MP server from the menu.
|
||||
; local-rcon-socket=0.0.0.0:0
|
||||
|
||||
; Password for RCON when launching MP server from the menu.
|
||||
; local-rcon-password=
|
||||
|
||||
|
||||
[interface]
|
||||
; Options: true, false
|
||||
; automatic-ui-scale=true
|
||||
|
||||
; custom-ui-scale=1.000000
|
||||
|
||||
; tooltip-delay=0.040000
|
||||
|
||||
; entity-tooltip-delay=0.000000
|
||||
|
||||
; tooltip-offset=20
|
||||
|
||||
; output-console-delay=1200
|
||||
|
||||
; train-stop-label-angle=0.085526
|
||||
|
||||
; active-quick-bars=2
|
||||
|
||||
; shortcut-bar-rows=2
|
||||
|
||||
; Options: true, false
|
||||
; autosort-inventory=true
|
||||
|
||||
; Options: true, false
|
||||
; research-finished-stops-game=false
|
||||
|
||||
; Options: true, false
|
||||
; use-item-groups=true
|
||||
|
||||
; Options: true, false
|
||||
; use-item-subgroups=true
|
||||
|
||||
; Options: true, false
|
||||
; use-version-filter-in-browse-games-gui=true
|
||||
|
||||
; Options: true, false
|
||||
; use-version-filter-in-install-mods-gui=true
|
||||
|
||||
; Options: true, false
|
||||
; play-sound-for-chat-messages=true
|
||||
|
||||
; Options: true, false
|
||||
; fuzzy-search-enabled=false
|
||||
|
||||
; Options: true, false
|
||||
; pick-ghost-cursor=false
|
||||
|
||||
; Options: true, false
|
||||
; show-minimap=true
|
||||
|
||||
; Options: true, false
|
||||
; show-tips-and-tricks=true
|
||||
|
||||
; Options: true, false
|
||||
; show-tutorial-notifications=true
|
||||
|
||||
; Options: true, false
|
||||
; show-turret-radius-when-blueprinting=false
|
||||
|
||||
; Options: true, false
|
||||
; show-item-labels-in-cursor=true
|
||||
|
||||
; Options: true, false
|
||||
; show-rail-block-visualization=true
|
||||
|
||||
; Options: true, false
|
||||
; show-missing-logistic-network-icon=true
|
||||
|
||||
; Options: true, false
|
||||
; show-interaction-indications=true
|
||||
|
||||
; Options: true, false
|
||||
; show-grid-when-paused=true
|
||||
|
||||
; Options: true, false
|
||||
; show-inserter-arrows-when-selected=true
|
||||
|
||||
; Options: true, false
|
||||
; show-inserter-arrows-when-detailed-info-is-on=false
|
||||
|
||||
; Options: true, false
|
||||
; show-pump-arrows-when-detailed-info-is-on=true
|
||||
|
||||
; Options: true, false
|
||||
; show-mining-drill-arrows-when-detailed-info-is-on=true
|
||||
|
||||
; Options: true, false
|
||||
; show-combinator-settings-when-detailed-info-is-on=false
|
||||
|
||||
; Options: true, false
|
||||
; entity-tooltip-on-the-side=true
|
||||
|
||||
; Options: true, false
|
||||
; show-mod-owners-in-tooltips=true
|
||||
|
||||
; Options: true, false
|
||||
; show-descriptions-in-tooltips=true
|
||||
|
||||
; Options: true, false
|
||||
; show-total-raw-in-recipe-tooltips=true
|
||||
|
||||
; debug-font-size=18
|
||||
|
||||
; train-visualization-length=5
|
||||
|
||||
|
||||
[sound]
|
||||
; master-volume=0.800000
|
||||
|
||||
; music-volume=0.500000
|
||||
|
||||
; game-effects-volume=0.700000
|
||||
|
||||
; gui-effects-volume=0.600000
|
||||
|
||||
; walking-sound-volume=0.250000
|
||||
|
||||
; environment-sounds-volume=0.550000
|
||||
|
||||
; alerts-volume=0.500000
|
||||
|
||||
; wind-volume=0.350000
|
||||
|
||||
; audible-distance=40.000000
|
||||
|
||||
; environment-audible-distance=30.000000
|
||||
|
||||
; maximum-environment-sounds=50
|
||||
|
||||
; active-gui-volume-modifier=0.800000
|
||||
|
||||
; active-gui-environment-volume-modifier=0.400000
|
||||
|
||||
; The maximum volume allowed for any sound.
|
||||
; maximum-volume=2.000000
|
||||
|
||||
; ambient-music-pause-mean-seconds=45.000000
|
||||
|
||||
; ambient-music-pause-variance-seconds=30.000000
|
||||
|
||||
; Options: main-tracks-only, interleave-main-tracks-with-interludes, randomize-all
|
||||
; ambient-music-mode=interleave-main-tracks-with-interludes
|
||||
|
||||
; zoom-audible-distance-coefficient=0.500000
|
||||
|
||||
; zoom-volume-coefficient=0.750000
|
||||
|
||||
|
||||
[map-view]
|
||||
; Options: true, false
|
||||
; show-logistic-network=false
|
||||
|
||||
; Options: true, false
|
||||
; show-electric-network=false
|
||||
|
||||
; Options: true, false
|
||||
; show-turret-range=false
|
||||
|
||||
; Options: true, false
|
||||
; show-pollution=true
|
||||
|
||||
; Options: true, false
|
||||
; show-networkless-logistic-members=false
|
||||
|
||||
; Options: true, false
|
||||
; show-train-station-names=true
|
||||
|
||||
; Options: true, false
|
||||
; show-player-names=true
|
||||
|
||||
; Options: true, false
|
||||
; show-non-standard-map-info=false
|
||||
|
||||
|
||||
[debug]
|
||||
; force=enemy
|
||||
|
||||
; Options: true, false
|
||||
; capture-perf-statistics=false
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-fps=debug
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-detailed-info=debug
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-time-usage=debug
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-gpu-time-usage=debug
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-sprite-counts=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-lua-object-statistics=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-heat-buffer-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-multiplayer-waiting-icon=debug
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-multiplayer-statistics=debug
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-multiplayer-selection-rectangles=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-debug-info-in-tooltips=debug
|
||||
|
||||
; Options: always, debug, never
|
||||
; hide-mod-guis=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-tile-grid=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-collision-rectangles=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-selection-rectangles=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-render-rectangles=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-entity-positions=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-entity-velocities=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-selected-entity-advanced-tiles=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-selected-input-transport-belts=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-paths=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-path-requests=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-next-waypoint-bb=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-target=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-unit-group-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-unit-behavior-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-pathfinder-fringe=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-path-cache=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-path-cache-paths=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-rail-paths=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-rolling-stock-count=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-rail-connections=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-rail-joints=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-rail-signal-states=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-rail-segment-collision-boxes=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-train-stop-point=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-train-braking-distance=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-train-signals=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-train-repathing=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-network-connected-entities=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-circuit-network-numbers=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-energy-sources-networks=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-active-state=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-wakeup-lists=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-transport-lines=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-transport-line-gaps=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-pollution-values=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-active-entities-on-chunk-counts=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-active-chunks=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-polluted-chunks=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; hide-chart-tags=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-enemy-expansion-candidate-chunks=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-enemy-expansion-candidate-chunk-values=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-bad-attack-chunks=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-tile-variations=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-raw-tile-transitions=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-fluid-box-fluid-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-environment-sound-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-environment-sound-area=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-selected-entity-audible-range=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-recently-played-sound-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-logistic-robot-targets=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-logistic-robots-on-map=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-recipe-icons-on-map=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-player-robots=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-fire-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-sticker-info=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-decorative-names=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-decorative-collision-rectangles=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; allow-increased-zoom=never
|
||||
|
||||
; Options: always, debug, never
|
||||
; show-chunk-components=never
|
||||
|
||||
|
||||
[multiplayer-lobby]
|
||||
; name=
|
||||
|
||||
; description=
|
||||
|
||||
; Options: true, false
|
||||
; visibility-public=true
|
||||
|
||||
; Options: true, false
|
||||
; visibility-steam=true
|
||||
|
||||
; Options: true, false
|
||||
; visibility-lan=true
|
||||
|
||||
; max-players=0
|
||||
|
||||
; Options: true, false
|
||||
; ignore-player-limit-when-returning=false
|
||||
|
||||
; max-upload-in-kilobytes-per-second=0
|
||||
|
||||
; max-upload-slots=5
|
||||
|
||||
; password=
|
||||
|
||||
; tag-list=
|
||||
|
||||
; afk-auto-kick=0
|
||||
|
||||
; Options: true, false, admins-only
|
||||
; allowed-commands=admins-only
|
||||
|
||||
; Options: true, false
|
||||
; only-admins-can-pause=true
|
||||
|
||||
; Options: true, false
|
||||
; autosave-only-on-server=true
|
||||
|
||||
; Options: true, false
|
||||
; non-blocking-saving=true
|
||||
|
||||
; Options: true, false
|
||||
; verify-user-identity=true
|
||||
|
||||
; Options: true, false
|
||||
; enable-whitelist=false
|
||||
|
||||
|
||||
[graphics]
|
||||
; lights-render-quality=0.250000
|
||||
|
||||
; Default preferred display index should force finding primary monitor
|
||||
; preferred-display-index=255
|
||||
|
||||
; screenshots-threads-count=8
|
||||
|
||||
; cache-sprite-atlas-count=1
|
||||
|
||||
; Options: true, false
|
||||
; cache-sprite-atlas=false
|
||||
|
||||
; Options: true, false
|
||||
; compress-sprite-atlas-cache=false
|
||||
|
||||
; Options: true, false
|
||||
; texture-streaming=true
|
||||
|
||||
; streamed-atlas-physical-vram-size=0
|
||||
|
||||
; sprite-vertex-buffer-size=1048576
|
||||
|
||||
; max-texture-size=0
|
||||
|
||||
; max-threads=8
|
||||
|
||||
; 'low' and 'very-low' options are deprecated and will be migrated to 'normal'
|
||||
;
|
||||
; Options: high, normal, low, very-low
|
||||
; graphics-quality=normal
|
||||
|
||||
; brightness=0
|
||||
|
||||
; contrast=0
|
||||
|
||||
; saturation=100
|
||||
|
||||
; Options: true, false
|
||||
; full-screen=true
|
||||
|
||||
; Options: true, false
|
||||
; minimize-on-focus-loss=false
|
||||
|
||||
; Options: true, false
|
||||
; show-smoke=true
|
||||
|
||||
; Options: true, false
|
||||
; show-clouds=true
|
||||
|
||||
; Options: true, false
|
||||
; show-decoratives=true
|
||||
|
||||
; Options: true, false
|
||||
; show-particles=true
|
||||
|
||||
; Options: true, false
|
||||
; show-item-shadows=true
|
||||
|
||||
; Options: true, false
|
||||
; show-inserter-shadows=true
|
||||
|
||||
; Options: true, false
|
||||
; show-animated-water=true
|
||||
|
||||
; Options: true, false
|
||||
; show-tree-distortion=true
|
||||
|
||||
; Options: true, false
|
||||
; force-opengl=false
|
||||
|
||||
; Options: true, false
|
||||
; v-sync=true
|
||||
|
||||
; Options: true, false
|
||||
; high-quality-animations=true
|
||||
|
||||
; Options: true, false
|
||||
; high-quality-shadows=false
|
||||
|
||||
; Options: true, false
|
||||
; high-quality-terrain=true
|
||||
|
||||
; Minimum number of turrets required to turn on the turret range overdraw optimization
|
||||
; turret-overdraw-minimum-count=4
|
||||
|
||||
; Scale at which the turret range overdraw optimization will start being applied
|
||||
; turret-overdraw-scale-threshold=0.200000
|
||||
|
||||
; Options: true, false
|
||||
; skip-vram-detection=false
|
||||
|
||||
; Options: true, false
|
||||
; halt-rendering-when-minimized=true
|
||||
|
||||
; Options: true, false
|
||||
; runtime-sprite-reload=false
|
||||
|
||||
; Options: true, false
|
||||
; full-color-depth=true
|
||||
|
||||
; Options: true, false
|
||||
; render-in-native-resolution=true
|
||||
|
||||
; Options: true, false
|
||||
; use-flip-presentation-model=false
|
||||
|
||||
; Options: true, false
|
||||
; debug-api=false
|
||||
|
||||
; Options: true, false
|
||||
; discard-buffers-on-begin-frame=true
|
||||
|
||||
; Options: all, high, medium, low
|
||||
; video-memory-usage=high
|
||||
|
||||
; Options: none, high-quality, low-quality
|
||||
; texture-compression-level=high-quality
|
||||
|
||||
; Options: true, false
|
||||
; compress-virtual-atlas=true
|
||||
|
||||
; Options: copy, copy-sequential, flip, flip-discard
|
||||
; dxgi-presentation-model=copy
|
||||
|
||||
; Options: none, flush, wait-for-vblank, flush-and-wait-for-vblank
|
||||
; dxgi-action-before-present=none
|
||||
|
||||
; relevant only for flip presentation models
|
||||
;
|
||||
; Options: true, false
|
||||
; dxgi-allow-tearing=false
|
||||
|
||||
; Options: false, true, auto
|
||||
; dxgi-flip-do-not-wait=false
|
||||
|
||||
; Options: true, false
|
||||
; dxgi-present-restart=false
|
||||
|
||||
; dxgi-swap-chain-buffer-count=0
|
||||
|
||||
; dxgi-max-frame-latency=0
|
||||
|
||||
; dxgi-adapter-index=-1
|
||||
|
||||
; max-sprite-loading-threads=32
|
||||
|
||||
; Options: true, false
|
||||
; gpu-accelerated-compression=true
|
||||
|
||||
; Options: true, false
|
||||
; gpu-accelerated-mipmap-compression=true
|
||||
|
||||
; Options: true, false
|
||||
; wait-until-mipmap-generation-finished=true
|
||||
|
||||
; Options: true, false
|
||||
; check-for-unused-pixels=false
|
||||
|
||||
; ogl-depth-buffer-bit-depth=0
|
||||
|
||||
; Options: false, true, auto
|
||||
; ogl-accelerated-renderer=auto
|
||||
|
||||
; Options: true, false
|
||||
; ogl-double-buffered=true
|
||||
|
||||
; Set to true if mipmapped sprites render very blurry on your GPU. Limited support.
|
||||
;
|
||||
; Options: true, false
|
||||
; legacy-gpu-no-mipmaps=false
|
||||
|
||||
; Options: true, false
|
||||
; force-linear-magnification=false
|
||||
|
||||
; Options: true, false
|
||||
; custom-mipmap-workaround=false
|
||||
|
||||
; Options: true, false
|
||||
; buffer-rename-workaround=false
|
||||
|
||||
; Comma separated list of OpenGL extensions that should not be used (for example: ARB_copy_image,KHR_debug)
|
||||
; disabled-opengl-extensions=
|
||||
|
||||
|
57
docker/files/docker-dlc.sh
Executable file
57
docker/files/docker-dlc.sh
Executable file
@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
set -eou pipefail
|
||||
|
||||
# Path to the mod-list.json file
|
||||
MOD_LIST_FILE="$MODS/mod-list.json"
|
||||
|
||||
ALL_SPACE_AGE_MODS=("elevated-rails" "quality" "space-age")
|
||||
|
||||
if [[ ! -f "$MOD_LIST_FILE" ]]; then
|
||||
# Create the mod-list.json file if it doesn't exist
|
||||
echo '{"mods":[{"name":"base","enabled":true}]}' > "$MOD_LIST_FILE"
|
||||
fi
|
||||
|
||||
enable_mod()
|
||||
{
|
||||
echo "Enable mod $1 for DLC"
|
||||
jq --arg mod_name "$1" 'if .mods | map(.name) | index($mod_name) then .mods |= map(if .name == $mod_name and .enabled == false then .enabled = true else . end) else . end' "$MOD_LIST_FILE" > "$MOD_LIST_FILE.tmp"
|
||||
mv "$MOD_LIST_FILE.tmp" "$MOD_LIST_FILE"
|
||||
}
|
||||
|
||||
disable_mod()
|
||||
{
|
||||
echo "Disable mod $1 for DLC"
|
||||
jq --arg mod_name "$1" 'if .mods | map(.name) | index($mod_name) then .mods |= map(if .name == $mod_name and .enabled == true then .enabled = false else . end) else .mods += [{"name": $mod_name, "enabled": false}] end' "$MOD_LIST_FILE" > "$MOD_LIST_FILE.tmp"
|
||||
mv "$MOD_LIST_FILE.tmp" "$MOD_LIST_FILE"
|
||||
}
|
||||
|
||||
# Enable or disable DLCs if configured
|
||||
if [[ ${DLC_SPACE_AGE:-} == "true" ]]; then
|
||||
# Define the DLC mods
|
||||
ENABLE_MODS=(${ALL_SPACE_AGE_MODS[@]})
|
||||
elif [[ ${DLC_SPACE_AGE:-} == "false" ]]; then
|
||||
# Define the DLC mods
|
||||
DISABLE_MODS=(${ALL_SPACE_AGE_MODS[@]})
|
||||
else
|
||||
ENABLE_MODS=()
|
||||
DISABLE_MODS=()
|
||||
|
||||
for SPACE_AGE_MOD in "${ALL_SPACE_AGE_MODS[@]}"; do
|
||||
REGEX="(^|\s)$SPACE_AGE_MOD($|\s)"
|
||||
if [[ "$DLC_SPACE_AGE" =~ $REGEX ]]; then
|
||||
ENABLE_MODS+=($SPACE_AGE_MOD)
|
||||
else
|
||||
DISABLE_MODS+=($SPACE_AGE_MOD)
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Iterate over each DLC mod to enable
|
||||
for MOD in "${ENABLE_MODS[@]}"; do
|
||||
enable_mod "$MOD"
|
||||
done
|
||||
|
||||
# Iterate over each DLC mod to disable
|
||||
for MOD in "${DISABLE_MODS[@]}"; do
|
||||
disable_mod "$MOD"
|
||||
done
|
124
docker/files/docker-entrypoint-rootless.sh
Executable file
124
docker/files/docker-entrypoint-rootless.sh
Executable file
@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
set -eoux pipefail
|
||||
INSTALLED_DIRECTORY=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
|
||||
FACTORIO_VOL=/factorio
|
||||
LOAD_LATEST_SAVE="${LOAD_LATEST_SAVE:-true}"
|
||||
GENERATE_NEW_SAVE="${GENERATE_NEW_SAVE:-false}"
|
||||
PRESET="${PRESET:-""}"
|
||||
SAVE_NAME="${SAVE_NAME:-""}"
|
||||
BIND="${BIND:-""}"
|
||||
CONSOLE_LOG_LOCATION="${CONSOLE_LOG_LOCATION:-""}"
|
||||
|
||||
# Create directories if they don't exist
|
||||
# In rootless mode, these should be writable by the container user
|
||||
mkdir -p "$FACTORIO_VOL"
|
||||
mkdir -p "$SAVES"
|
||||
mkdir -p "$CONFIG"
|
||||
mkdir -p "$MODS"
|
||||
mkdir -p "$SCENARIOS"
|
||||
mkdir -p "$SCRIPTOUTPUT"
|
||||
|
||||
# Generate RCON password if needed
|
||||
if [[ ! -f $CONFIG/rconpw ]]; then
|
||||
pwgen 15 1 >"$CONFIG/rconpw"
|
||||
fi
|
||||
|
||||
# Copy default configs if they don't exist
|
||||
if [[ ! -f $CONFIG/server-settings.json ]]; then
|
||||
cp /opt/factorio/data/server-settings.example.json "$CONFIG/server-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-gen-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-gen-settings.example.json "$CONFIG/map-gen-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json"
|
||||
fi
|
||||
|
||||
# Clean up incomplete saves
|
||||
NRTMPSAVES=$( find -L "$SAVES" -iname \*.tmp.zip -mindepth 1 | wc -l )
|
||||
if [[ $NRTMPSAVES -gt 0 ]]; then
|
||||
rm -f "$SAVES"/*.tmp.zip
|
||||
fi
|
||||
|
||||
# Update mods if requested
|
||||
if [[ ${UPDATE_MODS_ON_START:-} == "true" ]]; then
|
||||
"${INSTALLED_DIRECTORY}"/docker-update-mods.sh
|
||||
fi
|
||||
|
||||
# Handle DLC
|
||||
"${INSTALLED_DIRECTORY}"/docker-dlc.sh
|
||||
|
||||
# In rootless mode, we don't need to handle user switching or chown
|
||||
# The container runs as the specified user from the start
|
||||
EXEC=""
|
||||
if [[ -f /bin/box64 ]]; then
|
||||
# Use emulator for ARM hosts
|
||||
EXEC="/bin/box64"
|
||||
fi
|
||||
|
||||
# Update config path
|
||||
sed -i '/write-data=/c\write-data=\/factorio/' /opt/factorio/config/config.ini
|
||||
|
||||
# Generate new save if needed
|
||||
NRSAVES=$(find -L "$SAVES" -iname \*.zip -mindepth 1 | wc -l)
|
||||
if [[ $GENERATE_NEW_SAVE != true && $NRSAVES == 0 ]]; then
|
||||
GENERATE_NEW_SAVE=true
|
||||
SAVE_NAME=_autosave1
|
||||
fi
|
||||
|
||||
if [[ $GENERATE_NEW_SAVE == true ]]; then
|
||||
if [[ -z "$SAVE_NAME" ]]; then
|
||||
echo "If \$GENERATE_NEW_SAVE is true, you must specify \$SAVE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -f "$SAVES/$SAVE_NAME.zip" ]]; then
|
||||
echo "Map $SAVES/$SAVE_NAME.zip already exists, skipping map generation"
|
||||
else
|
||||
if [[ -n "$PRESET" ]]; then
|
||||
$EXEC /opt/factorio/bin/x64/factorio \
|
||||
--create "$SAVES/$SAVE_NAME.zip" \
|
||||
--preset "$PRESET" \
|
||||
--map-gen-settings "$CONFIG/map-gen-settings.json" \
|
||||
--map-settings "$CONFIG/map-settings.json"
|
||||
else
|
||||
$EXEC /opt/factorio/bin/x64/factorio \
|
||||
--create "$SAVES/$SAVE_NAME.zip" \
|
||||
--map-gen-settings "$CONFIG/map-gen-settings.json" \
|
||||
--map-settings "$CONFIG/map-settings.json"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build command flags
|
||||
FLAGS=(\
|
||||
--port "$PORT" \
|
||||
--server-settings "$CONFIG/server-settings.json" \
|
||||
--server-banlist "$CONFIG/server-banlist.json" \
|
||||
--rcon-port "$RCON_PORT" \
|
||||
--server-whitelist "$CONFIG/server-whitelist.json" \
|
||||
--use-server-whitelist \
|
||||
--server-adminlist "$CONFIG/server-adminlist.json" \
|
||||
--rcon-password "$(cat "$CONFIG/rconpw")" \
|
||||
--server-id /factorio/config/server-id.json \
|
||||
--mod-directory "$MODS" \
|
||||
)
|
||||
|
||||
if [ -n "$CONSOLE_LOG_LOCATION" ]; then
|
||||
FLAGS+=( --console-log "$CONSOLE_LOG_LOCATION" )
|
||||
fi
|
||||
|
||||
if [ -n "$BIND" ]; then
|
||||
FLAGS+=( --bind "$BIND" )
|
||||
fi
|
||||
|
||||
if [[ $LOAD_LATEST_SAVE == true ]]; then
|
||||
FLAGS+=( --start-server-load-latest )
|
||||
else
|
||||
FLAGS+=( --start-server "$SAVE_NAME" )
|
||||
fi
|
||||
|
||||
# Execute factorio
|
||||
# In rootless mode, we run directly without user switching
|
||||
exec $EXEC /opt/factorio/bin/x64/factorio "${FLAGS[@]}" "$@"
|
124
docker/files/docker-entrypoint.sh
Executable file
124
docker/files/docker-entrypoint.sh
Executable file
@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
set -eoux pipefail
|
||||
INSTALLED_DIRECTORY=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
|
||||
FACTORIO_VOL=/factorio
|
||||
LOAD_LATEST_SAVE="${LOAD_LATEST_SAVE:-true}"
|
||||
GENERATE_NEW_SAVE="${GENERATE_NEW_SAVE:-false}"
|
||||
PRESET="${PRESET:-""}"
|
||||
SAVE_NAME="${SAVE_NAME:-""}"
|
||||
BIND="${BIND:-""}"
|
||||
CONSOLE_LOG_LOCATION="${CONSOLE_LOG_LOCATION:-""}"
|
||||
|
||||
mkdir -p "$FACTORIO_VOL"
|
||||
mkdir -p "$SAVES"
|
||||
mkdir -p "$CONFIG"
|
||||
mkdir -p "$MODS"
|
||||
mkdir -p "$SCENARIOS"
|
||||
mkdir -p "$SCRIPTOUTPUT"
|
||||
|
||||
if [[ ! -f $CONFIG/rconpw ]]; then
|
||||
# Generate a new RCON password if none exists
|
||||
pwgen 15 1 >"$CONFIG/rconpw"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/server-settings.json ]]; then
|
||||
# Copy default settings if server-settings.json doesn't exist
|
||||
cp /opt/factorio/data/server-settings.example.json "$CONFIG/server-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-gen-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-gen-settings.example.json "$CONFIG/map-gen-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json"
|
||||
fi
|
||||
|
||||
NRTMPSAVES=$( find -L "$SAVES" -iname \*.tmp.zip -mindepth 1 | wc -l )
|
||||
if [[ $NRTMPSAVES -gt 0 ]]; then
|
||||
# Delete incomplete saves (such as after a forced exit)
|
||||
rm -f "$SAVES"/*.tmp.zip
|
||||
fi
|
||||
|
||||
if [[ ${UPDATE_MODS_ON_START:-} == "true" ]]; then
|
||||
${INSTALLED_DIRECTORY}/docker-update-mods.sh
|
||||
fi
|
||||
|
||||
${INSTALLED_DIRECTORY}/docker-dlc.sh
|
||||
|
||||
EXEC=""
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
# Update the User and Group ID based on the PUID/PGID variables
|
||||
usermod -o -u "$PUID" factorio
|
||||
groupmod -o -g "$PGID" factorio
|
||||
# Take ownership of factorio data if running as root
|
||||
chown -R factorio:factorio "$FACTORIO_VOL"
|
||||
# Drop to the factorio user
|
||||
EXEC="runuser -u factorio -g factorio --"
|
||||
fi
|
||||
if [[ -f /bin/box64 ]]; then
|
||||
# Use an emulator to run on ARM hosts
|
||||
# this only gets installed when the target docker platform is linux/arm64
|
||||
EXEC="$EXEC /bin/box64"
|
||||
fi
|
||||
|
||||
sed -i '/write-data=/c\write-data=\/factorio/' /opt/factorio/config/config.ini
|
||||
|
||||
NRSAVES=$(find -L "$SAVES" -iname \*.zip -mindepth 1 | wc -l)
|
||||
if [[ $GENERATE_NEW_SAVE != true && $NRSAVES == 0 ]]; then
|
||||
GENERATE_NEW_SAVE=true
|
||||
SAVE_NAME=_autosave1
|
||||
fi
|
||||
|
||||
if [[ $GENERATE_NEW_SAVE == true ]]; then
|
||||
if [[ -z "$SAVE_NAME" ]]; then
|
||||
echo "If \$GENERATE_NEW_SAVE is true, you must specify \$SAVE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -f "$SAVES/$SAVE_NAME.zip" ]]; then
|
||||
echo "Map $SAVES/$SAVE_NAME.zip already exists, skipping map generation"
|
||||
else
|
||||
if [[ ! -z "$PRESET" ]]; then
|
||||
$EXEC /opt/factorio/bin/x64/factorio \
|
||||
--create "$SAVES/$SAVE_NAME.zip" \
|
||||
--preset "$PRESET" \
|
||||
--map-gen-settings "$CONFIG/map-gen-settings.json" \
|
||||
--map-settings "$CONFIG/map-settings.json"
|
||||
else
|
||||
$EXEC /opt/factorio/bin/x64/factorio \
|
||||
--create "$SAVES/$SAVE_NAME.zip" \
|
||||
--map-gen-settings "$CONFIG/map-gen-settings.json" \
|
||||
--map-settings "$CONFIG/map-settings.json"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
FLAGS=(\
|
||||
--port "$PORT" \
|
||||
--server-settings "$CONFIG/server-settings.json" \
|
||||
--server-banlist "$CONFIG/server-banlist.json" \
|
||||
--rcon-port "$RCON_PORT" \
|
||||
--server-whitelist "$CONFIG/server-whitelist.json" \
|
||||
--use-server-whitelist \
|
||||
--server-adminlist "$CONFIG/server-adminlist.json" \
|
||||
--rcon-password "$(cat "$CONFIG/rconpw")" \
|
||||
--server-id /factorio/config/server-id.json \
|
||||
--mod-directory "$MODS" \
|
||||
)
|
||||
|
||||
if [ -n "$CONSOLE_LOG_LOCATION" ]; then
|
||||
FLAGS+=( --console-log "$CONSOLE_LOG_LOCATION" )
|
||||
fi
|
||||
|
||||
if [ -n "$BIND" ]; then
|
||||
FLAGS+=( --bind "$BIND" )
|
||||
fi
|
||||
|
||||
if [[ $LOAD_LATEST_SAVE == true ]]; then
|
||||
FLAGS+=( --start-server-load-latest )
|
||||
else
|
||||
FLAGS+=( --start-server "$SAVE_NAME" )
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
exec $EXEC /opt/factorio/bin/x64/factorio "${FLAGS[@]}" "$@"
|
28
docker/files/docker-update-mods.sh
Executable file
28
docker/files/docker-update-mods.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
set -eou pipefail
|
||||
|
||||
if [[ -f /run/secrets/username ]]; then
|
||||
USERNAME=$(cat /run/secrets/username)
|
||||
fi
|
||||
|
||||
if [[ -f /run/secrets/token ]]; then
|
||||
TOKEN=$(cat /run/secrets/token)
|
||||
fi
|
||||
|
||||
if [[ -z ${USERNAME:-} ]]; then
|
||||
USERNAME="$(jq -j ".username" "$CONFIG/server-settings.json")"
|
||||
fi
|
||||
|
||||
if [[ -z ${TOKEN:-} ]]; then
|
||||
TOKEN="$(jq -j ".token" "$CONFIG/server-settings.json")"
|
||||
fi
|
||||
|
||||
if [[ -z ${USERNAME:-} ]]; then
|
||||
echo "You need to provide your Factorio username to update mods."
|
||||
fi
|
||||
|
||||
if [[ -z ${TOKEN:-} ]]; then
|
||||
echo "You need to provide your Factorio token to update mods."
|
||||
fi
|
||||
|
||||
./update-mods.sh "$VERSION" "$MODS" "$USERNAME" "$TOKEN"
|
11
docker/files/players-online.sh
Executable file
11
docker/files/players-online.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
PLAYERS=$(rcon /players)
|
||||
ONLINE_COUNT=$(echo "$PLAYERS" | grep -c " (online)$")
|
||||
|
||||
if [[ "$ONLINE_COUNT" -gt "0" ]]; then
|
||||
echo "$PLAYERS"
|
||||
# exit with 75 (EX_TEMPFAIL) so watchtower skips the update
|
||||
# https://containrrr.dev/watchtower/lifecycle-hooks/
|
||||
exit 75
|
||||
fi
|
47
docker/files/scenario.sh
Executable file
47
docker/files/scenario.sh
Executable file
@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
set -eoux pipefail
|
||||
|
||||
if [[ -z ${1:-} ]]; then
|
||||
echo "No argument supplied"
|
||||
fi
|
||||
|
||||
SERVER_SCENARIO="$1"
|
||||
PRESET="${PRESET:-""}"
|
||||
|
||||
mkdir -p "$SAVES"
|
||||
mkdir -p "$CONFIG"
|
||||
mkdir -p "$MODS"
|
||||
mkdir -p "$SCENARIOS"
|
||||
|
||||
#chown -R factorio /factorio
|
||||
|
||||
if [[ ! -f $CONFIG/rconpw ]]; then
|
||||
pwgen 15 1 >"$CONFIG/rconpw"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/server-settings.json ]]; then
|
||||
cp /opt/factorio/data/server-settings.example.json "$CONFIG/server-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-gen-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-gen-settings.example.json "$CONFIG/map-gen-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json"
|
||||
fi
|
||||
|
||||
exec /opt/factorio/bin/x64/factorio \
|
||||
--port "$PORT" \
|
||||
--start-server-load-scenario "$SERVER_SCENARIO" \
|
||||
--preset "$PRESET" \
|
||||
--map-gen-settings "$CONFIG/map-gen-settings.json" \
|
||||
--map-settings "$CONFIG/map-settings.json" \
|
||||
--server-settings "$CONFIG/server-settings.json" \
|
||||
--server-banlist "$CONFIG/server-banlist.json" \
|
||||
--server-whitelist "$CONFIG/server-whitelist.json" \
|
||||
--use-server-whitelist \
|
||||
--server-adminlist "$CONFIG/server-adminlist.json" \
|
||||
--rcon-port "$RCON_PORT" \
|
||||
--rcon-password "$(cat "$CONFIG/rconpw")" \
|
||||
--server-id /factorio/config/server-id.json
|
27
docker/files/scenario2map.sh
Executable file
27
docker/files/scenario2map.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
set -eoux pipefail
|
||||
|
||||
if [[ -z ${1:-} ]]; then
|
||||
echo "No argument supplied"
|
||||
fi
|
||||
|
||||
SERVER_SCENARIO="$1"
|
||||
mkdir -p "$SAVES"
|
||||
mkdir -p "$CONFIG"
|
||||
mkdir -p "$MODS"
|
||||
mkdir -p "$SCENARIOS"
|
||||
|
||||
if [[ ! -f $CONFIG/server-settings.json ]]; then
|
||||
cp /opt/factorio/data/server-settings.example.json "$CONFIG/server-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-gen-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-gen-settings.example.json "$CONFIG/map-gen-settings.json"
|
||||
fi
|
||||
|
||||
if [[ ! -f $CONFIG/map-settings.json ]]; then
|
||||
cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json"
|
||||
fi
|
||||
|
||||
exec /opt/factorio/bin/x64/factorio \
|
||||
--scenario2map "$SERVER_SCENARIO"
|
239
docker/files/update-mods.sh
Executable file
239
docker/files/update-mods.sh
Executable file
@ -0,0 +1,239 @@
|
||||
#!/bin/bash
|
||||
set -eou pipefail
|
||||
|
||||
FACTORIO_VERSION=$1
|
||||
MOD_DIR=$2
|
||||
USERNAME=$3
|
||||
TOKEN=$4
|
||||
|
||||
MOD_BASE_URL="https://mods.factorio.com"
|
||||
|
||||
print_step()
|
||||
{
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
print_success()
|
||||
{
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
print_failure()
|
||||
{
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
# Checks if the current game version satisfies the mod's minimum required version.
|
||||
# Returns 1 if the game version is compatible with the mod, 0 if not
|
||||
check_game_version() {
|
||||
local mod_required_version="$1" # The minimum Factorio version required by the mod
|
||||
local current_game_version="$2" # The current Factorio version
|
||||
|
||||
local mod_major mod_minor game_major game_minor
|
||||
mod_major=$(echo "$mod_required_version" | cut -d '.' -f1)
|
||||
mod_minor=$(echo "$mod_required_version" | cut -d '.' -f2)
|
||||
game_major=$(echo "$current_game_version" | cut -d '.' -f1)
|
||||
game_minor=$(echo "$current_game_version" | cut -d '.' -f2)
|
||||
|
||||
# If game major version is greater than mod's required major version, it's compatible
|
||||
if [[ "$game_major" -gt "$mod_major" ]]; then
|
||||
echo 1
|
||||
return
|
||||
fi
|
||||
|
||||
# If game major version is less than mod's required major version, it's not compatible
|
||||
if [[ "$game_major" -lt "$mod_major" ]]; then
|
||||
echo 0
|
||||
return
|
||||
fi
|
||||
|
||||
# Major versions are equal, check minor versions
|
||||
# Game minor version must be >= mod's required minor version
|
||||
if [[ "$game_minor" -ge "$mod_minor" ]]; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks dependency string with provided version.
|
||||
# Only checks for operator based string, ignoring everything else
|
||||
# Returns 1 if check is ok, 0 if not
|
||||
check_dependency_version()
|
||||
{
|
||||
local dependency="$1"
|
||||
local mod_version="$2"
|
||||
|
||||
if [[ "$dependency" =~ ^(\?|!|~|\(~\)) ]]; then
|
||||
echo 1
|
||||
fi
|
||||
|
||||
local condition
|
||||
condition=$(echo "$dependency" | grep -oE '(>=|<=|>|<|=) [0-9]+(\.[0-9]+)*')
|
||||
|
||||
if [[ -z "$condition" ]]; then
|
||||
echo 1
|
||||
fi
|
||||
|
||||
local operator required_version
|
||||
operator=$(echo "$condition" | awk '{print $1}')
|
||||
required_version=$(echo "$condition" | awk '{print $2}')
|
||||
|
||||
case "$operator" in
|
||||
">=")
|
||||
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | head -n1)" == "$required_version" ]]; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
;;
|
||||
">")
|
||||
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | head -n1)" == "$required_version" && "$required_version" != "$mod_version" ]]; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
;;
|
||||
"<=")
|
||||
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | tail -n1)" == "$required_version" ]]; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
;;
|
||||
"<")
|
||||
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | tail -n1)" == "$required_version" && "$required_version" != "$mod_version" ]]; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
;;
|
||||
"=")
|
||||
if [[ "$mod_version" == "$required_version" ]]; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_mod_info()
|
||||
{
|
||||
local mod_info_json="$1"
|
||||
|
||||
# Process mod releases from newest to oldest, looking for a compatible version
|
||||
while IFS= read -r mod_release_info; do
|
||||
local mod_version mod_factorio_version
|
||||
mod_version=$(echo "$mod_release_info" | jq -r ".version")
|
||||
mod_factorio_version=$(echo "$mod_release_info" | jq -r ".info_json.factorio_version")
|
||||
|
||||
# Check if this mod version is compatible with our Factorio version
|
||||
# This prevents downloading mods that require a newer Factorio version (fixes #468)
|
||||
# and ensures backward compatibility (e.g., Factorio 2.0 can use 1.x mods) (fixes #517)
|
||||
if [[ $(check_game_version "$mod_factorio_version" "$FACTORIO_VERSION") == 0 ]]; then
|
||||
echo " Skipping mod version $mod_version because of factorio version mismatch" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
# If we found 'dependencies' element, we also check versions there
|
||||
if [[ $(echo "$mod_release_info" | jq -e '.info_json | has("dependencies") and (.dependencies | length > 0)') == true ]]; then
|
||||
while IFS= read -r dependency; do
|
||||
|
||||
# We only check for 'base' dependency
|
||||
if [[ "$dependency" == base* ]] && [[ $(check_dependency_version "$dependency" "$FACTORIO_VERSION") == 0 ]]; then
|
||||
echo " Skipping mod version $mod_version, unsatisfied base dependency: $dependency" >&2
|
||||
continue 2
|
||||
fi
|
||||
|
||||
done < <(echo "$mod_release_info" | jq -r '.info_json.dependencies[]')
|
||||
fi
|
||||
|
||||
echo "$mod_release_info" | jq -j ".file_name, \";\", .download_url, \";\", .sha1"
|
||||
break
|
||||
|
||||
done < <(echo "$mod_info_json" | jq -c ".releases|sort_by(.released_at)|reverse|.[]")
|
||||
}
|
||||
|
||||
update_mod()
|
||||
{
|
||||
MOD_NAME="$1"
|
||||
MOD_NAME_ENCODED="${1// /%20}"
|
||||
|
||||
print_step "Checking for update of mod $MOD_NAME for factorio $FACTORIO_VERSION ..."
|
||||
|
||||
MOD_INFO_URL="$MOD_BASE_URL/api/mods/$MOD_NAME_ENCODED/full"
|
||||
MOD_INFO_JSON=$(curl --silent "$MOD_INFO_URL")
|
||||
|
||||
if ! echo "$MOD_INFO_JSON" | jq -e .name >/dev/null; then
|
||||
print_success " Custom mod not on $MOD_BASE_URL, skipped."
|
||||
return 0
|
||||
fi
|
||||
|
||||
MOD_INFO=$(get_mod_info "$MOD_INFO_JSON")
|
||||
|
||||
if [[ "$MOD_INFO" == "" ]]; then
|
||||
print_failure " Not compatible with version"
|
||||
return 0
|
||||
fi
|
||||
|
||||
MOD_FILENAME=$(echo "$MOD_INFO" | cut -f1 -d";")
|
||||
MOD_URL=$(echo "$MOD_INFO" | cut -f2 -d";")
|
||||
MOD_SHA1=$(echo "$MOD_INFO" | cut -f3 -d";")
|
||||
|
||||
if [[ $MOD_FILENAME == null ]]; then
|
||||
print_failure " Not compatible with version"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -f $MOD_DIR/$MOD_FILENAME ]]; then
|
||||
print_success " Already up-to-date."
|
||||
return 0
|
||||
fi
|
||||
|
||||
print_step " Downloading $MOD_FILENAME"
|
||||
FULL_URL="$MOD_BASE_URL$MOD_URL?username=$USERNAME&token=$TOKEN"
|
||||
HTTP_STATUS=$(curl --silent -L -w "%{http_code}" -o "$MOD_DIR/$MOD_FILENAME" "$FULL_URL")
|
||||
|
||||
if [[ $HTTP_STATUS != 200 ]]; then
|
||||
print_failure " Download failed: Code $HTTP_STATUS."
|
||||
rm -f "$MOD_DIR/$MOD_FILENAME"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f $MOD_DIR/$MOD_FILENAME ]]; then
|
||||
print_failure " Downloaded file missing!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! [[ $(sha1sum "$MOD_DIR/$MOD_FILENAME") =~ $MOD_SHA1 ]]; then
|
||||
print_failure " SHA1 mismatch!"
|
||||
rm -f "$MOD_DIR/$MOD_FILENAME"
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_success " Download complete."
|
||||
|
||||
for file in "$MOD_DIR/${MOD_NAME}_"*".zip"; do # wildcard does usually not work in quotes: https://unix.stackexchange.com/a/67761
|
||||
if [[ $file != $MOD_DIR/$MOD_FILENAME ]]; then
|
||||
print_success " Deleting old version: $file"
|
||||
rm -f "$file"
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Process all enabled mods from mod-list.json, but skip built-in mods
|
||||
# The Space Age DLC includes built-in mods (elevated-rails, quality, space-age) that should not be downloaded
|
||||
if [[ -f $MOD_DIR/mod-list.json ]]; then
|
||||
jq -r ".mods|map(select(.enabled))|.[].name" "$MOD_DIR/mod-list.json" | while read -r mod; do
|
||||
# Skip base mod and DLC built-in mods
|
||||
if [[ $mod != base ]] && [[ $mod != elevated-rails ]] && [[ $mod != quality ]] && [[ $mod != space-age ]]; then
|
||||
update_mod "$mod" || true
|
||||
fi
|
||||
done
|
||||
fi
|
13
docker/rcon/Makefile
Normal file
13
docker/rcon/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
# Optimization
|
||||
OPT = -O3 -flto
|
||||
TARGET = rcon
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c17 -Wall -Wextra -pedantic $(OPT)
|
||||
REMOVE = rm -f
|
||||
|
||||
all:
|
||||
$(CC) $(CFLAGS) main.c -o $(TARGET)
|
||||
|
||||
clean:
|
||||
$(REMOVE) $(TARGET)
|
217
docker/rcon/main.c
Normal file
217
docker/rcon/main.c
Normal file
@ -0,0 +1,217 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define MIN_PACKET 10
|
||||
#define MAX_PACKET 4096
|
||||
#define MAX_BODY (MAX_PACKET - (3 * sizeof(uint32_t)) - 2)
|
||||
|
||||
#define RCON_HOST "127.0.0.1"
|
||||
|
||||
typedef enum {
|
||||
RCON_TYPE_RESPONSE = 0,
|
||||
RCON_TYPE_EXECCOMMAND = 2,
|
||||
RCON_TYPE_AUTH_RESPONSE = 2,
|
||||
RCON_TYPE_AUTH = 3,
|
||||
} packet_type;
|
||||
|
||||
typedef struct {
|
||||
uint32_t length;
|
||||
uint32_t id;
|
||||
packet_type type;
|
||||
char body[MAX_BODY];
|
||||
} packet;
|
||||
|
||||
int rcon_open(const char *port);
|
||||
void rcon_create(packet* pkt, packet_type type, const char* body);
|
||||
bool rcon_send(int rcon_socket, const packet* pkt);
|
||||
bool rcon_auth(int rcon_socket, const char* password);
|
||||
bool rcon_recv(int rcon_socket, packet* pkt, packet_type expected_type);
|
||||
char* combine_args(int argc, char* argv[]);
|
||||
char* read_password(const char* conf_dir);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "error: missing command argument\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
srand((unsigned int)time(NULL));
|
||||
|
||||
const char* port = getenv("RCON_PORT");
|
||||
if (port == NULL) {
|
||||
fprintf(stderr, "error: missing $RCON_PORT env\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const char* conf_dir = getenv("CONFIG");
|
||||
if (conf_dir == NULL) {
|
||||
fprintf(stderr, "error: missing $CONFIG env");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int rcon_socket = rcon_open(port);
|
||||
if (rcon_socket == -1) {
|
||||
fprintf(stderr, "error: could not connect\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!rcon_auth(rcon_socket, read_password(conf_dir))) {
|
||||
fprintf(stderr, "error: login failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
packet pkt;
|
||||
rcon_create(&pkt, RCON_TYPE_EXECCOMMAND, combine_args(argc, argv));
|
||||
if (!rcon_send(rcon_socket, &pkt)) {
|
||||
fprintf(stderr, "error: send command failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (rcon_recv(rcon_socket, &pkt, RCON_TYPE_RESPONSE) && pkt.length > 0) {
|
||||
puts(pkt.body);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
char* combine_args(int argc, char* argv[]) {
|
||||
// combine all cli arguments
|
||||
char* command = malloc(MAX_BODY);
|
||||
memset(command, 0, MAX_BODY);
|
||||
strcat(command, argv[1]);
|
||||
|
||||
for (int idx = 2; idx < argc; idx++) {
|
||||
strcat(command, " ");
|
||||
strcat(command, argv[idx]);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
char* read_password(const char* conf_dir) {
|
||||
char* path = malloc(strlen(conf_dir) + 64);
|
||||
strcpy(path, conf_dir);
|
||||
strcat(path, "/rconpw");
|
||||
|
||||
FILE* fptr = fopen(path, "r");
|
||||
fseek(fptr, 0, SEEK_END);
|
||||
long fsize = ftell(fptr);
|
||||
fseek(fptr, 0, SEEK_SET); /* same as rewind(f); */
|
||||
|
||||
char *password = malloc(fsize + 1);
|
||||
fread(password, fsize, 1, fptr);
|
||||
fclose(fptr);
|
||||
|
||||
password[fsize] = 0;
|
||||
if (password[fsize-1] == '\n') {
|
||||
password[fsize-1] = 0;
|
||||
}
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
int rcon_open(const char *port) {
|
||||
struct sockaddr_in address = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = htons(atoi(port))
|
||||
};
|
||||
inet_aton(RCON_HOST, &address.sin_addr);
|
||||
|
||||
int rcon_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (connect(rcon_socket, (struct sockaddr*) &address, sizeof(address)) < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return rcon_socket;
|
||||
}
|
||||
}
|
||||
|
||||
void rcon_create(packet* pkt, packet_type type, const char* body) {
|
||||
size_t body_length = strlen(body);
|
||||
if (body_length >= MAX_BODY - 2) {
|
||||
fprintf(stderr, "error: command to long");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pkt->id = abs(rand());
|
||||
pkt->type = type;
|
||||
pkt->length = (uint32_t)(sizeof(pkt->id) + sizeof(pkt->type) + body_length + 2);
|
||||
|
||||
memset(pkt->body, 0, MAX_BODY);
|
||||
strncpy(pkt->body, body, MAX_BODY);
|
||||
}
|
||||
|
||||
bool rcon_recv(int rcon_socket, packet* pkt, packet_type expected_type) {
|
||||
memset(pkt, 0, sizeof(*pkt));
|
||||
|
||||
// Read response packet length
|
||||
ssize_t expected_length_bytes = sizeof(pkt->length);
|
||||
ssize_t rx_bytes = recv(rcon_socket, &(pkt->length), expected_length_bytes, 0);
|
||||
|
||||
if (rx_bytes == -1) {
|
||||
perror("error: socket error");
|
||||
return false;
|
||||
} else if (rx_bytes == 0) {
|
||||
fprintf(stderr, "error: no data recieved\n");
|
||||
return false;
|
||||
} else if (rx_bytes < expected_length_bytes || pkt->length < MIN_PACKET || pkt->length > MAX_PACKET) {
|
||||
fprintf(stderr, "error: invalid data\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t received = 0;
|
||||
while (received < pkt->length) {
|
||||
rx_bytes = recv(rcon_socket, (char *)pkt + sizeof(pkt->length) + received, pkt->length - received, 0);
|
||||
if (rx_bytes < 0) {
|
||||
perror("error: socket error");
|
||||
return false;
|
||||
} else if (rx_bytes == 0) {
|
||||
fprintf(stderr, "error: connection lost\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
received += rx_bytes;
|
||||
}
|
||||
|
||||
return pkt->type == expected_type;
|
||||
}
|
||||
|
||||
bool rcon_send(int rcon_socket, const packet* pkt) {
|
||||
size_t length = sizeof(pkt->length) + pkt->length;
|
||||
char *ptr = (char*) pkt;
|
||||
|
||||
while (length > 0) {
|
||||
ssize_t ret = send(rcon_socket, ptr, length, 0);
|
||||
|
||||
if (ret == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr += ret;
|
||||
length -= ret;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcon_auth(int rcon_socket, const char* password) {
|
||||
packet pkt;
|
||||
rcon_create(&pkt, RCON_TYPE_AUTH, password);
|
||||
|
||||
if (!rcon_send(rcon_socket, &pkt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rcon_recv(rcon_socket, &pkt, RCON_TYPE_AUTH_RESPONSE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
2
lint.sh
Executable file
2
lint.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
git ls-files --exclude='*Dockerfile' --ignored | xargs --max-lines=1 ./hadolint
|
169
update.sh
Executable file
169
update.sh
Executable file
@ -0,0 +1,169 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
SEMVER_REGEX="^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
|
||||
|
||||
stable_online_version=$(curl 'https://factorio.com/api/latest-releases' | jq '.stable.headless' -r)
|
||||
experimental_online_version=$(curl 'https://factorio.com/api/latest-releases' | jq '.experimental.headless' -r)
|
||||
|
||||
stable_sha256=$(curl "https://factorio.com/download/sha256sums/" | grep -E "(factorio_headless_x64_|factorio-headless_linux_)${stable_online_version}.tar.xz" | awk '{print $1}')
|
||||
experimental_sha256=$(curl "https://factorio.com/download/sha256sums/" | grep -E "(factorio_headless_x64_|factorio-headless_linux_)${experimental_online_version}.tar.xz" | awk '{print $1}')
|
||||
|
||||
stable_current_version=$(jq 'with_entries(select(.value.tags | index("stable"))) | keys | .[0]' buildinfo.json -r)
|
||||
latest_current_version=$(jq 'with_entries(select(.value.tags | index("latest"))) | keys | .[0]' buildinfo.json -r)
|
||||
|
||||
echo "stable_online_version=${stable_online_version} experimental_online_version=${experimental_online_version}"
|
||||
echo "stable_current_version=${stable_current_version} latest_current_version=${latest_current_version}"
|
||||
|
||||
if [[ -z "${stable_online_version}" ]] || [[ -z "${experimental_online_version}" ]]; then
|
||||
exit
|
||||
fi
|
||||
if [[ "${stable_current_version}" == "${stable_online_version}" ]] && [[ "${latest_current_version}" == "${experimental_online_version}" ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
function get-semver(){
|
||||
local ver=$1
|
||||
local type=$2
|
||||
if [[ "$ver" =~ $SEMVER_REGEX ]]; then
|
||||
local major=${BASH_REMATCH[1]}
|
||||
local minor=${BASH_REMATCH[2]}
|
||||
local patch=${BASH_REMATCH[3]}
|
||||
fi
|
||||
case $type in
|
||||
major)
|
||||
echo "$major"
|
||||
;;
|
||||
minor)
|
||||
echo "$minor"
|
||||
;;
|
||||
patch)
|
||||
echo "$patch"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
stableOnlineVersionMajor=$(get-semver "${stable_online_version}" major)
|
||||
stableOnlineVersionMinor=$(get-semver "${stable_online_version}" minor)
|
||||
experimentalOnlineVersionMajor=$(get-semver "${experimental_online_version}" major)
|
||||
experimentalOnlineVersionMinor=$(get-semver "${experimental_online_version}" minor)
|
||||
stableCurrentVersionMajor=$(get-semver "${stable_current_version}" major)
|
||||
stableCurrentVersionMinor=$(get-semver "${stable_current_version}" minor)
|
||||
latestCurrentVersionMajor=$(get-semver "${latest_current_version}" major)
|
||||
latestCurrentVersionMinor=$(get-semver "${latest_current_version}" minor)
|
||||
|
||||
stableOnlineVersionShort=$stableOnlineVersionMajor.$stableOnlineVersionMinor
|
||||
experimentalOnlineVersionShort=$experimentalOnlineVersionMajor.$experimentalOnlineVersionMinor
|
||||
stableCurrentVersionShort=$stableCurrentVersionMajor.$stableCurrentVersionMinor
|
||||
latestCurrentVersionShort=$latestCurrentVersionMajor.$latestCurrentVersionMinor
|
||||
|
||||
echo "stableOnlineVersionShort=${stableOnlineVersionShort} experimentalOnlineVersionShort=${experimentalOnlineVersionShort}"
|
||||
echo "stableCurrentVersionShort=${stableCurrentVersionShort} latestCurrentVersionShort=${latestCurrentVersionShort}"
|
||||
|
||||
# Create new buildinfo.json with only current versions
|
||||
tmpfile=$(mktemp)
|
||||
|
||||
# Start with empty JSON object
|
||||
echo '{}' > "$tmpfile"
|
||||
|
||||
# Add stable version
|
||||
if [[ "$stable_online_version" == "$experimental_online_version" ]]; then
|
||||
# Stable and experimental are the same version
|
||||
jq --arg stable_online_version "$stable_online_version" --arg sha256 "$stable_sha256" --arg stableOnlineVersionShort "$stableOnlineVersionShort" --arg stableOnlineVersionMajor "$stableOnlineVersionMajor" \
|
||||
'. + {($stable_online_version): {sha256: $sha256, tags: ["latest", "stable", ("stable-" + $stable_online_version), $stableOnlineVersionMajor, $stableOnlineVersionShort, $stable_online_version]}}' "$tmpfile" > buildinfo.json
|
||||
else
|
||||
# Different stable and experimental versions
|
||||
# First add stable
|
||||
jq --arg stable_online_version "$stable_online_version" --arg sha256 "$stable_sha256" --arg stableOnlineVersionShort "$stableOnlineVersionShort" --arg stableOnlineVersionMajor "$stableOnlineVersionMajor" \
|
||||
'. + {($stable_online_version): {sha256: $sha256, tags: ["stable", ("stable-" + $stable_online_version), $stableOnlineVersionMajor, $stableOnlineVersionShort, $stable_online_version]}}' "$tmpfile" > buildinfo.json.tmp
|
||||
mv buildinfo.json.tmp "$tmpfile"
|
||||
|
||||
# Then add experimental
|
||||
if [[ $stableOnlineVersionShort == "$experimentalOnlineVersionShort" ]]; then
|
||||
jq --arg experimental_online_version "$experimental_online_version" --arg sha256 "$experimental_sha256" \
|
||||
'. + {($experimental_online_version): {sha256: $sha256, tags: ["latest", $experimental_online_version]}}' "$tmpfile" > buildinfo.json
|
||||
else
|
||||
jq --arg experimental_online_version "$experimental_online_version" --arg sha256 "$experimental_sha256" --arg experimentalOnlineVersionShort "$experimentalOnlineVersionShort" --arg experimentalOnlineVersionMajor "$experimentalOnlineVersionMajor" \
|
||||
'. + {($experimental_online_version): {sha256: $sha256, tags: ["latest", $experimentalOnlineVersionMajor, $experimentalOnlineVersionShort, $experimental_online_version]}}' "$tmpfile" > buildinfo.json
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f -- "$tmpfile"
|
||||
|
||||
# Generate README tags with logical sorting and de-duplication
|
||||
# First, collect all unique tags with their versions
|
||||
# Use regular arrays for bash compatibility
|
||||
declare tag_versions
|
||||
while IFS= read -r version; do
|
||||
while IFS= read -r tag; do
|
||||
# If this tag is already seen, compare versions to keep the latest
|
||||
if [[ -n "${tag_versions[$tag]}" ]]; then
|
||||
# Compare version strings - keep the higher one
|
||||
if [[ "$version" > "${tag_versions[$tag]}" ]]; then
|
||||
tag_versions[$tag]="$version"
|
||||
fi
|
||||
else
|
||||
tag_versions[$tag]="$version"
|
||||
fi
|
||||
done < <(jq -r ".\"$version\".tags[]" buildinfo.json)
|
||||
done < <(jq -r 'keys[]' buildinfo.json | sort -V -r)
|
||||
|
||||
# Build the tags list for README
|
||||
readme_tags=""
|
||||
# First add the current latest and stable tags
|
||||
latest_version=$(jq -r 'to_entries | map(select(.value.tags | contains(["latest"]))) | .[0].key' buildinfo.json)
|
||||
stable_version=$(jq -r 'to_entries | map(select(.value.tags | index("stable"))) | .[0].key' buildinfo.json)
|
||||
|
||||
if [[ -n "$latest_version" ]]; then
|
||||
latest_tags=$(jq -r ".\"$latest_version\".tags | map(select(. == \"latest\" or . == \"$latest_version\")) | join(\", \")" buildinfo.json | sed 's/"/`/g')
|
||||
readme_tags="${readme_tags}\n* \`${latest_tags}\`"
|
||||
fi
|
||||
|
||||
if [[ -n "$stable_version" ]] && [[ "$stable_version" != "$latest_version" ]]; then
|
||||
stable_tags=$(jq -r ".\"$stable_version\".tags | sort | join(\", \")" buildinfo.json | sed 's/"/`/g')
|
||||
readme_tags="${readme_tags}\n* \`${stable_tags}\`"
|
||||
fi
|
||||
|
||||
# Add major.minor tags (e.g., 2.0, 1.1) - only the latest version for each
|
||||
declare -A major_minor_seen
|
||||
while IFS= read -r version; do
|
||||
if [[ "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||
major="${BASH_REMATCH[1]}"
|
||||
minor="${BASH_REMATCH[2]}"
|
||||
major_minor="$major.$minor"
|
||||
|
||||
# Skip if this is the latest or stable version (already added above)
|
||||
if [[ "$version" == "$latest_version" ]] || [[ "$version" == "$stable_version" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Only add if we haven't seen this major.minor yet
|
||||
if [[ -z "${major_minor_seen[$major_minor]}" ]]; then
|
||||
major_minor_seen[$major_minor]=1
|
||||
tags=$(jq -r ".\"$version\".tags | join(\", \")" buildinfo.json | sed 's/"/`/g')
|
||||
if [[ -n "$tags" ]]; then
|
||||
readme_tags="${readme_tags}\n* \`${tags}\`"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done < <(jq -r 'keys[]' buildinfo.json | sort -V -r)
|
||||
|
||||
readme_tags="${readme_tags}\n"
|
||||
|
||||
perl -i -0777 -pe "s/<!-- start autogeneration tags -->.+<!-- end autogeneration tags -->/<!-- start autogeneration tags -->$readme_tags<!-- end autogeneration tags -->/s" README.md
|
||||
|
||||
# Replace VERSION and SHA256 args in docker-compose.yaml with latest stable values.
|
||||
docker_compose_path="docker/docker-compose.yml"
|
||||
sov="VERSION=${stable_online_version}" yq -i '.services.factorio.build.args[0] = env(sov)' "$docker_compose_path"
|
||||
sha="SHA256=${stable_sha256}" yq -i '.services.factorio.build.args[1] = env(sha)' "$docker_compose_path"
|
||||
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
|
||||
git add buildinfo.json
|
||||
git add README.md
|
||||
git add docker/docker-compose.yml
|
||||
git commit -a -m "Auto Update Factorio to stable version: ${stable_online_version} experimental version: ${experimental_online_version}"
|
||||
|
||||
git tag -f latest
|
||||
git push
|
||||
git push origin --tags -f
|
Reference in New Issue
Block a user