-
Notifications
You must be signed in to change notification settings - Fork 0
/
paldmin-lib.pl
458 lines (344 loc) · 8.79 KB
/
paldmin-lib.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
=head1 Paldmin Library
Functions for the Palworld Server configuration.
=cut
use WebminCore;
init_config();
# Globals
our $module_config_url = "@{[&get_webprefix()]}/config.cgi?$module_name";
our %service = (
"enabled" => 0,
"name" => undef,
"valid" => 0,
"msg" => {
"type" => undef,
"content" => undef
}
);
=head2 Validation
Validation of config, files, accessing, etc.
Returns errors
=cut
=head3 init_service()
Initializes the global service var.
Configured service in a hash:
enabled:
If service is enabled by the module config
name:
Name of the service to check
valid:
0 if unvalid, 1 if valid and service exists
msg:
type: Type for the message (info, warning, error, success,...)
content: Message for disabled/invalid, etc when parsing config
=cut
sub init_service {
# Check if enabled
if (!defined $config{"systemctl"} || length($config{"systemctl"}) <= 0) {
$service{"msg"}{"type"} = "info";
$service{"msg"}{"content"} = text("service_no_service", $module_config_url);
return;
}
$service{"enabled"} = 1;
$service{"name"} = $config{"systemctl"};
# Check if systemctl is valid
if (has_command("systemctl")) {
my $output = backquote_command("systemctl list-unit-files $service{'name'} 2>&1");
# Check if the output contains any unit files for the service
if ($output =~ /(\d+)\s+unit files listed/) {
my $num_unit_files = $1;
if ($num_unit_files > 0) {
# Success (service found)
$service{"valid"} = 1;
}
}
}
if (!$service{"valid"}) {
$service{"msg"}{"type"} = "info";
$service{"msg"}{"content"} = text("service_missing_daemon", $service{"name"} ,$module_config_url);
}
}
=head2 validate_savegame()
Checks if the server has run before.
Returns:
@[type, title, content]
or
empty list on success
=cut
sub validate_savegame() {
my ($e, $sd) = get_savedir_validation();
if ($e > 0) {
return ('warning', 'Palworld Savegame Missing', text('index_wsave_dir',"<tt>$sd</tt>"))
}
($e, $sd) = get_saveconfig_validation();
if ($e > 0) {
return ('warning', 'Palworld Savegame Config Missing', text('index_wsave_conf',"<tt>$sd</tt>"))
}
return;
}
=head2 get_savedir_validation()
Checks if palserver contains a save directory.
Returns either the save directory or undef
=cut
sub get_savedir_validation() {
my $sd = "$config{'palserver'}/Pal/Saved";
return ((!-d $sd), $sd);
}
=head2 get_saveconfig_validation()
Checks if palserver contains a config file (PalworldSettings.ini)
in the config dir inside the save directory.
Returns:
@[
>0 if error, 0 on success
checked config path
]
=cut
sub get_saveconfig_validation() {
my ($esd, $sd) = get_savedir_validation();
my $conf_world_settings = "$sd/Config/LinuxServer/PalWorldSettings.ini";
return ((!-r $conf_world_settings), $conf_world_settings);
}
=head1 Service Info
Basic Service Info about status etc
=cut
=head1 get_service_status
Retrieves the service status via systemctl and returns a hash with the info.
Returns: {
'state': 'active' | 'loaded' | 'inactive' | ...
}
=cut
sub get_service_status {
my %rv;
if (!$service{"valid"}) {
return %rv;
}
my $res = backquote_command("systemctl status ".$service{"name"});
# Extract information from the command output
if ($res =~ /Loaded: .+; (\w+); preset: \w+/) {
$rv{'atboot'} = $1;
}
if ($res =~ /Active: (\w+)/) {
$rv{'state'} = $1;
}
if ($res =~ /Active: \w+ \(\w+\)\ssince\s([^*].*)/) {
$rv{'upsince'} = $1;
}
if ($res =~ /Memory: (\d+[^\n]*)/) {
$rv{'memory'} = $1;
}
return %rv;
}
=head2 get_server_state
Returns the service state or undef if no systemctl registered
=cut
sub get_server_state {
if (!$service{"valid"}) {
return undef;
}
return (backquote_command("systemctl is-active ".$service{"name"}) =~ s/\n//r);
}
=head2 is_server_running()
Returns 1 if systemctl is running, else 0
=cut
sub is_server_running() {
return (get_server_state() eq "active") ? 1 : 0;
}
=head1 Server Control
Basic things to control the server, such as start, stop, restart, etc.
=cut
=head2 start_server()
Starts the server by the service
=cut
sub start_server() {
if (!$service{"valid"}) {
return text('missing_cmd', "@{[&get_webprefix()]}/config.cgi?$module_name");
}
my $out = backquote_logged("systemctl start $config{'systemctl'} 2>&1 </dev/null");
if ($?) {
return "<pre>$out</pre>";
}
}
=head2 stop_server()
Stop the server by the service
=cut
sub stop_server {
if (!$service{"valid"}) {
return text('missing_cmd', $module_config_url);
}
my $out = backquote_logged("systemctl stop $config{'systemctl'} 2>&1 </dev/null");
if ($?) {
return "<pre>$out</pre>";
}
}
=head2 restart_server(force_restart)
Restarts the server. If force is set, uses the stop and start routine.
Returns undef on success, else the out of the restart
=cut
sub restart_server {
my ($force_restart) = @_;
my $out;
$out = &backquote_logged("systemctl restart $config{'systemctl'} 2>&1 </dev/null") if (!$force_restart);
if ($? || $force_restart) {
stop_server();
$out = start_server();
}
return $? ? $out : undef;
}
=head1 Palworld Config
Basic stuff to configure the palworld server
=cut
=head2 get_setting(settings)
Returns the configured setting for a field.
Returns undef on error
=cut
sub get_setting {
my ($setting) = @_;
my %settings = get_world_settings();
return $settings{$setting};
}
=head2 get_world_settings()
Retrieves the default world settings and the save world settings.
Combines them by replacing the default world settings with save world settings if applicable.
Returns undefined on error (if either setting doesn't exist).
=cut
sub get_world_settings() {
my %def = get_default_world_settings();
if (!%def) {
return undef;
}
my %save = get_save_world_settings();
if (!%save) {
return %def;
}
foreach my $savek (keys %save) {
$def{$savek} = $save{$savek};
}
return %def;
}
=head2 get_default_world_settings()
Returns the default world settings from the ini file.
=cut
sub get_default_world_settings() {
return settings_file_read("$config{'palserver'}/DefaultPalWorldSettings.ini");
}
=head2 get_save_world_settings()
Returns the world settings from the ini file.
=cut
sub get_save_world_settings() {
my ($e, $ini) = get_saveconfig_validation();
if ($e > 0) {
return undef;
}
return settings_file_read($ini);
}
=head2 settings_file_read(file)
Reads a given settings file.
Returns undefined on error, else returns the settings as a 2D Array.
Returns:
[
[Key, Value]
]
=cut
sub settings_file_read {
my ($ini) = @_;
if (!-r $ini) {
return undef;
}
my %rv;
open_readfile(INI, $ini) || return undef;
while(<INI>) {
my $option_settings_line = $_;
if ($option_settings_line =~ /OptionSettings=\((.*?)\)/) {
my $options_str = $1;
my @pairs = split /,/, $options_str;
foreach my $pair (@pairs) {
my ($key, $value) = split /=/, $pair;
$rv{$key} = $value;
}
}
}
close(INI);
return %rv;
}
=head2 get_banned_steamids()
Tries to read the banlist.txt.
If the file doesn't exist, returns an empty array;
=cut
sub get_banned_steamids() {
my @rv;
my ($esd, $sd) = get_savedir_validation();
my $file = $sd."/SaveGames/banlist.txt";
open_readfile(BANS, $file) || return @rv;
while(<BANS>) {
chomp;
if (/steam_(\d+)/) {
push @rv, $1;
}
}
close(BANS);
return @rv;
}
=head2 add_steamid_to_banlist(steamid)
Takes a steamid with the prefix "steamid_XXXX".
Checks if the banlist.txt exists, if not creates it.
Returns:
a string with error
undef on success
=cut
sub add_steamid_to_banlist {
my ($sId) = @_;
my ($e, $sd) = get_savedir_validation();
if ($e > 0) {
return text('banlist_eadd_missdir', $sd);
}
my $file = $sd."/SaveGames/banlist.txt";
lock_file($file);
my $lref = read_file_lines($file);
push(@$lref, $sId);
flush_file_lines($file);
unlock_file($file);
return undef;
}
=head2 remove_steamids_from_banlist(@steamids)
Takes an array of steamids with the prefix "steam_"
and removes them from the banlist.txt
=cut
sub remove_steamids_from_banlist {
my ($ids) = @_;
my ($e, $sd) = get_savedir_validation();
if ($e > 0) {
return text('banlist_eadd_missdir', $sd);
}
my $file = $sd."/SaveGames/banlist.txt";
my @splices = ();
lock_file($file);
my $lref = read_file_lines($file);
# Find all line indexes with matching id
for my $id (@$ids) {
for my $i (0 .. $#$lref) {
my $line = @$lref[$i];
if (index($id, $line) >= 0) {
push @splices, $i;
last; # Break the loop once the ID is found
}
}
}
for my $splice (reverse @splices) {
splice(@$lref, $splice, 1);
}
flush_file_lines($file);
unlock_file($file);
return undef;
}
=head1 Utility
Simple Utility functions
=cut
=head1 get_files_in_dir(dir)
=cut
sub get_files_in_dir {
my ($dir) = @_;
opendir(DIR, $dir);
local @rv = grep { $_ ne "." && $_ ne ".." } readdir(DIR);
closedir(DIR);
return @rv;
}
1;