Skip to content

Commit 6e9b595

Browse files
committed
chore: convert rust server to smart deployment
1 parent d08184f commit 6e9b595

7 files changed

Lines changed: 452 additions & 6 deletions

File tree

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ jobs:
115115
druid registry push ./scrolls/minecraft/forge/1.21.6 -p main=25565 -p rcon=25575 -i artifacts.druid.gg/druid-team/druidd-java:21 --min-disk 3Gi --min-ram 512Mi --min-cpu 0.25 --smart
116116
druid registry push ./scrolls/minecraft/forge/1.21.7 -p main=25565 -p rcon=25575 -i artifacts.druid.gg/druid-team/druidd-java:21 --min-disk 3Gi --min-ram 512Mi --min-cpu 0.25 --smart
117117
druid registry push ./scrolls/minecraft/cuberite/latest -p main=25565 -p webpanel=8080 -i artifacts.druid.gg/druid-team/druidd-ubuntu --min-disk 3Gi --min-ram 512Mi --min-cpu 0.25 --smart
118-
druid registry push ./scrolls/rust/rust-oxide/latest -p main=28015/udp -p rcon -p rustplus -i artifacts.druid.gg/druid-team/druidd-steamcmd --min-disk 10Gi --min-ram 6Gi --min-cpu 1
119-
druid registry push ./scrolls/rust/rust-vanilla/latest -p main=28015/udp -p rcon -p rustplus -i artifacts.druid.gg/druid-team/druidd-steamcmd --min-disk 10Gi --min-ram 6Gi --min-cpu 1
118+
druid registry push ./scrolls/rust/rust-oxide/latest -p main=/udp -p query=/udp -p rcon -p rustplus -i artifacts.druid.gg/druid-team/druidd-steamcmd --min-disk 10Gi --min-ram 6Gi --min-cpu 1 --smart
119+
druid registry push ./scrolls/rust/rust-vanilla/latest -p main=/udp -p query=/udp -p rcon -p rustplus -i artifacts.druid.gg/druid-team/druidd-steamcmd --min-disk 10Gi --min-ram 6Gi --min-cpu 1 --smart
120120
druid registry push ./scrolls/lgsm/pwserver -p main=8211/udp -p rcon=25575 -i artifacts.druid.gg/druid-team/druidd-lgsm:pw -m --min-disk 7Gi --min-ram 2Gi --min-cpu 0.5 --smart
121121
druid registry push ./scrolls/lgsm/arkserver -p main=/udp -p query=/udp -p rcon -i artifacts.druid.gg/druid-team/druidd-lgsm:ark -m --min-disk 25Gi --min-ram 7Gi --min-cpu 0.5 --smart
122122
druid registry push ./scrolls/lgsm/untserver -p main=27015/udp -p mainv6=27016 -i artifacts.druid.gg/druid-team/druidd-lgsm:unt -m --min-disk 7Gi --min-ram 1Gi --min-cpu 0.5

