-
Notifications
You must be signed in to change notification settings - Fork 16
/
spadsPluginApiDoc.html
1696 lines (1055 loc) · 97.4 KB
/
spadsPluginApiDoc.html
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
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SPADS Plugin API Doc</title>
<link rel="stylesheet" href="pod2html.css" type="text/css" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rev="made" href="mailto:" />
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/anchor-js/anchor.min.js"></script>
<ul id="index">
<li><a href="#NAME">NAME</a></li>
<li><a href="#SYNOPSIS">SYNOPSIS</a></li>
<li><a href="#DESCRIPTION">DESCRIPTION</a></li>
<li><a href="#PYTHON-SPECIFIC-NOTES">PYTHON SPECIFIC NOTES</a>
<ul>
<li><a href="#How-to-read-the-documentation">How to read the documentation</a></li>
<li><a href="#Perl-strings-conversions">Perl strings conversions</a></li>
<li><a href="#Windows-limitations">Windows limitations</a></li>
</ul>
</li>
<li><a href="#CALLBACK-FUNCTIONS">CALLBACK FUNCTIONS</a>
<ul>
<li><a href="#Mandatory-callbacks">Mandatory callbacks</a></li>
<li><a href="#Configuration-callback">Configuration callback</a></li>
<li><a href="#Dependencies-callback">Dependencies callback</a></li>
<li><a href="#Event-based-callbacks">Event-based callbacks</a></li>
<li><a href="#Customization-callbacks">Customization callbacks</a></li>
<li><a href="#Event-loop-callback">Event loop callback</a></li>
</ul>
</li>
<li><a href="#API-FUNCTIONS">API FUNCTIONS</a>
<ul>
<li><a href="#Accessors">Accessors</a></li>
<li><a href="#Plugin-management">Plugin management</a></li>
<li><a href="#Handlers-management">Handlers management</a></li>
<li><a href="#SPADS-operations">SPADS operations</a></li>
<li><a href="#AutoHost-messaging-system">AutoHost messaging system</a></li>
<li><a href="#Time-utils">Time utils</a></li>
<li><a href="#Data-formatting">Data formatting</a></li>
<li><a href="#Forking-processes">Forking processes</a></li>
<li><a href="#Timers-management">Timers management</a></li>
<li><a href="#Sockets-management">Sockets management</a></li>
<li><a href="#Python-specific">Python specific</a></li>
</ul>
</li>
<li><a href="#SHARED-DATA">SHARED DATA</a>
<ul>
<li><a href="#Constants">Constants</a></li>
<li><a href="#Variables">Variables</a></li>
</ul>
</li>
<li><a href="#SEE-ALSO">SEE ALSO</a></li>
<li><a href="#COPYRIGHT">COPYRIGHT</a></li>
</ul>
<h1 id="NAME">NAME</h1>
<p>SpadsPluginApi - SPADS Plugin API</p>
<h1 id="SYNOPSIS">SYNOPSIS</h1>
<p>Perl:</p>
<pre><code>package MyPlugin;
use SpadsPluginApi;
my $pluginVersion='0.1';
my $requiredSpadsVersion='0.11';
sub getVersion { return $pluginVersion; }
sub getRequiredSpadsVersion { return $requiredSpadsVersion; }
sub new {
my $class=shift;
my $self = {};
bless($self,$class);
slog("MyPlugin plugin loaded (version $pluginVersion)",3);
return $self;
}
1;</code></pre>
<p>Python:</p>
<pre><code>import perl
spads=perl.MyPlugin
pluginVersion = '0.1'
requiredSpadsVersion = '0.12.29'
def getVersion(pluginObject):
return pluginVersion
def getRequiredSpadsVersion(pluginName):
return requiredSpadsVersion
class MyPlugin:
def __init__(self,context):
spads.slog("MyPlugin plugin loaded (version %s)" % pluginVersion,3)</code></pre>
<h1 id="DESCRIPTION">DESCRIPTION</h1>
<p><code>SpadsPluginApi</code> is a Perl module implementing the plugin API for SPADS. This API allows anyone to add new features as well as customize existing SPADS features (such as balancing algorithms, battle status presentation, players skills management, command aliases...).</p>
<p>This API relies on plugin callback functions (implemented by SPADS plugins), which can in turn call plugin API functions (implemented by SPADS core) and access shared SPADS data.</p>
<p>Plugins can be coded in Perl (since SPADS 0.11) or Python (since SPADS 0.12.29).</p>
<h1 id="PYTHON-SPECIFIC-NOTES">PYTHON SPECIFIC NOTES</h1>
<h2 id="How-to-read-the-documentation">How to read the documentation</h2>
<p>This documentation was written when SPADS was only supporting plugins coded in Perl, so types for function parameters and return values are described using Perl terminology, with Perl sigils (<code>%</code> for hash, <code>@</code> for array, <code>$</code> for scalars). Python plugins just need to ignore these sigils and use their equivalent internal Python types instead (Python dictionaries instead of Perl hashes, Python lists instead of Perl arrays, Python tuples instead of Perl lists, Python None value instead of Perl undef value...).</p>
<p>In order to call functions from the plugin API, Python plugin modules must first import the special <code>perl</code> module, and then use the global variable of this module named after the plugin name to call the functions (refer to the SYNOPSYS for an example of a Python plugin named <code>MyPlugin</code> calling the <code>slog</code> API function).</p>
<h2 id="Perl-strings-conversions">Perl strings conversions</h2>
<p>SPADS uses the <code>Inline::Python</code> Perl module to interface Perl code with Python code. Unfortunately, some versions of this module don't auto-convert Perl strings entirely when used with Python 3 (Python 2 is unaffected): depending on the version of Python and the version of the <code>Inline::Python</code> Perl module used by your system, it is possible that the Python plugin callbacks receive Python byte strings as parameters instead of normal Python strings. In order to mitigate this problem, the plugin API offers both a way to detect from the Python plugin code if current system is affected (by calling the <code>get_flag</code> function with parameter <code>'use_byte_string'</code>), and a way to workaround the problem by calling a dedicated <code>fix_string</code> function which fixes the string values if needed. For additional information regarding these Python specific functions of the plugin API, refer to the <a href="#Python-specific">API FUNCTIONS - Python specific</a> section. Python plugins can also choose to implement their own pure Python function which fixes strings coming from SPADS if needed like this for example:</p>
<pre><code> def fix_spads_string(spads_string):
if hasattr(spads_string,'decode'):
return spads_string.decode('utf-8')
return spads_string
def fix_spads_strings(*spads_strings):
fixed_strings=[]
for spads_string in spads_strings:
fixed_strings.append(fix_spads_string(spads_string))
return fixed_strings</code></pre>
<h2 id="Windows-limitations">Windows limitations</h2>
<p>On Windows systems, Perl emulates the <code>fork</code> functionality using threads. As the <code>Inline::Python</code> Perl module used by SPADS to interface with Python code isn't thread-safe, the fork functions of the plugin API (<code>forkProcess</code> and <code>forkCall</code>) aren't available from Python plugins when running on a Windows system. On Windows systems, using native Win32 processes for parallel processing is recommended anyway. Plugins can check if the fork functions of the plugin API are available by calling the <code>get_flag</code> Python specific plugin API function with parameter <code>'can_fork'</code>, instead of checking by themselves if the current system is Windows. Refer to the <a href="#Python-specific">API FUNCTIONS - Python specific</a> section for more information regarding this function.</p>
<p>Automatic asynchronous socket management (<code>addSocket</code> plugin API function) is also unavailable for Python plugins on Windows systems (this is due to the file descriptor numbers being thread-specific on Windows, preventing SPADS from accessing sockets opened in Python interpreter scope). However, it is still possible to manage asynchronous sockets manually, for example by hooking the SPADS event loop (<code>eventLoop</code> plugin callback) to perform non-blocking <code>select</code> with 0 timeout (refer to <a href="https://docs.python.org/3/library/select.html#select.select">Python select documentation</a>). Plugins can check if the <code>addSocket</code> function of the plugin API is available by calling the <code>get_flag</code> Python specific plugin API function with parameter <code>'can_add_socket'</code>, instead of checking by themselves if the current system is Windows. Refer to the <a href="#Python-specific">API FUNCTIONS - Python specific</a> section for more information regarding this function.</p>
<h1 id="CALLBACK-FUNCTIONS">CALLBACK FUNCTIONS</h1>
<p>The callback functions are called from SPADS core and implemented by SPADS plugins. SPADS plugins are actually Perl or Python classes instanciated as objects. So most callback functions are called as object methods and receive a reference to the plugin object as first parameter. The exceptions are the constructor (Perl: <code>new</code> receives the class/plugin name as first parameter, Python: <code>__init__</code> receives the plugin object being created as first parameter), and a few other callbacks which are called/checked before the plugin object is actually created: <code>getVersion</code> and <code>getRequiredSpadsVersion</code> (mandatory callbacks), <code>getParams</code> and <code>getDependencies</code> (optional callbacks).</p>
<h2 id="Mandatory-callbacks">Mandatory callbacks</h2>
<p>To be valid, a SPADS plugin must implement at least these 3 callbacks:</p>
<dl>
<dt id="Perl-new-pluginName-context-.-.-.-.-Python-__init__-self-context">[Perl] <code>new($pluginName,$context)</code> . . . . [Python] <code>__init__(self,context)</code></dt>
<dd>
<p>This is the plugin constructor, it is called when SPADS (re)loads the plugin.</p>
<p>The <code>$context</code> parameter is a string which indicates in which context the plugin constructor has been called: <code>"autoload"</code> means the plugin is being loaded automatically at startup, <code>"load"</code> means the plugin is being loaded manually using <code>!plugin <pluginName> load</code> command, <code>"reload"</code> means the plugin is being reloaded manually using <code>!plugin <pluginName> reload</code> command.</p>
</dd>
<dt id="getVersion-self"><code>getVersion($self)</code></dt>
<dd>
<p>returns the plugin version number (example: <code>"0.1"</code>).</p>
</dd>
<dt id="getRequiredSpadsVersion-pluginName"><code>getRequiredSpadsVersion($pluginName)</code></dt>
<dd>
<p>returns the required minimum SPADS version number (example: <code>"0.11"</code>).</p>
</dd>
</dl>
<h2 id="Configuration-callback">Configuration callback</h2>
<p>SPADS plugins can use the core SPADS configuration system to manage their own configuration parameters. This way, all configuration management tools in place (parameter values checking, <code>!reloadconf</code> etc.) can be reused by the plugins. To do so, the plugin must implement following configuration callback:</p>
<dl>
<dt id="getParams-pluginName"><code>getParams($pluginName)</code></dt>
<dd>
<p>This callback must return a reference to an array containing 2 elements. The first element is a reference to a hash containing the plugin global settings declarations, the second one is the same but for plugin preset settings declarations. These hashes use setting names as keys, and references to array of allowed types as values. The types must be either strings that match the keys of the <code>%paramTypes</code> hash defined in SpadsConf.pm file, or custom functions to implement custom value checks. Such custom value checking functions take 2 parameters: the value to check as first parameter and a boolean specifying if the value is the default value (i.e. first value declared in the definition, for preset settings) as second parameter. The function must return <code>true</code> if the value is valid, or <code>false</code> if it is invalid (refer to examples below).</p>
<p>As for global SPADS core settings, the global settings of the plugin cannot be changed dynamically except by editing and reloading the configuration files (for example by using the <code>!reloadConf</code> command). The global settings are defined before the preset definition sections in the plugin configuration file. Only one definition is allowed for each global setting, and only one value can be configured in each global setting definition. Global settings aren't impacted by preset changes. Global settings should be used for the structural parameterization of the plugin, which isn't supposed to be modified during runtime and isn't useful from end user point of view (for example file/directory path settings...).</p>
<p>As for SPADS core preset settings, the preset settings of the plugin can be changed dynamically (using the <code>!plugin <pluginName> set <settingName> <settingValue></code> command for example, or simply by changing current global preset). The preset settings are defined in the preset definition sections of the plugin configuration file, after the global plugin settings. The preset settings can be defined multiple times (once in each preset declaration section). Multiple values can be configured in each preset setting definition (values are separated by pipe character <code>|</code>, the first value is the default value applied when loading the preset and the remaining values are the other allowed values). Preset settings are impacted by preset changes. Preset settings should be used for settings that need to be visible by end users and/or modifiable easily at runtime.</p>
<p>Example of implementation in Perl:</p>
<pre><code>my %globalPluginParams = ( MyGlobalSetting1 => ['integer'],
MyGlobalSetting2 => ['ipAddr']);
my %presetPluginParams = ( MyPresetSetting1 => ['readableDir','null'],
MyPresetSetting2 => ['bool', \&myCustomSettingValueCheck] );
sub myCustomSettingValueCheck {
my ($settingValue,$isDefault)=@_;
return $settingValue eq 'true' || $settingValue eq 'false'
}
sub getParams { return [\%globalPluginParams,\%presetPluginParams] }</code></pre>
<p>Example of implementation in Python:</p>
<pre><code>def myCustomSettingValueCheck(settingValue,isDefault):
return settingValue == 'true' or settingValue == 'false'
globalPluginParams = { 'MyGlobalSetting1': ['integer'],
'MyGlobalSetting2': ['ipAddr'] }
presetPluginParams = { 'MyPresetSetting1': ['readableDir','null'],
'MyPresetSetting2': ['bool', myCustomSettingValueCheck], };
def getParams(pluginName):
return [ globalPluginParams , presetPluginParams ]</code></pre>
<p>In these examples, the preset setting <code>MyPresetSetting2</code> is declared as supporting both the values of the predefined type "bool" and the values allowed by the custom function <code>myCustomSettingValueCheck</code>. The predefined type "bool" allows values <code>0</code> and <code>1</code>. The custom function <code>myCustomSettingValueCheck</code> allows values <code>"true"</code> and <code>"false"</code>. So in the end the allowed values for the preset setting <code>MyPresetSetting2</code> are: <code>0</code>, <code>1</code>, <code>"true"</code> and <code>"false"</code>.</p>
</dd>
</dl>
<h2 id="Dependencies-callback">Dependencies callback</h2>
<p>SPADS plugins can use data and functions from other plugins (dependencies). But this can only work if the plugin dependencies are loaded before the plugin itself. That's why following callback should be used by such dependent plugins to declare their dependencies, which will allow SPADS to perform the check for them. Also, SPADS will automatically unload dependent plugins when one of their dependencies is unloaded.</p>
<dl>
<dt id="getDependencies-pluginName"><code>getDependencies($pluginName)</code></dt>
<dd>
<p>This callback must return the plugin dependencies (list of plugin names).</p>
<p>Example of implementation in Perl:</p>
<pre><code>sub getDependencies { return ('SpringForumInterface','MailAlerts'); }</code></pre>
<p>Example of implementation in Python:</p>
<pre><code>def getDependencies(pluginName):
return ('SpringForumInterface','MailAlerts')</code></pre>
</dd>
</dl>
<h2 id="Event-based-callbacks">Event-based callbacks</h2>
<p>Following callbacks are triggered by events from various sources (SPADS, Spring lobby, Spring server...):</p>
<dl>
<dt id="onBattleClosed-self"><code>onBattleClosed($self)</code></dt>
<dd>
<p>This callback is called when the battle lobby of the autohost is closed.</p>
</dd>
<dt id="onBattleOpened-self"><code>onBattleOpened($self)</code></dt>
<dd>
<p>This callback is called when the battle lobby of the autohost is opened.</p>
</dd>
<dt id="onGameEnd-self-endGameData"><code>onGameEnd($self,\%endGameData)</code></dt>
<dd>
<p>This callback is called each time a game hosted by the autohost ends.</p>
<p>The <code>\%endGameData</code> parameter is a reference to a hash containing all the data stored by SPADS concerning the game that just ended. It is recommended to use a data printing function (such as the <code>Dumper</code> function from the standard <code>Data::Dumper</code> module included in Perl core) to check the content of this hash for the desired data.</p>
</dd>
<dt id="onJoinBattleRequest-self-userName-ipAddr"><code>onJoinBattleRequest($self,$userName,$ipAddr)</code></dt>
<dd>
<p>This callback is called each time a client requests to join the battle lobby managed by the autohost.</p>
<p><code>$userName</code> is the name of the user requesting to join the battle</p>
<p><code>$ipAddr</code> is the IP address of the user requesting to join the battle</p>
<p>This callback must return:</p>
<p><code>0</code> if the user is allowed to join the battle</p>
<p><code>1</code> if the user isn't allowed to join the battle (without explicit reason)</p>
<p><code>"<explicit reason string>"</code> if the user isn't allowed to join the battle, with explicit reason</p>
</dd>
<dt id="onJoinedBattle-self-userName"><code>onJoinedBattle($self,$userName)</code></dt>
<dd>
<p>This callback is called each time a user joins the battle lobby of the autohost.</p>
<p><code>$userName</code> is the name of the user who just joined the battle lobby</p>
</dd>
<dt id="onLeftBattle-self-userName"><code>onLeftBattle($self,$userName)</code></dt>
<dd>
<p>This callback is called each time a user leaves the battle lobby of the autohost.</p>
<p><code>$userName</code> is the name of the user who just left the battle lobby</p>
</dd>
<dt id="onLobbyConnected-self-lobbyInterface"><code>onLobbyConnected($self,$lobbyInterface)</code></dt>
<dd>
<p>/!\ RENAMED TO <code>onLobbySynchronized</code> /!\</p>
<p>This callback has been renamed to <code>onLobbySynchronized</code> since SPADS v0.13.35. It can still be used under the name <code>onLobbyConnected</code> for the sake of backward compatibility, however this name is misleading so its usage under this name is not recommended (the callback is only called when the lobby connection is fully synchronized, i.e. when all the initial commands sent by the lobby server after successfully logging in have been received).</p>
<p>The <code>$lobbyInterface</code> parameter is the instance of the <a href="https://github.com/Yaribz/SpringLobbyInterface">SpringLobbyInterface</a> module used by SPADS.</p>
</dd>
<dt id="onLobbyDisconnected-self"><code>onLobbyDisconnected($self)</code></dt>
<dd>
<p>This callback is called each time the autohost is disconnected from the lobby server.</p>
</dd>
<dt id="onLobbyLoggedIn-self-lobbyInterface"><code>onLobbyLoggedIn($self,$lobbyInterface)</code></dt>
<dd>
<p>This callback is called each time the autohost successfully logins on the lobby server. It is one of the three callbacks triggered when a new connection to the lobby server is established. These callbacks are called in following order:</p>
<ul>
<li><p>1) <code>onLobbyLogin</code> (called when trying to login)</p>
</li>
<li><p>2) <code>onLobbyLoggedIn</code> (called when successfully logged in)</p>
</li>
<li><p>3) <code>onLobbySynchronized</code> (called when state is synchronized with server)</p>
</li>
</ul>
<p>The <code>$lobbyInterface</code> parameter is the instance of the <a href="https://github.com/Yaribz/SpringLobbyInterface">SpringLobbyInterface</a> module used by SPADS.</p>
<p>Note: This callback is the ideal place for a plugin to register its lobby command handlers using the <code>addLobbyCommandHandler</code> function of the plugin API, if this plugin needs to process the commands sent by the lobby server during the initial lobby state synchronization phase (i.e. commands sent before the <code>LOGININFOEND</code> command). For plugins that do not require being called back during the initial lobby state synchronization phase, the <code>onLobbySynchronized</code> callback should be used instead to register the lobby command handlers.</p>
</dd>
<dt id="onLobbyLogin-self-lobbyInterface"><code>onLobbyLogin($self,$lobbyInterface)</code></dt>
<dd>
<p>This callback is called each time the autohost tries to login on the lobby server. It is one of the three callbacks triggered when a new connection to the lobby server is established. These callbacks are called in following order:</p>
<ul>
<li><p>1) <code>onLobbyLogin</code> (called when trying to login)</p>
</li>
<li><p>2) <code>onLobbyLoggedIn</code> (called when successfully logged in)</p>
</li>
<li><p>3) <code>onLobbySynchronized</code> (called when state is synchronized with server)</p>
</li>
</ul>
<p>The <code>$lobbyInterface</code> parameter is the instance of the <a href="https://github.com/Yaribz/SpringLobbyInterface">SpringLobbyInterface</a> module used by SPADS.</p>
</dd>
<dt id="onLobbySynchronized-self-lobbyInterface"><code>onLobbySynchronized($self,$lobbyInterface)</code></dt>
<dd>
<p>This callback is called each time the autohost completes the lobby state synchronization phase, which occurs after successfully logging in. It is one of the three callbacks triggered when a new connection to the lobby server is established. These callbacks are called in following order:</p>
<ul>
<li><p>1) <code>onLobbyLogin</code> (called when trying to login)</p>
</li>
<li><p>2) <code>onLobbyLoggedIn</code> (called when successfully logged in)</p>
</li>
<li><p>3) <code>onLobbySynchronized</code> (called when state is synchronized with server)</p>
</li>
</ul>
<p>The <code>$lobbyInterface</code> parameter is the instance of the <a href="https://github.com/Yaribz/SpringLobbyInterface">SpringLobbyInterface</a> module used by SPADS.</p>
<p>Note: This callback is the ideal place for a plugin to register its lobby command handlers using the <code>addLobbyCommandHandler</code> function of the plugin API, as long as this plugin does not need to process the commands sent by the lobby server during the initial lobby state synchronization phase (i.e. commands sent before the <code>LOGININFOEND</code> command). For plugins that require being called back during the initial lobby state synchronization phase, the <code>onLobbyLoggedIn</code> callback must be used instead to register the lobby command handlers.</p>
</dd>
<dt id="onPresetApplied-self-oldPresetName-newPresetName"><code>onPresetApplied($self,$oldPresetName,$newPresetName)</code></dt>
<dd>
<p>This callback is called each time a global preset is applied.</p>
<p><code>$oldPresetName</code> is the name of the previous global preset</p>
<p><code>$newPresetName</code> is the name of the new global preset</p>
</dd>
<dt id="onPrivateMsg-self-userName-message"><code>onPrivateMsg($self,$userName,$message)</code></dt>
<dd>
<p>This callback is called each time the autohost receives a private message.</p>
<p><code>$userName</code> is the name of the user who sent a private message to the autohost</p>
<p><code>$message</code> is the private message received by the autohost</p>
<p>This callback must return:</p>
<p><code>0</code> if the message can be processed by other plugins and SPADS core</p>
<p><code>1</code> if the message must not be processed by other plugins and SPADS core (this prevents logging)</p>
</dd>
<dt id="onReloadConf-self-keepSettings"><code>onReloadConf($self,$keepSettings)</code></dt>
<dd>
<p>This callback is called each time the SPADS configuration is reloaded.</p>
<p><code>$keepSettings</code> is a boolean parameter indicating if current settings must be kept.</p>
<p>This callback must return:</p>
<p><code>0</code> if an error occured while reloading the plugin configuration</p>
<p><code>1</code> if the plugin configuration has been reloaded correctly</p>
</dd>
<dt id="onSettingChange-self-settingName-oldValue-newValue"><code>onSettingChange($self,$settingName,$oldValue,$newValue)</code></dt>
<dd>
<p>This callback is called each time a setting of the plugin configuration is changed (using <code>!plugin <pluginName> set ...</code> command).</p>
<p><code>$settingName</code> is the name of the updated setting</p>
<p><code>$oldValue</code> is the previous value of the setting</p>
<p><code>$newValue</code> is the new value of the setting</p>
</dd>
<dt id="onSpringStart-self-springPid"><code>onSpringStart($self,$springPid)</code></dt>
<dd>
<p>This callback is called each time a Spring process is launched to host a game.</p>
<p><code>$springPid</code> is the PID of the Spring process that has just been launched.</p>
</dd>
<dt id="onSpringStop-self-springPid"><code>onSpringStop($self,$springPid)</code></dt>
<dd>
<p>This callback is called each time the Spring process ends.</p>
<p><code>$springPid</code> is the PID of the Spring process that just ended.</p>
</dd>
<dt id="onUnload-self-context"><code>onUnload($self,$context)</code></dt>
<dd>
<p>This callback is called when the plugin is unloaded. If the plugin has added handlers for SPADS command, lobby commands, or Spring commands, then they must be removed here. If the plugin has added timers or forked process callbacks, they should also be removed here. If the plugin handles persistent data, then these data must be serialized and written to persistent storage here.</p>
<p>The <code>$context</code> parameter is a string which indicates in which context the callback has been called: <code>"exiting"</code> means the plugin is being unloaded because SPADS is exiting, <code>"restarting"</code> means the plugin is being unloaded because SPADS is restarting, <code>"unload"</code> means the plugin is being unloaded manually using <code>!plugin <pluginName> unload</code> command, <code>"reload"</code> means the plugin is being reloaded manually using <code>!plugin <pluginName> reload</code> command.</p>
</dd>
<dt id="onVoteRequest-self-source-user-command-remainingVoters"><code>onVoteRequest($self,$source,$user,\@command,\%remainingVoters)</code></dt>
<dd>
<p>This callback is called each time a vote is requested by a player.</p>
<p><code>$source</code> indicates the way the vote has been requested (<code>"pv"</code>: private lobby message, <code>"battle"</code>: battle lobby message, <code>"chan"</code>: master lobby channel message, <code>"game"</code>: in game message)</p>
<p><code>$user</code> is the name of the user requesting the vote</p>
<p><code>\@command</code> is an array reference containing the command for which a vote is requested</p>
<p><code>\%remainingVoters</code> is a reference to a hash containing the players allowed to vote. This hash is indexed by player names. Perl plugins can filter these players by removing the corresponding entries from the hash directly, but Python plugins must use the alternate method based on the return value described below.</p>
<p>This callback must return <code>0</code> to prevent the vote call from happening, or <code>1</code> to allow it without changing the remaining voters list, or an array reference containing the player names that should be removed from the remaining voters list.</p>
</dd>
<dt id="onVoteStart-self-user-command"><code>onVoteStart($self,$user,\@command)</code></dt>
<dd>
<p>This callback is called each time a new vote poll is started.</p>
<p><code>$user</code> is the name of the user who started the vote poll</p>
<p><code>\@command</code> is an array reference containing the command for which a vote is started</p>
</dd>
<dt id="onVoteStop-self-voteResult"><code>onVoteStop($self,$voteResult)</code></dt>
<dd>
<p>This callback is called each time a vote poll is stoped.</p>
<p><code>$voteResult</code> indicates the result of the vote: <code>-1</code> (vote failed), <code>0</code> (vote cancelled), <code>1</code> (vote passed)</p>
</dd>
<dt id="postSpadsCommand-self-command-source-user-params-commandResult"><code>postSpadsCommand($self,$command,$source,$user,\@params,$commandResult)</code></dt>
<dd>
<p>This callback is called each time a SPADS command has been called.</p>
<p><code>$command</code> is the name of the command (without the parameters)</p>
<p><code>$source</code> indicates the way the command has been called (<code>"pv"</code>: private lobby message, <code>"battle"</code>: battle lobby message, <code>"chan"</code>: master lobby channel message, <code>"game"</code>: in game message)</p>
<p><code>$user</code> is the name of the user who called the command</p>
<p><code>\@params</code> is a reference to an array containing the parameters of the command</p>
<p><code>$commandResult</code> indicates the result of the command (if it is defined and set to <code>0</code> then the command failed, in all other cases the command succeeded)</p>
</dd>
<dt id="preGameCheck-self-force-checkOnly-automatic"><code>preGameCheck($self,$force,$checkOnly,$automatic)</code></dt>
<dd>
<p>This callback is called each time a game is going to be launched, to allow plugins to perform pre-game checks and prevent the game from starting if needed.</p>
<p><code>$force</code> is <code>1</code> if the game is being launched using <code>!forceStart</code> command, <code>0</code> else</p>
<p><code>$checkOnly</code> is <code>1</code> if the callback is being called in the context of a vote call, <code>0</code> else</p>
<p><code>$automatic</code> is <code>1</code> if the game is being launched automatically through autoStart functionality, <code>0</code> else</p>
<p>The return value must be the reason for preventing the game from starting (for example <code>"too many players for current map"</code>), or <code>1</code> if no reason can be given, or undef to allow the game to start.</p>
</dd>
<dt id="preSpadsCommand-self-command-source-user-params"><code>preSpadsCommand($self,$command,$source,$user,\@params)</code></dt>
<dd>
<p>This callback is called each time a SPADS command is called, just before it is actually executed.</p>
<p><code>$command</code> is the name of the command (without the parameters)</p>
<p><code>$source</code> indicates the way the command has been called (<code>"pv"</code>: private lobby message, <code>"battle"</code>: battle lobby message, <code>"chan"</code>: master lobby channel message, <code>"game"</code>: in game message)</p>
<p><code>$user</code> is the name of the user who called the command</p>
<p><code>\@params</code> is a reference to an array containing the parameters of the command</p>
<p>This callback must return <code>0</code> to prevent the command from being processed by other plugins and SPADS core, or <code>1</code> to allow it.</p>
</dd>
</dl>
<h2 id="Customization-callbacks">Customization callbacks</h2>
<p>Following callbacks are called by SPADS during specific operations to allow plugins to customize features (more callbacks can be added on request):</p>
<dl>
<dt id="addStartScriptTags-self-additionalData"><code>addStartScriptTags($self,\%additionalData)</code></dt>
<dd>
<p>This callback is called when a Spring start script is generated, just before launching the game. It allows plugins to declare additional scrip tags which will be written in the start script.</p>
<p><code>\%additionalData</code> is a reference to a hash which must be updated by Perl plugins by adding the desired keys/values. For example a Perl plugin can add a modoption named <code>hiddenoption</code> with value <code>test</code> like this: <code>$additionalData{"game/modoptions/hiddenoption"}="test"</code>. For tags to be added in player sections, the special key <code>playerData</code> must be used. This special key must point to a hash associating each account ID to a hash containing the tags to add in the corresponding player section (subsections can be created by using nested hashes). For tags to be added in AI bot sections, the special key <code>aiData</code> must be used. This special key must point to a hash associating each AI bot name to a hash containing the tags to add in the corresponding AI bot section (subsections can be created by using nested hashes).</p>
<p>Note for Python plugins: As Python plugins cannot modify the data structures passed as parameters to the callbacks, an alternate way to implement this callback is offered. Instead of modifying the <code>additionnalData</code> dictionary directly, the callback can return a new dictionary containing the entries which must be added. For example a Python plugin can add a tag named <code>CommanderLevel</code> set to value <code>8</code> in the player section of the player whose account ID is <code>1234</code> like this: <code>return {'playerData': { '1234': { 'CommanderLevel': 8 } } }</code></p>
</dd>
<dt id="balanceBattle-self-players-bots-clanMode-nbTeams-teamSize"><code>balanceBattle($self,\%players,\%bots,$clanMode,$nbTeams,$teamSize)</code></dt>
<dd>
<p>This callback is called each time SPADS needs to balance a battle and evaluate the resulting balance quality. It allows plugins to replace the built-in balance algorithm.</p>
<p><code>\%players</code> is a reference to a hash containing the players in the battle lobby. This hash is indexed by player names, and the values are references to a hash containing player data. For balancing, you should only need to access the players skill as follows: <code>$players->{<playerName>}->{skill}</code></p>
<p><code>\%bots</code> is a reference to a hash containing the bots in the battle lobby. This hash has the exact same structure as <code>\%players</code>.</p>
<p><code>$clanMode</code> is the current clan mode which must be applied to the balance. Clan modes are specified <a href="http://planetspads.free.fr/spads/doc/spadsDoc_All.html#set:clanMode">here</a>. <code><maxUnbalance></code> thresholds are automatically managed by SPADS, plugins don't need to handle them. So basically, plugins only need to check if <code>tag</code> and/or <code>pref</code> clan modes are enabled and apply them to their balance algorithm.</p>
<p><code>$nbTeams</code> and <code>$teamSize</code> are the target battle structue computed by SPADS. The number of entities to balance is the number of entries in <code>\%players</code> + number of entries in <code>\%bots</code>. The number of entities to balance is always <code>> $nbTeams*($teamSize-1)</code>, and <code><= $nbTeams*$teamSize</code>.</p>
<p>If the plugin is unable to balance the battle, it must not update <code>\%players</code> and <code>\%bots</code>. The callback must return undef or a negative value so that SPADS knows it has to use another plugin or the internal balance algorithm instead.</p>
<p>If the plugin is able to balance the battle, it can use two methods to transmit the desired balance to SPADS (Python plugins can only use the second method):</p>
<p>The first method consists in updating directly the <code>\%players</code> and <code>\%bots</code> hash references with the team and id information. Assigned player teams must be written in <code>$players->{<playerName>}->{battleStatus}->{team}</code>, and assigned player ids must be written in <code>$players->{<playerName>}->{battleStatus}->{id}</code>. The <code>\%bots</code> hash reference works the same way. The return value is the unbalance indicator, defined as follows: <code>standardDeviationOfTeamSkills * 100 / averageTeamSkill</code>.</p>
<p>The second method consists in returning an array reference containing the balance information instead of directly editing the <code>\%players</code> and <code>\%bots</code> parameters. The returned array must contain 3 items: the unbalance indicator (as defined in first method description above), the player assignation hash and the bot assignation hash. The player assignation hash and the bot assignation hash have exactly the same structure: the keys are the player/bot names and the values are hashes containing <code>team</code> and <code>id</code> items with the corresponding values for the balanced battle.</p>
</dd>
<dt id="canBalanceNow-self"><code>canBalanceNow($self)</code></dt>
<dd>
<p>This callback allows plugins to delay the battle balance operation. It is called each time a battle balance operation is required (either automatic if autoBalance is enabled, either manual if <code>!balance</code> command is called). If the plugin is ready for balance, it must return <code>1</code>. Else, it can delay the operation by returning <code>0</code> (the balance algorithm won't be launched as long as the plugin didn't return <code>1</code>).</p>
</dd>
<dt id="changeUserAccessLevel-self-userName-userData-isAuthenticated-currentAccessLevel"><code>changeUserAccessLevel($self,$userName,\%userData,$isAuthenticated,$currentAccessLevel)</code></dt>
<dd>
<p>This callback is called by SPADS each time it needs to get the access level of a user. It allows plugins to overwrite this level. Don't call the <code>getUserAccessLevel($user)</code> function from this callback, or the program will be locked in recursive loop! (and it would give you the same value as <code>$currentAccessLevel</code> anyway).</p>
<p><code>\%userData</code> is a reference to a hash containing the lobby data of the user</p>
<p><code>$isAuthenticated</code> indicates if the user has been authenticated (0: lobby server in LAN mode and not authenticated at autohost level, 1: authenticated by lobby server only, 2: authenticated by autohost)</p>
<p>The callback must return the new access level value if changed, or undef if not changed.</p>
</dd>
<dt id="delayShutdown-self"><code>delayShutdown($self)</code></dt>
<dd>
<p>This callback is called by SPADS each time it needs to quit or restart. It allows plugins to delay the shutdown operation, which can be useful to prevent SPADS from exiting before a process forked by the plugin ends for example.</p>
<p>The callback must return a false value if SPADS can quit right now, or a true value if SPADS must wait before exiting.</p>
</dd>
<dt id="filterRotationMaps-self-rotationMaps"><code>filterRotationMaps($self,\@rotationMaps)</code></dt>
<dd>
<p>This callback is called by SPADS each time a new map must be picked up for rotation. It allows plugins to remove some maps from the rotation maps list just before the new map is picked up.</p>
<p><code>\@rotationMaps</code> is a reference to an array containing the names of the maps currently allowed for rotation.</p>
<p>The callback must return a reference to a new array containing the filtered map names.</p>
</dd>
<dt id="fixColors-self-players-bots-battleStructure"><code>fixColors($self,\%players,\%bots,\%battleStructure)</code></dt>
<dd>
<p>This callback is called each time SPADS needs to fix the teams colors. It allows plugins to replace the built-in color fixing algorithm.</p>
<p><code>\%players</code> is a reference to a hash containing the players currently in the battle lobby. This hash is indexed by player names, and the values are references to hashes containing following player data: <code>team</code> (team number), <code>id</code> (id number) and <code>color</code> (current color configured in lobby, i.e. a hash containing keys <code>"red"</code>, <code>"green"</code>, <code>"blue"</code> and whose values are numbers between 0 and 255 included).</p>
<p><code>\%bots</code> is a reference to a hash containing the bots currently in the battle lobby. This hash has the exact same structure as <code>\%players</code>.</p>
<p><code>\%battleStructure</code> is a reference to a hash containing data concerning the battle structure. This parameter is provided to plugins for ease of use but actually these data are redundant with the data already provided in the two previous parameters (the <code>%players</code> and <code>%bots</code> hashes), they are just organized in a different way. The <code>%battleStructure</code> hash is indexed by team numbers. For each team number, the associated value is a reference to a hash indexed by the ID numbers contained in the team. For each ID number, the associated value is a reference to a hash containing the two following keys: <code>players</code> (the associated value is a reference to an array containing the names of the players belonging to this ID) and <code>bots</code> (the associated value is a reference to an array containing the names of the AI bots belonging to this ID).</p>
<p>If the plugin is unable to fix colors, then it must return <code>undef</code> so that SPADS knows it has to use another plugin or the internal color fixing algorithm instead.</p>
<p>If the plugin is able to fix the players and AI bots colors, then it must return a reference to a hash containing all colors assignations, indexed by ID numbers. The keys must be the ID numbers and the values are references to hash whose keys are <code>"red"</code>, <code>"green"</code> and <code>"blue"</code> and values are the corresponding RGB values (between 0 and 255 included) of the color assigned to the ID.</p>
</dd>
<dt id="setMapStartBoxes-self-boxes-mapName-nbTeams-nbExtraBox"><code>setMapStartBoxes($self,\@boxes,$mapName,$nbTeams,$nbExtraBox)</code></dt>
<dd>
<p>This callback allows plugins to set map start boxes (for "Choose in game" start position type).</p>
<p><code>\@boxes</code> is a reference to an array containing the start boxes definitions. A start box definition is a string containing the box coordinates separated by spaces, in following order: left, top, right, bottom (0,0 is top left corner and 200,200 is bottom right corner). If the array already contains box definitions, it means SPADS already knows boxes for this map.</p>
<p><code>$mapName</code> is the name of the map for which start boxes are requested</p>
<p><code>$nbTeams</code> is the current number of teams configured (at least this number of start boxes must be provided)</p>
<p><code>$nbExtraBox</code> is the number of extra box required. Usually this is 0, unless a special game mode is enabled such as King Of The Hill.</p>
<p>If the plugin isn't able or doesn't need to provide/override start boxes, it must not update the <code>\@boxes</code> array. It must return <code>0</code> so that SPADS knows it has to check other plugins for possible start boxes.</p>
<p>If the plugin needs to provide/override start boxes, it can use two methods to transmit the start box definitions (Python plugins can only use the second method):</p>
<p>The first method consists in replacing the <code>\@boxes</code> array content directly with the new desired start box definitions. If other plugins should be allowed to replace the start box definitions, the callback must return <code>0</code>, else it must return <code>1</code>.</p>
<p>The second method consists in returning an array reference containing the new start box definitions instead of directly updating the <code>\@boxes</code> parameter. The returned array must contain 2 items: the normal return value as first item (<code>0</code> to allow other plugins to replace the start boxes, or <code>1</code> else), and an array reference containg the new start box definitions as second item.</p>
</dd>
<dt id="setVoteMsg-self-reqYesVotes-maxReqYesVotes-reqNoVotes-maxReqNoVotes-nbRequiredManualVotes"><code>setVoteMsg($self,$reqYesVotes,$maxReqYesVotes,$reqNoVotes,$maxReqNoVotes,$nbRequiredManualVotes)</code></dt>
<dd>
<p>This callback allows plugins to customize the vote status messages.</p>
<p><code>$reqYesVotes</code> is the total number of "yes" votes required for vote to pass (if away-voters don't vote).</p>
<p><code>$reqNoVotes</code> is the total number of "no" votes required for vote to fail (if away-voters don't vote).</p>
<p><code>$maxReqYesVotes</code> is the maximum total number of "yes" votes required for vote to pass (if all away-voters come back and vote).</p>
<p><code>$maxReqNoVotes</code> is the maximum total number of "no" votes required for vote to fail (if all away-voters come back and vote).</p>
<p><code>$nbRequiredManualVotes</code> is the minimum number of manual votes required for vote to be taken into account.</p>
<p>The callback must return a list containing following 2 elements: the lobby vote message, and the in-game vote message (undef values can be used to keep default messages).</p>
</dd>
<dt id="updateCmdAliases-self-aliases"><code>updateCmdAliases($self,\%aliases)</code></dt>
<dd>
<p>This callback allows plugins to add new SPADS command aliases by adding new entries in the <code>\%aliases</code> hash reference. This hash is indexed by alias names and the values are references to an array containing the associated command. For example, a Perl plugin can add an alias "<code>!cvmap ...</code>" for "<code>!callVote map ...</code>" like this: <code>$aliases->{cvmap}=['callVote','map']</code></p>
<p><code>"%<N>%"</code> can be used as placeholders for original alias command parameters. For example, a Perl plugin can add an alias "<code>!iprank <playerName></code>" for "<code>!chrank <playerName> ip</code>" like this: <code>$aliases->{iprank}=['chrank','%1%','ip']</code></p>
<p>Note for Python plugins: As Python plugins cannot modify the data structures passed as parameters to the callbacks, an alternate way to implement this callback is offered. Instead of modifying the <code>aliases</code> dictionary directly, the callback can return a new dictionary containing the alias entries which must be added.</p>
</dd>
<dt id="updatePlayerSkill-self-playerSkill-accountId-modName-gameType"><code>updatePlayerSkill($self,\%playerSkill,$accountId,$modName,$gameType)</code></dt>
<dd>
<p>This callback is called by SPADS each time it needs to get or update the skill of a player (on battle join, on game type change...). This allows plugins to replace the built-in skill estimations (rank, TrueSkill...) with custom skill estimations (ELO, Glicko ...).</p>
<p><code>\%playerSkill</code> is a reference to a hash containing the skill data of the player. A Perl plugin can update the <code>skill</code> entry as follows: <code>$playerSkill->{skill}=<skillValue></code>. The skill uncertainty can also be updated by plugins, by updating the <code>sigma</code> entry as follows: <code>$playerSkill->{sigma}=<skillUncertaintyValue></code>.</p>
<p><code>$accountId</code> is the account ID of the player for whom skill value is requested.</p>
<p><code>$modName</code> is the currently hosted MOD (example: <code>"Balanced Annihilation V7.72"</code>)</p>
<p><code>$gameType</code> is the current game type (<code>"Duel"</code>, <code>"Team"</code>, <code>"FFA"</code> or <code>"TeamFFA"</code>)</p>
<p>The return value is the skill update status: <code>0</code> (skill not updated by the plugin), <code>1</code> (skill updated by the plugin), <code>2</code> (skill updated by the plugin in degraded mode)</p>
<p>Note for Python plugins: As Python plugins cannot modify the data structures passed as parameters to the callbacks, an alternate way to implement this callback is offered. Instead of modifying the <code>playerSkill</code> dictionary directly, the callback can return a list containing the normal return value as first item (described above), the new skill value as second item, and optionally the new skill uncertainty value as third item.</p>
</dd>
<dt id="updateGameStatusInfo-self-playerStatus-accessLevel"><code>updateGameStatusInfo($self,\%playerStatus,$accessLevel)</code></dt>
<dd>
<p>This callback is called by SPADS for each player in game when the <code>!status</code> command is called, to allow plugins to update and/or add data which will be presented to the user issuing the command.</p>
<p><code>\%playerStatus</code> is a reference to the hash containing current player status data. A Perl plugin can update existing data or add new data in this hash. For example: <code>$playerStatus->{myPluginData}=<myPluginValue></code></p>
<p><code>$accessLevel</code> is the autohost access level of the user issuing the <code>!status</code> command.</p>
<p>The return value must be a reference to an array containing the names of the status information updated or added by the plugin.</p>
<p>Note for Python plugins: As Python plugins cannot modify the data structures passed as parameters to the callbacks, an alternate way to implement this callback is offered. Instead of modifying the <code>playerStatus</code> dictionary directly, the callback can return a new dictionary containing the data to add/modify in the <code>playerStatus</code> dictionary.</p>
</dd>
<dt id="updateStatusInfo-self-playerStatus-accountId-modName-gameType-accessLevel"><code>updateStatusInfo($self,\%playerStatus,$accountId,$modName,$gameType,$accessLevel)</code></dt>
<dd>
<p>This callback is called by SPADS for each player in the battle lobby when the <code>!status</code> command is called, to allow plugins to update and/or add data which will be presented to the user issuing the command.</p>
<p><code>\%playerStatus</code> is a reference to the hash containing current player status data. A Perl plugin can update existing data or add new data in this hash. For example: <code>$playerStatus->{myPluginData}=<myPluginValue></code></p>
<p><code>$accountId</code> is the account ID of the player for whom status data update is requested.</p>
<p><code>$modName</code> is the currently hosted MOD (example: <code>"Balanced Annihilation V7.72"</code>)</p>
<p><code>$gameType</code> is the current game type (<code>"Duel"</code>, <code>"Team"</code>, <code>"FFA"</code> or <code>"TeamFFA"</code>)</p>
<p><code>$accessLevel</code> is the autohost access level of the user issuing the <code>!status</code> command.</p>
<p>The return value must be a reference to an array containing the names of the status information updated or added by the plugin.</p>
<p>Note for Python plugins: As Python plugins cannot modify the data structures passed as parameters to the callbacks, an alternate way to implement this callback is offered. Instead of modifying the <code>playerStatus</code> dictionary directly, the callback can return a new dictionary containing the data to add/modify in the <code>playerStatus</code> dictionary.</p>
</dd>
</dl>
<h2 id="Event-loop-callback">Event loop callback</h2>
<p>SPADS uses the asynchronous programming paradigm, so it is based on a main event loop. The following callback is called during each iteration of this event loop:</p>
<dl>
<dt id="eventLoop-self"><code>eventLoop($self)</code></dt>
<dd>
<p>Warning: this callback is called very frequently (during each iteration of SPADS main event loop), so performing complex operations here can be very intensive on the CPU. It is recommended to use timers (<code>addTimer</code>/<code>removeTimer</code> functions) instead for all time related operations (timeouts, scheduled actions, regular serialization of persistent data to avoid data loss...). This callback shouldn't be blocking, otherwise SPADS may become unstable.</p>
</dd>
</dl>
<h1 id="API-FUNCTIONS">API FUNCTIONS</h1>
<p>The API functions are implemented by SPADS core and can be called by SPADS plugins (directly from Perl plugins, or via <code>spads.[...]</code> from Python plugins).</p>
<h2 id="Accessors">Accessors</h2>
<dl>
<dt id="getBosses"><code>getBosses()</code></dt>
<dd>
<p>This accessor returns a reference to the hash containing the names of the bosses in the battle lobby (if the hash is empty, the boss mode is disabled).</p>
</dd>
<dt id="getConfMacros"><code>getConfMacros()</code></dt>
<dd>
<p>This accessor returns a reference to the hash containing the configuration macros used to (re)start SPADS.</p>
</dd>
<dt id="getCurrentVote"><code>getCurrentVote()</code></dt>
<dd>
<p>This accessor returns a reference to a hash containing information regarding votes.</p>
<p>If there is no vote in progress and the last vote succeeded, then the returned hash is always empty.</p>
<p>If there is no vote in progress and the last vote failed, then the content of the returned hash depends on the delay since the last vote ended. If the delay is greater than the <a href="http://planetspads.free.fr/spads/doc/spadsDoc_All.html#global:reCallVoteDelay">reCallVoteDelay</a>, then the returned hash is empty, else it contains the two following keys:</p>
<ul>
<li><p><code>user</code>: the name of the user who started the last vote</p>
</li>
<li><p><code>expireTime</code>: the time when the last vote failed (in UNIX timestamp format)</p>
</li>
</ul>
<p>If there is a vote in progress, then the returned hash contains following keys:</p>
<ul>
<li><p><code>user</code>: the name of the user who started the vote</p>
</li>
<li><p><code>expireTime</code>: the time when the vote will timeout, in UNIX timestamp format</p>
</li>
<li><p><code>awayVoteTime</code>: the time when the automatic votes for away users (see <a href="http://planetspads.free.fr/spads/doc/spadsDoc_Preferences.html#pset:voteMode">voteMode</a> preference) will be taken into account, in UNIX timestamp format. This field is reset to zero when the corresponding time is reached and votes for away users are triggered.</p>
</li>
<li><p><code>source</code>: the source of the message which started the vote (either <code>"pv"</code>, <code>"chan"</code>, <code>"game"</code> or <code>"battle"</code>)</p>
</li>
<li><p><code>command</code>: a reference to an array containg the command being voted (the first element is the command name, the other elements are the command parameters)</p>
</li>
<li><p><code>remainingVoters</code>: a reference to a hash whose keys are the names of the players allowed to vote who didn't vote yet</p>
</li>
<li><p><code>yesCount</code>: current number of "yes" votes</p>
</li>
<li><p><code>noCount</code>: current number of "no" votes</p>
</li>
<li><p><code>blankCount</code>: current number of "blank" votes</p>
</li>
<li><p><code>awayVoters</code>: a reference to a hash whose keys are the names of the players who auto-voted blank due to being away (see <a href="http://planetspads.free.fr/spads/doc/spadsDoc_Preferences.html#pset:voteMode">voteMode</a> preference)</p>
</li>
<li><p><code>manualVoters</code>: a reference to a hash whose keys are the names of the players who voted manually, and the values are the actual votes (<code>"yes"</code>, <code>"no"</code> or <code>"blank"</code>)</p>
</li>
</ul>
<p>Note: An easy way to check if a vote is currently in progress consists in calling <code>getCurrentVote()</code> and checking if the returned hash contains the <code>command</code> key. If the hash contains the <code>command</code> key then it means a vote is in progress, else it means no vote is in progress.</p>
</dd>
<dt id="getLobbyInterface"><code>getLobbyInterface()</code></dt>
<dd>
<p>This accessor returns the instance of the <a href="https://github.com/Yaribz/SpringLobbyInterface">SpringLobbyInterface</a> module used by SPADS.</p>
<p>Following methods, called on the <code>SpringLobbyInterface</code> object, can be useful to plugins for accessing various lobby data:</p>
<ul>
<li><p>- <code>getUsers()</code></p>
<p>This method returns a reference to a hash containing the data regarding all the online users. The hash is indexed by player names and the values are references to hashes with following content:</p>
<ul>
<li><p><code>accountId</code>: the lobby account ID of the user</p>
</li>
<li><p><code>country</code>: the country code of the user (2 characters)</p>
</li>
<li><p><code>ip</code>: the IP address of the user, if known (<code>undef</code> else)</p>
</li>
<li><p><code>lobbyClient</code>: the name of the lobby client software used by the user</p>
</li>
<li><p><code>status</code>: the lobby status of the user, which is itself a reference to another hash, containing following content: <code>access</code> (<code>0</code>: normal user, <code>1</code>: moderator), <code>away</code> (<code>0</code>: active, <code>1</code>: away), <code>bot</code> (<code>0</code>: human, <code>1</code>: bot), <code>inGame</code> (<code>0</code>: out of game, <code>1</code>: in game), <code>rank</code> (integer between 0 and 7 included)</p>
</li>
</ul>
<p>Note 1: this method performs a deep copy of the data to prevent external code from corrupting internal data. However it is also possible to read the same data directly without triggering a deep copy by accessing the <code>users</code> field of the <code>SpringLobbyInterface</code> object.</p>
<p>Note 2: if you need to retrieve users indexed by lobby account IDs instead of names, you can access the <code>accounts</code> field of the <code>SpringLobbyInterface</code> object. It contains a reference to a hash whose keys are the lobby account IDs and values are the lobby user names.</p>
</li>
<li><p>- <code>getChannels()</code></p>
<p>This method returns a reference to a hash containing the data regarding all the lobby channels joined by SPADS. The hash is indexed by channel names and the values are references to hashes with following content:</p>
<ul>
<li><p><code>topic</code>: a reference to a hash with following content: <code>author</code> (the name of the user who set the topic), <code>content</code> (the topic content)</p>
</li>
<li><p><code>users</code>: a reference to a hash whose keys are the names of the users in the lobby channel</p>
</li>
</ul>
<p>Note: this method performs a deep copy of the data to prevent external code from corrupting internal data. However it is also possible to read the same data directly without triggering a deep copy by accessing the <code>channels</code> field of the <code>SpringLobbyInterface</code> object.</p>
</li>
<li><p>- <code>getBattles()</code></p>
<p>This method returns a reference to a hash containing the data regarding all the battle lobbies currently hosted on the lobby server. The hash is indexed by battle ID and the values are references to hashes with following content:</p>
<ul>
<li><p><code>engineName</code>: the name of the engine used by the battle lobby (usually <code>"spring"</code>)</p>
</li>
<li><p><code>engineVersion</code>: the version of the engine used by the battle lobby (example: <code>"105.0"</code>)</p>
</li>
<li><p><code>founder</code>: the name of the user who created the battle lobby</p>
</li>
<li><p><code>ip</code>: the IP address used by the battle lobby for game hosting</p>
</li>
<li><p><code>locked</code>: the lock status of the battle lobby (<code>0</code>: unlocked, <code>1</code>: locked)</p>
</li>
<li><p><code>map</code>: the map currently selected in the battle lobby</p>
</li>
<li><p><code>mapHash</code>: the hash of the map currently selected in the battle lobby (computed by the unitsync library)</p>
</li>
<li><p><code>maxPlayers</code>: the battle lobby size (maximum number of players who can be in the battle lobby, ignoring spectators)</p>
</li>
<li><p><code>mod</code>: the mod (game name) used by the battle lobby</p>
</li>
<li><p><code>natType</code>: the type of NAT traversal method used by the host (<code>0</code>: none, <code>1</code>: hole punching, <code>2</code>: fixed source ports)</p>
</li>
<li><p><code>nbSpec</code>: the number of spectators currently in the battle lobby</p>
</li>
<li><p><code>passworded</code>: the password status of the battle lobby (<code>0</code>: not password protected, <code>1</code>: password protected)</p>
</li>
<li><p><code>port</code>: the port used by the battle lobby for game hosting</p>
</li>
<li><p><code>rank</code>: the minimum rank limit used by the battle lobby</p>
</li>
<li><p><code>title</code>: the description of the battle lobby</p>
</li>
<li><p><code>userList</code>: a reference to an array containing the names of the users currently in the battle lobby</p>
</li>
</ul>
<p>Note: this method performs a deep copy of the data to prevent external code from corrupting internal data. However it is also possible to read the same data directly without triggering a deep copy by accessing the <code>battles</code> field of the <code>SpringLobbyInterface</code> object.</p>
</li>
<li><p>- <code>getBattle()</code></p>
<p>This method returns a reference to a hash containing the data regarding the battle lobby currently hosted by SPADS. This hash has following content:</p>
<ul>
<li><p><code>battleId</code>: the battle ID of the battle lobby</p>
</li>
<li><p><code>botList</code>: a reference to an array containing the names of the AI bots currently in the battle lobby</p>
</li>
<li><p><code>bots</code>: a reference to a hash containing the data regarding the AI bots currently in the battle lobby. This hash is indexed by AI bot names and the values are references to hashes with following content: <code>aiDll</code> (details regarding the AI bot, usually the AI name and version, or the AI DLL), <code>battleStatus</code> (data representing the status of the AI bot in the battle lobby, see BATTLESTATUS hash description below), <code>color</code> (data specifying the team color of the AI bot using RGB model, see COLOR hash description below), <code>owner</code> (name of the user hosting the AI bot)</p>
</li>
<li><p><code>restrictedUnits</code>: a reference to a hash whose keys are the names of the game units currently restricted, and values are the corresponding unit limit.</p>
</li>
<li><p><code>modHash</code>: the hash of the mod (game) used by the battle lobby</p>
</li>
<li><p><code>password</code>: the password used to protect the battle lobby (<code>"*"</code> if no password is set)</p>
</li>
<li><p><code>scriptTags</code>: a reference to a hash containing all the script tags (in lower case) and their associated values (used to generate the start script)</p>
</li>
<li><p><code>startRects</code>: a reference to a hash containing the start box definitions. This hash is indexed by box numbers and the values are references to hashes containing the box coordinates (<code>top</code>, <code>left</code>, <code>bottom</code> and <code>right</code>) in the <code>0-200</code> range (<code>0,0</code> is the top left corner and <code>200,200</code> is the bottom right corner).</p>
</li>
<li><p><code>users</code>: a reference to a hash containing the data regarding the users currently in the battle lobby. This hash is indexed by user names and the values are references to hashes with following content: <code>battleStatus</code> (data representing the status of the player in the battle lobby, see BATTLESTATUS hash description below), <code>color</code> (data specifying the team color of the player using RGB model, see COLOR hash description below), <code>ip</code> (IP address of the user if known, <code>undef</code> else), <code>port</code> (client port of the user if known, <code>undef</code> else), and optionally <code>scriptPass</code> if the client provided a script password when joining the battle lobby.</p>
</li>
</ul>
<p>BATTLESTATUS hash description:</p>
<ul>
<li><p><code>bonus</code>: resource bonus value (integer in <code>0-100</code> range, <code>0</code> means no bonus)</p>
</li>
<li><p><code>id</code>: player team number (starting at <code>0</code>)</p>
</li>
<li><p><code>mode</code>: player/spectator mode (<code>0</code>: spectator, <code>1</code>: player)</p>
</li>
<li><p><code>ready</code>: ready state (<code>0</code>: not ready, <code>1</code>: ready)</p>