-
Notifications
You must be signed in to change notification settings - Fork 14
/
ysi readme.txt
1967 lines (1234 loc) · 95.7 KB
/
ysi readme.txt
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
Alex "Y_Less" Cole's Server/Script Includes V0.1
0) Contents
0 - Contents
1 - About
0 - General
1 - History
2 - Readme
0 - Contents
1 - About
2 - Files
3 - Installation
4 - Use
5 - Required Functions
6 - Useable Functions
7 - Internal Functions
8 - Options
9 - Technical Information
10 - Changelog
11 - Future
12 - Legal
13 - Credits
3 - Conventions
2 - Files
0 - pawno/
0 - new.pwn
1 - pawno/include/
0 - YSI.inc
2 - pawno/include/YSI/
0 - YSI_debug.own
1 - YSI_dependencies.own
2 - YSI_misc.own
3 - YSI_post.own
3 - pawno/include/YSI/Core/
0 - YSI_bintree.own
1 - YSI_bit.own
2 - YSI_commands.own
3 - YSI_format.own
4 - YSI_help.own
5 - YSI_INI.own
6 - YSI_languages.own
7 - YSI_player.own
8 - YSI_text.own
4 - pawno/include/YSI/Gamemode/
0 - YSI_properties.own
5 - pawno/include/YSI/Server/
0 - YSI_modules.own
6 - pawno/include/YSI/System/
0 - YSI_default.own
1 - YSI_group.own
2 - YSI_script.own
7 - pawno/include/YSI/Visual/
0 - YSI_areas.own
1 - YSI_checkpoints.own
2 - YSI_objects.own
3 - YSI_race.own
8 - scriptfiles/YSI/
0 - core.EN
1 - core.DK
2 - core.DE
3 - core.NL
4 - core.FR
5 - core.PL
6 - core_format.YSI
9 - scriptfiles/YSI/users/
10 - gamemodes/
0 - _YSI_EXAMPLE_SCRIPTS_WANTED_.pwn
11 - filterscripts/
0 - _YSI_EXAMPLE_SCRIPTS_WANTED_.pwn
12 - /
0 - readme.txt
3 - Installation
4 - Use
0 - General
1 - Users
2 - INI
0 - Reading
1 - Writing
3 - Commands
0 - General
1 - Alternate Names
2 - Prefixes
3 - Shortcuts
4 - /commands
5 - Permissions
6 - Warnings
4 - Text and Languages
0 - Overview
1 - Languages
2 - Loading Text
3 - Displaying Text
4 - Example
5 - Groups
0 - Explanation
1 - Use
6 - Help
7 - Checkpoints
8 - Areas
0 - General
1 - Types
0 - Box
1 - Cube
2 - Sphere
3 - Circle
4 - Polys
2 - Settings
9 - Objects
0 - General
1 - Use
10 - Races
0 - Introduction
1 - Definition
2 - Addition
3 - Starting
4 - Processing
5 - Finishing
11 - Properties
0 - General
1 - API functions
0 - CreateProperty
1 - CreateBank
2 - CreateAmmunation
3 - CreateMoneyArea
4 - CreateMoneyPoint
5 - CreateTeleport
6 - CreateForbiddenArea
5 - Required Functions
0 - YSI_debug.own
1 - YSI_dependencies.own
2 - YSI_misc.own
3 - YSI_post.own
4 - YSI_bintree.own
5 - YSI_bit.own
6 - YSI_commands.own
7 - YSI_format.own
8 - YSI_help.own
9 - YSI_INI.own
10 - YSI_languages.own
11 - YSI_player.own
12 - YSI_text.own
13 - YSI_properties.own
14 - YSI_modules.own
15 - YSI_default.own
16 - YSI_group.own
17 - YSI_script.own
18 - YSI_areas.own
19 - YSI_checkpoints.own
20 - YSI_objects.own
21 - YSI_race.own
6 - Useable Functions
0 - YSI_debug.own
1 - YSI_dependencies.own
2 - YSI_misc.own
3 - YSI_post.own
4 - YSI_bintree.own
5 - YSI_bit.own
6 - YSI_commands.own
7 - YSI_format.own
8 - YSI_help.own
9 - YSI_INI.own
10 - YSI_languages.own
11 - YSI_player.own
12 - YSI_text.own
13 - YSI_properties.own
14 - YSI_modules.own
15 - YSI_default.own
16 - YSI_group.own
17 - YSI_script.own
18 - YSI_areas.own
19 - YSI_checkpoints.own
20 - YSI_objects.own
21 - YSI_race.own
7 - Internal Functions
0 - YSI_debug.own
1 - YSI_dependencies.own
2 - YSI_misc.own
3 - YSI_post.own
4 - YSI_bintree.own
5 - YSI_bit.own
6 - YSI_commands.own
7 - YSI_format.own
8 - YSI_help.own
9 - YSI_INI.own
10 - YSI_languages.own
11 - YSI_player.own
12 - YSI_text.own
13 - YSI_properties.own
14 - YSI_modules.own
15 - YSI_default.own
16 - YSI_group.own
17 - YSI_script.own
18 - YSI_areas.own
19 - YSI_checkpoints.own
20 - YSI_objects.own
21 - YSI_race.own
8 - Options
9 - Technical Information
10 - Changelog
11 - Future
12 - Legal
13 - Credits
===============================================================================
1) About.
1.0) General.
Y_Less' Server/Script Includes (aka YSI) are a set of libaraies designed to make coding of gamemode (especially larger ones) easy by providing a set of fast, stable, efficient and flexible functions to take the emphasis of coding away from HOW things are done to allow more time on WHAT is done. Currently a basic RPG will take months of coding because things like checkpoint streaming, object streaming, properties etc all take large amounts of coding and integration to get working. This library set provides all those facilities and more so you only need to worry about where to put your properties and how much they will cost, not whether you rememebered to add the checkpoint to the streaming system you are still not fully sure works properly.
The libraries were also written with speed as a major consideration employing some more advanced techniques such as lists and binary trees to offset the overhead a generic library such as this incurs. On the whole these techniques offer considerable speed increase, some functions were written multiple times to get the most speed possible out of them. And where systems are slower than custom counterparts the vast number of extra features should be more than enough to offset the marginal difference. E.g. the custom command processor is slightly slower than standard searches using strcmp and strtok (especially for small numbers of commands) however this system has renamable functions, automatic called, registering and unregistering of commands, alternate names, optional prefixes and more so the almost negligable speed decrease is more than worth the extra flexability this gives. Plus the fact that this command system gains very little extra processing time the more commands you add. With strcmp and strtokif you double the number of commands you double the average processing time, with this doubling the number of commands adds a single comparison function to the processing. More on the command processor and all other modules in section 2 (files).
Obviously it's not all good, as hinted at earlier there is extra overhead involved in systems like this as instead of your checkpoints been hard coded into your mode and streamer for example, they're stored and loaded from an array. However due to a wide range of compile time options these overheads can be minimised by tweaking system settings to match your mode. Things like memory usage and processing time can also be reduced by tweaking settings, this is covered more in section 6 (options).
1.1) History.
The system started unofficially around a year ago, I was developing the first version of SA:Underground (my "on-off-off some more" side project) and needed a way to handle multiple races easilly and load MTA maps as well as my own. Thus the first version of YSI_race was born.
A little later I was developing my admin script (which has now been re-written about 4 times, almost every one better than other admin scripts and none released :(), based on my earlier messaging and logging script releases. The latest version of this had a modules system for loading additional scripts for extended functionality and registering their functions with the central script. It also had an advanced command parser including things like alternate names, prefixes, player permissions, help system and native support for %n for naming commands. As well as these there was a language system, global user system and ini loader based on passed parameters not reread files (as I didn't like other implementations). Obviously these were the starts of YSI_text, YSI_languages, YSI_modules (this is the only module in YSI less well developed than it's predecessor), YSI_players, YSI_INI, YSI_format (along with some SA:MP/AMXModX/Quake 3 code), YSI_help and YSI_commands.
At another point in time I had developed my areas script to end the days of complex property creation LVDMod involved, unfortunately mine was even more complicated as I added far too many features in a small space (there are just as many features in YSI_properties but better managed). This was the basis of YSI_properties, YSI_areas and YSI_checkpoints I also developed an object streaming system shortly before the release of 0.2 for testing purposes and a new MTA map loader to go with it.
Since these original scripts pretty much everything has been re-written from the ground up (the only original code I know of is Race_OnPlayerEnterRaceCheckpoint and that's been modifed for checkpoint types and variable names (although the general logic is all original)). As a result some parts of YSI have been rewritten up to four or fives times from scratch, although each time with reference to the last.
1.2) Readme.
1.2.0) Contents.
Lists all the sections in this readme
1.2.1) About.
This section. Tells you about the script and other associated items of possible interest.
1.2.2) Files.
Lists all the files in the YSI download and gives a brief explanation of what they're for.
1.2.3) Installation.
Explains briefly (as it's easy) how to install YSI into your mode.
1.2.4) Use.
This is one of the major sections. It explains how to use the major parts of the major parts of YSI. All the features you may want to use 90% of the time are covered here plus tips on improving your modes custom features and further streamlining development.
1.2.5) Required Functions.
These are all the functions listed down the right hand side which you have to call for the script to work properly. Note however that almost all of these are called via the Script_ and Default_ interfaces so you don't actually need to worry about them (see section 4.0). These are generally refered to as core functions in the code file headers though some may be inline or public.
Note: These are only required if you actually use the containing file.
1.2.6) Useable Functions.
These are functions you can use in your script to utilise YSI and do whatever you like. This is the section most likely to be useful as a reference. These are generally the API and stock functions in the file headers though some may be inline or public.
1.2.7) Internal Functions.
These are listed here for reference only, you can't use them in your script and they're not listed in the function list in PAWNO. They are functions used within YSI and which should not be touched as they can mess things up if used improperly. These are generally static functions in the file headers though some may be static stock, inline, public or core.
1.3) Conventions.
1.3.0) Developing.
There are a few conventions used throughout the code and documentation you should be aware of (mostly just if you're developing YSI itself, most of this is not required knowledge for using the script.
If you want to develop a new library it is requested you use these conventions otherwise you're just writing another library compatible with YSI, not a YSI library. More on this in section <TO DO>.
1.3.1) Function Types.
Functions are listed in the file headers under a few headings:
core - These are functions required by the script. They have no typing and will generate warnings if not used.
stock - These are the general library functions, if they're not used they will be excluded from the mode.
static - These are internal functions for the library and shouldn't (and can't) be called from outside the library. static stock functions are listed here too.
API - These are functions which are common for the script and don't use the library naming scheme for clarity to less advanced coders and for compatability with functions they replace. There is no set rule for this, I just do it when I feel like it and think it's worth it.
inline - These are not real functions but listed as functions. They're define macros made to look like functions for speed after compiling as they're very simple bits of code where the overhead of calling a function would significantly affect their run time.
1.3.2) Function Naming.
For readability and clarity YSI uses an OO style system similar to C++ (class::method()) although not exactly the same. There are few functions which don't follow this but they should be minimised (generally only the functions more likely to be widely used are APIed and even then not all). The reason for this is reduction of likelyhood of conflicting functions in different libraries and it means functions follow a standard format. The format is generally:
Filename_FunctionName()
(note the capitalisation) although the lead part doesn't have to be the filename, but preferably something related (the YSI_languages "class" (or what may be occasionally referred to as a class even though it's blatantly not) is just Langs_ as I couldn't be bothered writing Languages_ for every function).
The obvious exceptions to this rule are the YSI_misc and YSI_post files. YSI_misc just has generic functions so they are just named as normal. YSI_post is an error catcher for all files so has a number of functions to deal with errors from a number of files when they're not included.
Current prefixes (for collision avoidance):
Debug_, Bintree_, Bit_, Command_, Format_, Help_, INI_, Langs_, Player_, Text_, Property_, Modules_, Default_, Group_, Script_, Area_, Checkpoint_, Object_, Race_
Planned prefixes (so you don't nick my future plans :p) (read into these as you like):
XML_, Zone_, Vehicle_, Pickup_, Class_
The other exception to this rule (although this isn't an important thing at all, just a convention I like), for INI tag callbacks I use:
Filename_GeneralName_%s()
Where %s is obviously the tag name.
===============================================================================
2) Files.
2.0) pawno/
2.0.0) new.pwn
Replacement for the standard new.pwn replacing the public functions with their Script_ counterparts and adding a few other function calls to provide a prefectly compiling base to work off.
2.1) pawno/include/
2.1.0) YSI.inc
The main include file. This defines all the natives* (within block comments to not affect the compiler) so they appear in PAWNO in the right hand side. These functions are split up into the different files and in some cases there is a second split (represented by a space) between the functions in there. These gaps separate standard functions (File_FunctionName()) and API functions (FunctionName()). There is no difference in use but the standard functions tend to be slightly more advanced, the API fuctions are designed to be dead easy to use (however there aare standard functions which are also easy to use).
This file also includes all the other required files but displays all functions regardless of file compile options.
* "all the natives" refers to all the functions provided by the YSI system (which aren't strictly native) which can (and in some cases should) be called from your script. For every function on the right you can use there is probably at least one more internally you can't use to protect the operation of the script.
2.2) pawno/include/YSI/
2.2.0) YSI_debug.own
Provides a number of debug functions so you can add debug information to your script without having to remember to remove it again.
2.2.1) YSI_dependencies.own
Processes your compile options to include only the required files and files which can be included given your options.
2.2.2) YSI_misc.own
Provides a largeish number of standard small functions to your script and the includes.
2.2.3) YSI_post.own
Catches errors generated by a incomplete compile set of librarie includes. I.e. surpresses problems created by excluding certain libraries from your mode.
2.3) pawno/include/YSI/Core/
2.3.0) YSI_bintree.own
Provides an interface for using binary trees simply, used by the script.
2.3.1) YSI_bit.own
Provides bit array functions for the script and library so you don't have to waste entire cells for a single yes/no value per player, you can create bit arrays over 32 bits big to store the yes/no value for every player in only 7 cells not 200 (assuming 200 players).
2.3.2) YSI_commands.own
The new command processor. Provides functions for adding and dynamically renaming commands and processes commands using a binary tree of hash values for speed.
2.3.3) YSI_format.own
PAWN implementation of the format() function originally written for AMXModX based on vsprintf from Quake 3. Adds a few extra items such as octal radix and commands (due to their renamable nature).
2.3.4) YSI_help.own
Provides a /help function which allows you to type /help <command> and get help on that specific command (provided the command itself provides this functionality via the 'help' parameter).
2.3.5) YSI_INI.own
Provides highly optimised INI functions for reading and writing INI files without repeated disk accesses and slow file scanning per item as all other INI file implementations do. Used by the user system and text system.
2.3.6) YSI_languages.own
Provides an interface to the text system for loading multiple files and languages for text used throughout the script to enable messages to be sent in different languages to different people.
2.3.7) YSI_player.own
Provides user systems and per player options for the script.
2.3.8) YSI_text.own
Handles text displays for different languages based on player settings.
2.4) pawno/include/YSI/Gamemode/
2.4.0) YSI_properties.own
Provides fairly high level mode interaction through the handling of multiple types of elements common to a range on modes including properties, ammunations, banks etc.
2.5) pawno/include/YSI/Server/
2.5.0) YSI_modules.own
Provides handling of modules loaded into the script. Very out of date.
2.6) pawno/include/YSI/System/
2.6.0) YSI_default.own
Calls required functions at the correct times when these functions are called so you only need to worry about calling one function instead of them all in the correct orders.
2.6.1) YSI_group.own
Handles user groups, similar to teams and levels combined.
2.6.2) YSI_script.own
Hooks the callbacks, calls the functions in YSI_default.own and forwards them to your mode as Script_CallbackName().
2.7) pawno/include/YSI/Visual/
2.7.0) YSI_areas.own
Provides area detection in a range of shapes using optimised storage methods.
2.7.1) YSI_checkpoints.own
Provides checkpoint streaming.
2.7.2) YSI_objects.own
Provides object streaming, worlds for objects and more using lists based on a grid structure for speed.
2.7.3) YSI_race.own
Provides functions for creating and running races easilly.
2.8) scriptfiles/YSI/
2.8.0) core.EN
Standard text items in English.
2.8.1) core.DK
Standard text items in Danish. Translated by TheAlpha.
2.8.2) core.DE
Standard text items in German. Translated by breadfish.
2.8.3) core.NL
Standard text items in Dutch. Translated by Fireburn.
2.8.4) core.FR
Standard text items in French. Translated by yom.
3.8.4) core.PL
Standard text items in Polish. Translated by 50p.
2.8.6) core_format.YSI
Holds the formatting information for all items in the core language files.
2.9) scriptfiles/YSI/users/
2.10) gamemodes/
2.10.0) _YSI_EXAMPLE_SCRIPTS_WANTED_.pwn
Basically an advert to appear first in people's open dialogue box.
2.11) filterscripts/
2.11.0) _YSI_EXAMPLE_SCRIPTS_WANTED_.pwn
Basically an advert to appear first in people's open dialogue box.
2.12) /
2.12.0) readme.txt
This file.
===============================================================================
3) Installation.
Installation is designed to be as easy as possible. Provided you have installed the server with it's default directory structure (i.e. with pawno, filterscripts, gamemodes and (possibly if you have one) scriptfiles as child directories) then simply extract the contents of the archive to your server folder. The files in the archive have the same structure as your server directory so they will all be put in the correct places. Then simply open pawno and click new.
If you have moved your pawno directory somewhere else you will need to put YSI.inc and the YSI directory (and all children) in your pawno/include/ folder.
===============================================================================
4) Use.
4.0) General.
The easiest way to use YSI is install it and click new but there are more advanced options if you require.
YSI is designed to be highly scalable so beginners and more advanced coders can both use it at a their own level. If you use the YSI new.pwn file all functions and script calls are already set up so you can just get on with coding as if it were a normal script. If you choose to integrate YSI into an existing script there are a few more steps you need to take (it's often easier to copy your existing code into the YSI new.pwn file for fast integration).
Obviously YSI libraries require a few callbacks to call them so they know what to do when. The easiest way as mentioned it to use the provided Script_ interface in new.pwn. The second easiest way is to use the Default_ interface which calls all the required functions in the correct order. If you choose to use neither of these methods you will have to call the callback functions in different files yourself from the callbacks. To aid in this all those functions are not stock so if there is a file you need to call but don't the compiler will issue a warning for that function. It should be obvious where most of these functions should be called from (e.g. Player_OnPlayerConnect should be called from OnPlayerConnect). The only ones not named like that are the File_File functions (e.g. Player_Player) these should be called from your init function (OnFilterScriptInit or OnGameModeInit). Also for efficiency of mode reasons Langs_Langs and Text_Text should be called after everything else. Command_Parse should also be called with those two and definately after Command_Command.
4.1) Users.
YSI provides native support for users and login systems. When a player logs in with /login (by default) first OnPlayerLogin(playerid, yid) is called, returning a 0 here refuses the login. If the login succeedes their data is loaded from their personal file. yid is their unique identifier used for their file and identification by the script. All data in the file is grouped by tags so you could have a user file which looks like:
[ysi_core]
language = 0
[my_mode_1]
health = 100.0
[my_mode_2]
health = 50.0
As you can see there are two health values but based on the mode been run you can simply load one. The ini loading system (which will be explained further in section 4.<TO DO>) reads values from the file and distributes them as required, this is faster and requires far less disk accesses than current methods which request data and load it item by item from the file. To save the data for your current modes tag simply add the following function to your mode (and forward it):
public LoginDat_<mode_tag>(playerid, identifier[], text[])
{
}
Where <mode_tag> is the tag for this mode's data exactly as it appears in the ini. E.g. for my_mode_1 in the example ini above (including parsing of certain values):
public LoginDat_my_mode_1(playerid, identifier[], text[])
{
if (!strcmp(identifier, "health", true)) SetPlayerHealth(playerid, floatstr(text));
else if (!strcmp(identifier, "wanted", true)) SetPlayerWantedLevel(playerid, strval(text));
}
Obviously that function would load the two items "health" and "wanted" from the ini however with the example above the wanted code would never be called, greatly reducing the need for error checking for either returning 0 or not returning anything required in other ini loading scripts.
When a player logs out OnPlayerLogout(playerid, yid) is called. The first thing you need to do here is set the tag using Logout_SetTag(tagname[]), in our first example that would be:
Logout_SetTag("my_mode_1");
This ensures the data is saved correclty for this mode, not overwriting other mode's data.
You can then write the data you want saving using:
Logout_SaveString(name[], data[]);
Logout_SaveInt(name[], data);
Logout_SaveFloat(name[], Float:data);
These are just wrappers for the equivalent INI functions but abstract them slightly so you don't need to worry about the INI file handle.
The other commands involved in the user functions are /register and /groupnick. The first command is fairly obvious in function, the second is similar to the /ns group command on IRC. It adds your current nick to the stats of the nick specified providing you enter the correct password.
4.2) INI.
4.2.0) Reading
The INI functions in YSI are slightly different to other INI libraries available. With other libraries you request a line from the INI file, the file is opened and every single line is read until the one you want is reached at which point the data you want is saved and the file closed. If you then want another piece of data the same process is repeated which is obviously slow, inefficient and not very good on your disk due to the repeated file reads.
This INI library reads all the file at once and you save the data you want which means the file in only read once and if you have a large amount of data not used by any one mode a large amount of searching time will be saved too. This is done by means of a custom callback system. You call the INI_Parse function passing a file to parse and the format of the callback functions you want calling. The call line for the text loading functions is:
INI_ParseFile(filename, "Text_Tag_%s")
The %s is for the tag currently being read so if filename was YSI/core.EN (the default English language file) the callbacks would be:
Text_Tag_ysi_properties
Text_Tag_ysi_race
Text_Tag_ysi_text
Text_Tag_ysi_commands
Text_Tag_ysi_langs
Text_Tag_ysi_help
Text_Tag_ysi_players
This is what the Text_RegisterTag functions in the various YSI files are for, they (if you look at the macro which defines them) create the callback function required to save these pieces of data. Another example:
INI file (example.INI):
[size]
thing1 = 56
thing2 = 126
[colour]
thing1 = 12
thing2 = 98
PAWN file:
new
gSize1,
gColour1;
forward savedata_size(identifier[], data[]);
forward savedata_colour(identifier[], data[]);
main()
{
INI_ParseFile("example.INI", "savedata_%s");
return 1;
}
public savedata_size(identifier[], data[])
{
if (!strcmp(identifier, "thing1")) gSize1 = strval(data);
}
public savedata_colour(identifier[], data[])
{
if (!strcmp(identifier, "thing1")) gColour1 = strval(data);
}
Obviously after this code is run gSize1 will be 56 and gColour1 will be 12. You can also include the filename in the callback name:
INI_ParseFile("example.INI", "savedata_%s_%s");
public savedata_size_example.INI(identifier[], data[]) {}
You can also have the filename first:
INI_ParseFile("example.INI", "savedata_%s_%s", true);
public savedata_example.INI_size(identifier[], data[]) {}
You can (as is shown in the player load examples) have a single extra parameter first for array identification:
INI_ParseFile("example.INI", "savedata_%s", false, true, 45);
public savedata_size(number, identifier[], data[]) {}
The first false means do not reverse format parameters (tag name and file name). The true means include the extra parameter and the 45 is the extra parameter (which can be any single cell value).
The other major difference with this INI library is it fully supports INI comments (; delimited):
; This line won't be read
data = read value ; ignored ending of line
Comments on the ends of lines are also rewritten if the data on that line is changed so doing:
INI_WriteString(file, "data", "written value");
(see section 4.2.1). Would result in:
; This line won't be read
data = written value ; ignored ending of line
4.2.1) Writing
Writing INI files works on a similar principle to that of reading them - disk access and entire file scans (and, in the case of writing, copies) are bad and time consuming. For this reason a buffer system is employed. The buffer system stores all data being written to the ini until you are done writing and close the ini or the buffer fills up. When either of these events occur the file is loaded and copied, replacing all values in the file with their buffer equivalent where applicable and dunmping everything from the buffer not in the file already under their correct tag in the file. The buffer is an intertwined list structure so tags can be changed repeatedly and all data added to the end of the buffer will still be associated with their correct tag. The write process also uses multiple bit arrays to ensure text is not checked against if it's already been written to the file.
To start writing a new INI file simply use:
new INI:nameOfIniHandle = INI_Open("filename");
This will return a handle to the buffer being used for the file specifed or -1 if there are no available buffers. To write data to the buffer you will first need to set the tag (or the data won't be saved):
INI_SetTag(nameOfIniHandle, "tag_name");
This will set the header for subsequent data to be stored under until the next header is set. Data is written using:
INI_WriteString(nameOfIniHandle, "identifier1", "data");
INI_WriteInt(nameOfIniHandle, "identifier2", 42);
INI_WriteFloat(nameOfIniHandle, "identifier3", 42.0);
You can then close the file to write all the remaining data from the buffer to the file.
INI_Close(nameOfIniHandle);
That code above will produce an INI file called "filename" with the following data:
[tag_name]
identifier1 = data
identifier2 = 42
identifier3 = 42.000000
Floats are to 6dp by default but this can be altered with the optional extra parameter:
INI_WriteFloat(nameOfIniHandle, "identifier3", 42.0, 2);
Gives:
identifier3 = 42.00
4.3) Commands.
4.3.0) General
Commands in YSI are designed to be similar to DCMD but slightly easier. As with DCMD you need a declaration and a function however unlike dcmd the declaration should not be in OnPlayerCommandText. You merely have to declare the command's existence, not check it's called. The other difference from dcmd is the number of parameters, with dcmd you had to put the command name, the variable you were checking against and the length of the command, none of that is nessecary with ycmd. The function follows a similar naming convention as well, ycmd_commandname however there is an extra parameter (help) and the function must be public. Example:
forward ycmd_kill(playerid, params[], help); // Forward declaration for our public function.
public ycmd_kill(playerid, params[], help) // Function header.
{
if (help) // If a player typed: /help kill (see section <TO DO>)
{
Text_Send(playerid, "KILL_COMMAND_HELP"); // Language specific message, see section <TO DO>
}
else // If a player typed: /kill
{
SetPlayerHealth(playerid, 0.0); // Kill the player who entered the command
}
return 1; // Command executed return.
#pragma unused params // Not required on public functions but neater
}
Script_OnFilterScriptInit() // Init function only called once (using the YSI Script_ interface).
{
ycmd(kill); // Initial declaration adding the kill command to the list of commands.
return 1;
}
That example also introduced the start of the help and text/language systems in YSI, as well as used the Script_ interface. If the only part of YSI you are using is the command processor your code may look more like this:
Command_(kill) return SetPlayerHealth(playerid, 0.0); // The kill function with reduced header.
public OnFilterScriptInit() // public not Script_.
{
Command_Command(); // Must be called first.
ycmd(kill); // Commands go here.
Command_Parse(); // Must be called last.
return 1;
}
public OnPlayerCommandText(playerid, cmdtext[])
{
return Command_Process(playerid, cmdtext); // Must be called here.
}
Here you can first see the Command_(name) function, this is a simple macro to replace all the many forwards and function declarations, it will expand to the same code as the first example. The main disadvantage to this is the variables are not obviously declared (you can't see playerid being declared as it's hidden in a macro) thus it makes code ambiguous and possibly confusing (I stopped using it after confusing myself during debugging :)).
The other thing you will notice in that example is that as no part of the script besides commands is in use the Script_ interface has been removed (although it can still be used and will adapt to your compile options (see section 6). Because of this the required Command functions have to be called manually.
4.3.1) Alternate Names.
The YSI command system has a unique feature - alternate names, these will overwrite the default name based on the function called. As described in section 4.3.0 the command and the function it calls must be the same, as with DCMD, however this is not always feasable. YSI has an inbuilt /commands command (see section <TO DO>) however your mode may also have a /commands command (although you should be using the server nature of YSI but this isn't fully developed yet...) which will cause problems if they both activate at once. You can either find where the command and function are declared in YSI (plus every text reference (see section <TO DO>)) or your other mode and alter them, which is all well and good if you can code, or you can simply set an alternate name for the YSI /commands command and it will no longer respond to the original command. To do this simply put:
Command_SetAltName("commands", "ysi commands");
Or whatever you want the new command to be. The first parameter is the real command name (which is used extensively throughout YSI as it doesn't conflict internally) to identify what it is you are renaming. The second parameter is the new command for people to type, but the original ycmd_commands function will still be called. You also don't need to worry about things like text displaying (for example): "type /commands for a list of commands", YSI has inbuilt support within text to update command names (see section <TO DO>).
You also need to toggle the script to use the altnames, but this is minor:
Command_UseAltNames(1);
This still requires a small bit of coding knowledge on the part of the user but with a bit of clever coding you can easilly come up with something like:
INI file (YSI/options.INI):
[commands]
commands = ysi commands
help = ysi help
login = ysi login
In OnGameModeInit:
Command_UseAltNames(1);
INI_ParseFile("YSI/options.INI", "LoadedOptions_%s");
Elsewhere in your script:
forward LoadedOptions_commands(identifier[], text[]);
public LoadedOptions_commands(identifier[], text[])
{
Command_SetAltName(identifier, text);
}
Few very basic functions knocked together in 30 seconds and you have written yourself a complete file based command name customisation system almost anyone can use. This is only scratching the surface of YSI and think how long that would have taken to write using any other combination of system (a lot more that 7 lines (and it can easilly be made 4)).
4.3.2) Prefixes
In the last example of section 4.3.1 I gave an example of how to convert your commands to start with "ysi " (including the space, which is fully supported in YSI). This is fine if you only want to prefix a couple (and will do for them all) but it's a hassle if you have a large amount of commands to rename, this is where prefixes come in:
Command_SetPrefix("ysi");
With that function all your commands are now:
commands,
help,
login,
etc
They've not changed, that's not right :(
Command_UsePrefix(1);
Gives:
ysicommands,
ysihelp,
ysilogin,
etc
But that's still not quite what you wanted, a quick fix later:
Command_UseSpace(1);
And you've got:
ysi commands,
ysi help,
ysi login,
etc
Much better :). This can also be easilly integrated into using an INI system but you can do that yourself.
Warning: If you were to combine the example prefix settings with the example altnames above you would end up with:
ysi ysi commands,
ysi ysi help,
ysi ysi login,
etc
4.3.3) Shortcuts.
Another YSI innovation, shortcuts allow players to do things like:
/a
And have them PERSONALLY configured as another command (this command does not even have to be part of the YSI system). Currently shorcuts can only be a single character and ascend from the character 'a' through the ASCII character set. Shorcuts, which part of the command system, are player specific so are classed as part of the Player code. To add a shortcut use:
Player_AddShortcut('a', "commandname");
4.3.4) /commands
The commands function in YSI is inbuilt into the command processor. This means you do not need to worry at all about it displaying all the commands, everything available to a player (see section 4.3.5) at any given moment will be displayed in the list, updating as required with alterations in commands and use rights.
/commands uses the Command_Name(funcname[]) function to get the real current name of a function. Due to the original (and hopefully future) serverwide design of YSI this returns the real name in a property (property 179176128 - the adler32 hash of something I forgot but is "Command" (gotta love code comments)). If you use this function don't forget to unpack the property string before using.
4.3.5) Permissions.
The YSI command suite has an inbuilt permissions system working on a per-player basis (this is based around the groups system (see section <TO DO>)). There are no level or team restrictions on commands as you may have someone of equal level (it the conventional admin system sense) who you do not want abusing a certain command but you still want to give access to for other commands of that "level". Under most systems at best this would have meant creating a whole new level between two others and setting their's to that, at worst it would have meant demoting meaning they couldn't use commands they should have been able to. That is not a problem in YSI, simply do:
Command_SetPlayerUse("commandname", playerid, 0);
And they no longer have access to that command. Conversely if you want to give a player temporary (or even permenant) access to a command you can simply do:
Command_SetPlayerUse("commandname", playerid, 1);
As mentioned earlier internally commands are referenced by their "real" name (i.e. the name of the function). So even if you had used altnames to rename /commands to /thing_to_tell_me_commands you would still do:
Command_SetPlayerUse("commands", playerid, use);
You can still set group options as in team or admin level systems but that will be explained in greater depth in section <TO_DO>. You can also use:
Command_SetPlayerUseByID(command, playerid, use);
But that would require you knowing the exact index in the internal array of the command you want to set (although this is obviously faster).
4.3.6) Warnings.
There are a number of reasons warnings can be issued when entering commands - you don't have permission to use them, you're a badly connected kicked player, you entered dodgey characters etc. For each of these there are a number of options. By default they all return 0, which will give an UNKNOWN COMMAND message from the server, but of course, this being YSI, you can change that:
Command_SetDisconnectReturn(set);
Sets what should be returned to players not connected (1 or 0)
Command_SetIllegalReturn(set);
Sets what should be returned to players who use illegal characters (1 or 0)
Command_SetDeniedReturn(set);
Sets what should be returned to players who try use a command they can't (1 or 0). 1 is best used with Command_UseDeniedMessage(1)
Command_UseDeniedMessage(set);
Sets whether the warning message (YSI_COMM_BLEVEL) should be shown to players who try use a command they can't use
4.4) Text and Languages.
4.4.0) Overview.
One of the major features of YSI is it's integrated language and text system. Instead of typing what you want to appear on someone's screen directly into your mode you use refernces to loaded strings and put the real text in a file. It sounds complicated but it's really not.
The whole point of the system is to allow people of different mother toungues to see server and system messages in their own language (subject to available translations of course). Certain servers, such as certain Dutch or Russian ones, have a definate user base over two distinct major languages (Dutch and English and Russian and English (there may be major Dutch/Russian servers but for obvious reasons I don't play on them thus am not aware of any)). In these cases there is a definate case for displaying messages in two languages, especially if the user can select which of the languages they would prefer to recieve messages in.
There are a number of obvious ways this can be achieved:
Display both:
SendClientMessage(playerid, 0xFF0000AA, "Example Message");
SendClientMessage(playerid, 0xFF0000AA, "Voorbeeld bericht");
Use comparisons for every single message you have:
if (gPlayerLanguage[playerid] == 1) SendClientMessage(playerid, 0xFF0000AA, "Example Message");
else SendClientMessage(playerid, 0xFF0000AA, "Voorbeeld bericht");
The first is almost always out of the question, especially for larger numbers of languages. The second, even with switches, becomes unwieldy very quickly the more languages you add. Plus with both of these methods you have to go all through your code to find every instance to add a language.
Compare those to:
Text_Send(playerid, "EXAMPLE_MESSAGE_NAME");
Then separate files for each language:
file.EN:
EXAMPLE_MESSAGE_NAME = Example Message
file.NL:
EXAMPLE_MESSAGE_NAME = Voorbeeld bericht
It may seem a bit bloated at first but it makes things A LOT easier in the long run, especially as it means you can change loaded languages and text displayed without even recompiling, very useful for released modes and others.
4.4.1) Languages.
The first thing you need to do to use the text system is load some text. To do thins you need to select some files to load and select what languages to load them for. You selection of language files may look like (for example):
core.EN
core.NL
core.FR
core_format.YSI
main.EN
main.NL
main_format.YSI
We don't need to worry about the _format.YSI files for now, they'll be explained in section <TO_DO>. core.XX stores the data for the YSI libraries, in this example main.XX stores the text used in the custom mode. XX is a lnguage identifier, based on the country shortcode: EN = English, NL = Dutch (Nederlands), FR = French (Français).
We need to load both core and main as the text from both the core script and the mode will be required (this may not always be the case depending on what modules of YSI you require). To do this we add the following lines to an initialisation function (e.g. Script_OnFilterScriptInit):
Langs_AddFile("core");
Langs_AddFile("main");
There are no file extensions as they are determined by languages. On the server we are on there are mostly English and French speaking people, so it is decided to only load the data for those languages, so the following functions are added:
Text_AddLanguage("EN", "English");
Text_AddLanguage("FR", "Français");
The first parameter identifies the files to load, the second is the name for the purposes of listing and requesting use of a language.
As you may have noticed there is no main.FR, if this is the case or if the main.FR file is missing certain entries the system will default to displaying the English equivalents. English is the primary language in this example simply because it was added first, here French is the default language (this also means people joining the server will recieve messages in this language until they change):
Text_AddLanguage("FR", "Français");
Text_AddLanguage("EN", "English");
In this instance, if the French files were missing certain entries no other language would be displayed instead as french is the default so you would simple get an error message for text not found.
4.4.2) Loading Text.
Half the difficulty of loading text was covered in section 4.4.1 just by selecting files to load (and that wasn't exactly hard was it :D), the rest of the loading is done by the text engine but you still have to tell it what text you want to load. These are selected lines from core.EN:
[ysi_properties]
YSI_PROP_NAME = Property: "%s" Cost: $%d Reward: $%d
YSI_PROP_BUY = Type "/%n" to buy
YSI_PROP_OWN = You already own this property
[ysi_race]
YSI_RACE_COUNTDOWN = %d
YSI_RACE_GO = GO GO GO!!!
As you can hopefully tell from the tags there's text here for properties and for races (the one consisting entirely of "%d" may seem pointless but:
YSI_RACE_COUNTDOWN = Start in: %d seconds
Is just as valid and may be what someone wants for their mode so it was left as a text entry). If however you didn't want to use the YSI_property system it would be s bit of a waste of time, processing and memory to load them all and store them in the limited text buffer. That is where tag registering comes in.
For every tag you want in a file you need to add:
Text_RegisterTag(tagname);
To your script. There is a line in YSI_properties which looks like this:
Text_RegisterTag(ysi_properties);
If you don't include the properties code this line isn't compiled and the properties text is never saved. What the function actually does is very similar to the Command_(name) function mentioned earlier. It creates a public function for the INI loading system for that tag, that function then calls the text save system and passes the data over, but only the data with corresponding public functions is saved. You don't need to worry about HOW it does it, all you need to know is that you mustn't use ""s around the text and you must place the line OUTSIDE other functions (it's actually a whole function, not a function call).
4.4.3) Displaying Text.
So you've loaded selected text from selected files in selected languages, (it wasn't really that hard was it?) - now you want to display it.
Unfortunately there's just a fraction more loading of data to do, fortunately you don't need to worry about any code for it. The name_format.YSI files mentioned earlier hold the formatting information for your text items. This means you can change all aspects of your text's appearence (excluding text_draw styling) without any recompiling. Example of core_format.YSI:
[colours]
GREEN = 0x00FF00AA
[data]
name = YSI_PROP_NAME
color = GREEN
name YSI_PROP_BUY
time 5000
style 3
YSI_SELL_HELP_4
color = 0x00FF00AA
As you can see there are two sections to the file, the first defines colours in much the same way as people do:
#define COLOUR_GREEN 0x00FF00AA
in PAWN scripts. These colours are used for defining styles in the second section so you can change things easilly, just as in scripts. The second section defines the text's appearence, default is black SendClientMessage as that is 0, 0 but any aspect can be changed. Options per text are:
name - Text identifier you are setting data for, identifier optional (as with YSI_SELL_HELP_4 above).
style - How you want the text to appear. 0 is SendClientMessage, anything else is that GameText style.
type - See style.
colour - The colour you want your SendClientMessage text.
color - See colour.
time - Time to display GameText message for.
colour can either be a hex number (0x prefix optional) or a string representing one of the predefined colours.
Note: You can spell colour correctly or you can drop the u if you really insist, I wrote the code to accept either.
Now you have the text loaded and the style information for that text there's not a lot left to do beyond show it:
Text_Send(playerid, "YSI_PROP_BUY");
Going on the stlye information above and the default text entry for this playerid should see:
Type "/%n" to buy
In GameText style 3 for 5 seconds (5000ms) (in actual fact they would see:
Type "/#n" to buy
Due to SA:MP character protection).