scrolls/rust/rust-oxide/latest/init-files/start.sh.scroll_template

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)
55
-app.port $DRUID_PORT_RUSTPLUS_1 \
66
-app.publicip $DRUID_IP_1 \
77
-server.ip "0.0.0.0" \
8-
-server.port 28015 \
8+
-server.port $DRUID_PORT_MAIN_1 \
9+
-server.queryport $DRUID_PORT_QUERY_1 \
910
-rcon.ip "0.0.0.0" \
1011
-rcon.port {{ .Config.rcon_web_rust.port }} \
1112
-rcon.password {{ .Config.rcon_web_rust.password }} \
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
function string.fromhex(str)
2+
return (str:gsub('..', function(cc)
3+
return string.char(tonumber(cc, 16))
4+
end))
5+
end
6+
7+
function string.tohex(str)
8+
return (str:gsub('.', function(c)
9+
return string.format('%02X', string.byte(c))
10+
end))
11+
end
12+
13+
function handle(ctx, data)
14+
15+
-- prtocol begins with FFFFFFFF and the packedid
16+
17+
-- get packet index
18+
19+
-- check if start with FFFFFFFF
20+
21+
hex = string.tohex(data)
22+
23+
if string.sub(hex, 1, 8) ~= "FFFFFFFF" then
24+
debug_print("Invalid Packet " .. hex)
25+
return
26+
end
27+
28+
packetId = string.sub(hex, 9, 10)
29+
30+
payload = string.sub(hex, 11)
31+
32+
-- check if packet is 54
33+
34+
debug_print("Packet ID: " .. packetId)
35+
36+
if packetId == "55" then
37+
38+
if payload == "FFFFFFFF" or payload == "00000000" then
39+
debug_print("Received Packet: " .. hex)
40+
resHex = string.fromhex("FFFFFFFF414BA1D522") -- this is not good, as we allways pass the same key for the challenge
41+
ctx.sendData(resHex)
42+
return
43+
end
44+
45+
if payload == "4BA1D522" then
46+
debug_print("Received Packet: " .. hex)
47+
resHex = string.fromhex("FFFFFFFF4400") -- this is not good to be hardcoded, but fine for now
48+
49+
ctx.sendData(resHex)
50+
return
51+
end
52+
debug_print("Bad challenge: " .. hex)
53+
return
54+
end
55+
56+
if packetId == "56" then
57+
58+
if payload == "FFFFFFFF" or payload == "00000000" then
59+
debug_print("Received Packet: " .. hex)
60+
resHex = string.fromhex("FFFFFFFF414BA1D522") -- this is not good, as we allways pass the same key for the challenge
61+
ctx.sendData(resHex)
62+
return
63+
end
64+
65+
if payload == "4BA1D522" then
66+
debug_print("Received Packet: " .. hex)
67+
resHex = string.fromhex(
68+
"FFFFFFFF451A00414C4C4F57444F574E4C4F414443484152535F69003100414C4C4F57444F574E4C4F41444954454D535F69003100436C757374657249645F73004B4150323032326E76637738393233386E3332726677653900435553544F4D5345525645524E414D455F73006B617020707670202F20342D6D616E202F2078352D783235202F20776F726B65727320667269656E646C79207365727665720044617954696D655F730037360047616D654D6F64655F73005465737447616D654D6F64655F43004841534143544956454D4F44535F690031004C45474143595F690030004D4154434854494D454F55545F66003132302E303030303030004D4F44305F7300323839373838353837383A4544393730443545343845324143433334333545374339373345434135373637004D4F44315F7300323536343534363435353A3934413336414236343933453241443335364631343142313932383633453445004D4F44325F7300333034363539363536343A3832453245393730343446444139463642464237353439443730433337423133004D4F44335F7300313939393434373137323A3836453432424644343646453430363338443639344141384342453634344134004D6F6449645F6C0030004E6574776F726B696E675F690030004E554D4F50454E505542434F4E4E003530004F4646494349414C5345525645525F690030004F574E494E474944003930323032313035363131373133353337004F574E494E474E414D45003930323032313035363131373133353337005032504144445200393032303231303536313137313335333700503250504F52540037373837005345415243484B4559574F5244535F7300437573746F6D0053657276657250617373776F72645F620066616C73650053455256455255534553424154544C4559455F6200747275650053455353494F4E464C41475300313730370053455353494F4E49535056455F69003000") -- this is not good to be hardcoded, but fine for now
69+
70+
ctx.sendData(resHex)
71+
return
72+
end
73+
debug_print("Bad challenge: " .. hex)
74+
return
75+
end
76+
77+
if packetId == "54" then
78+
79+
80+
81+
local snapshotMode = get_snapshot_mode()
82+
local snapshotPercentage = get_snapshot_percentage()
83+
84+
85+
queue = get_queue()
86+
name = get_var("ServerListName") or "Coldstarter is cool (server is idle, join to start)"
87+
88+
map = get_var("MapName") or "server idle"
89+
90+
local finishSec = get_finish_sec()
91+
92+
if finishSec ~= nil then
93+
finishSec = math.ceil(finishSec)
94+
end
95+
96+
if snapshotMode ~= "noop" then
97+
if snapshotMode == "restore" then
98+
if snapshotPercentage == nil or snapshotPercentage == 100 then
99+
name = get_var("ServerListNameRestoring") or "EXTRACTING snapshot, this might take a moment"
100+
map = get_var("MapNameRestoring") or "extracting snapshot"
101+
else
102+
name = get_var("ServerListNameRestoring") or "DOWNLOADING snapshot - " .. string.format("%.2f", snapshotPercentage) .. "%"
103+
map = get_var("MapNameRestoring") or "downloading snapshot"
104+
end
105+
else
106+
if snapshotPercentage == nil or snapshotPercentage == 100 then
107+
name = get_var("ServerListNameBackingUp") or "BACKING UP, this might take a moment"
108+
else
109+
name = get_var("ServerListNameBackingUp") or "BACKING UP - " .. string.format("%.2f", snapshotPercentage) .. "%"
110+
end
111+
map = get_var("MapNameBackingUp") or "backing up server"
112+
end
113+
elseif queue ~= nil and queue["install"] == "running" then
114+
if finishSec ~= nil then
115+
-- finish sec is not necissary applicable, but it's better to show something I guess
116+
name = get_var("ServerListNameInstalling") or
117+
string.format("INSTALLING, this might take a moment - %ds", finishSec)
118+
else
119+
name = get_var("ServerListNameInstalling") or "INSTALLING, this might take a moment"
120+
end
121+
122+
map = get_var("MapNameInstalling") or "installing server"
123+
elseif finishSec ~= nil then
124+
nameTemplate = get_var("ServerListNameStarting") or "Druid Gameserver (starting) - %ds"
125+
name = string.format(nameTemplate, finishSec)
126+
end
127+
128+
folder = get_var("GameSteamFolder") or "ark_survival_evolved"
129+
130+
gameName = get_var("GameName") or "ARK: Survival Evolved"
131+
132+
steamIdString = get_var("GameSteamId") or "0"
133+
134+
steamId = tonumber(steamIdString)
135+
136+
serverPort = get_port("main")
137+
138+
-- hex
139+
nameHex = string.tohex(name)
140+
141+
mapHex = string.tohex(map)
142+
143+
folderHex = string.tohex(folder) -- ark: ark_survival_evolved
144+
145+
steamIdHex = number_to_little_endian_short(steamId)
146+
147+
gameHex = string.tohex(gameName)
148+
149+
maxPlayerHex = "00"
150+
playerHex = "00"
151+
botHex = "00"
152+
153+
serverTypeHex = "64" -- dedicated
154+
155+
osHex = "6C" -- l (6C) for linux, w (77) for windows
156+
157+
visibility = "00" -- 01 for private, 00 for public
158+
159+
version = string.tohex("1.0.0.0")
160+
161+
-- EDF & 0x80: Port
162+
-- EDF & 0x10: SteamID
163+
-- EDF & 0x20 Keywords
164+
-- EDF & 0x01 GameID
165+
166+
edfFlagHex = "B1"
167+
168+
-- short as hex
169+
gamePortHex = number_to_little_endian_short(serverPort)
170+
171+
steamId = "01D075C44C764001"
172+
173+
tags =
174+
",OWNINGID:90202064633057281,OWNINGNAME:90202064633057281,NUMOPENPUBCONN:50,P2PADDR:90202064633057281,P2PPORT:" ..
175+
serverPort .. ",LEGACY_i:0"
176+
177+
tagsHex = string.tohex(tags)
178+
179+
edfHex = gamePortHex .. steamId .. tagsHex .. "00" .. "FE47050000000000"
180+
181+
res = "FFFFFFFF4911" .. nameHex .."00" .. mapHex .."00".. folderHex .."00" .. gameHex .."00" .. steamIdHex .. playerHex .. maxPlayerHex .. botHex .. serverTypeHex ..
182+
osHex .. visibility .. version .."00" .. edfFlagHex .. edfHex
183+
184+
resHex = string.fromhex(res)
185+
186+
ctx.sendData(resHex)
187+
return
188+
end
189+
190+
debug_print("Unknown Packet: " .. hex)
191+
192+
end
193+
194+
function number_to_little_endian_short(num)
195+
-- Ensure the number is in the 16-bit range for unsigned short
196+
if num < 0 or num > 65535 then
197+
error("Number " .. num .. " out of range for 16-bit unsigned short")
198+
end
199+
200+
-- Convert the number to two bytes in little-endian format
201+
local low_byte = num % 256 -- Least significant byte
202+
local high_byte = math.floor(num / 256) % 256 -- Most significant byte
203+
204+
-- Format as hexadecimal string
205+
return string.format("%02X%02X", low_byte, high_byte)
206+
end

scrolls/rust/rust-oxide/latest/scroll.yaml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,23 @@ app_version: latest
55
ports:
66
- name: main
77
protocol: udp
8-
port: 28015
8+
sleep_handler: generic
9+
- name: query
10+
protocol: udp
11+
description: Steam Query Port. Use this to connect via the Steam client.
12+
finish_after_command: install
13+
sleep_handler: packet_handler/query.lua
14+
vars:
15+
- name: GameName
16+
value: "Rust"
17+
- name: GameSteamFolder
18+
value: rust
19+
- name: GameSteamId
20+
value: "0"
21+
- name: MapName
22+
value: Procedual Map
23+
- name: ServerListName
24+
value: "Druid Gameserver (idle) - Start server by joining"
925
- name: rustplus
1026
protocol: tcp
1127
- name: rcon

scrolls/rust/rust-vanilla/latest/init-files/start.sh.scroll_template

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)
55
-app.port $DRUID_PORT_RUSTPLUS_1 \
66
-app.publicip $DRUID_IP_1 \
77
-server.ip "0.0.0.0" \
8-
-server.port 28015 \
8+
-server.port $DRUID_PORT_MAIN_1 \
9+
-server.queryport $DRUID_PORT_QUERY_1 \
910
-rcon.ip "0.0.0.0" \
1011
-rcon.port {{ .Config.rcon_web_rust.port }} \
1112
-rcon.password {{ .Config.rcon_web_rust.password }} \

0 commit comments

Comments
 (0)