Scripting
Scripting
====================================
For sider.dll version: 5.2.3
April 4, 2019
1. Introduction
~~~~~~~~~~~~~~~
After reading this guide, the next step is to study the example (and
non-example) modules, which are provided with this release of Sider.
Find them in the "modules" directory.
2. Module structure
~~~~~~~~~~~~~~~~~~~
If you are familiar with Lua and how modules are typically organized
then this will make full sense to you. If are you new to Lua, i would
strongly recommend reading "Programming in Lua" by Roberto Ierusalimschy.
2nd edition covers Lua 5.1, which is the version of the language used
by Sider. However, any other edition of the book will be just as helpful.
Example module:
-----------------------------
local m = {}
function m.init(ctx)
log("Hello, world!")
end
return m
-----------------------------
As you have already guessed, this module doesn't really do much. But it
is a valid module, and can be loaded by Sider. For that you need to save
it as <something>.lua file in the "modules" folder, inside sider. Let's
assume that you named it: test.lua. Then, you must also enable it in
sider.ini, like this:
lua.module = "test.lua"
**************************************************************************
**************************************************************************
VERY IMPORTANT: File encoding must be UTF-8. This is vital, if you are
using non-latin characters in the strings in the module code - for example,
in paths. If you only have latin-1 chars, then ANSI is ok too.
**************************************************************************
**************************************************************************
If you now run the game, your module will get loaded by Sider, and then
the "init" function will be called, so you should see a "Hello, world!"
message in sider.log.
If you made a mistake and your module has a syntax error, for example,
or some other problem, then you should see an error message in sider.log,
explaining where (on which line of the script) the problem occurred.
Let's now see how you can make a more useful module. First step for that
is to understand the context object (ctx).
3. Context object
~~~~~~~~~~~~~~~~~
The context object also contains a register function, which has the
following signature:
ctx.register(event_name, handler_function)
The idea of the context object is that in your handler functions, your
code will make decisions on what to do, using the information in the
context.
4. Supported events
~~~~~~~~~~~~~~~~~~~
This event occurs when the game needs to find out some information
about the file, specified by relative filename. Maybe the game needs
to create a buffer, or it needs to determine the filesize, or something
else. Your handler function is expected to return a string key that
will be used for caching of "livecpk_get_filepath". You can just
return filename as the key, without any changes, or return nil -
which will result in the same outcome. But sometimes, you need to make
a different key, because the context has some additional information
that is important.
IMPORTANT: This event can fire a lot of times for a given filename,
so try to avoid doing expensive computations in the handler function
or logging anything, because it may affect your frame rate.
This event fires after both home and away teams are determined -
either during the team selection in exhibition game, or when the next
match becomes known in a league or a cup mode (League, Master League,
UCL, Europa League, etc.)
The team ids are also set as "home_team" and "away_team" fields in
the context object so that they can be used later, if needed.
This event occurs, when the game sets the mach duration. If your handler
function returns an integer, then this value will be used as the match
time in minutes. This way you can accelerate or slow down the matches
beyound the allowed 5-30 minute range. See timeaccel.lua - for an example
of such script.
This event fires, when the game prepares to display the stadium image
or when it is entering pre-game menus of non-exhibition modes. In addition
to the actual id of the stadium chosen, the "stadium_id" parameter may have
the following special values:
253 : "home stadium"
254 : "random stadium"
You handler function can change it, if it returns an integer value instead of nil.
This integer value can either be a stadium id, or one of the following special
values, mentioned above.
NOTE: the final stadium selection isn't actually made, until after the
"set_stadium" event. So, if you want to change the stadium, or see what
was eventually chosen as random/home stadium, then you will need to also register
for the "set_stadium" event.
This event fires, when the stadium settings are chosen for the upcoming
match. The "options" parameter is a Lua table which contains the following
keys: "stadium", "timeofday", "weather", "weather_effects", "season".
Each of these has an integer value, as the game uses:
for stadium - it is the id of the stadium,
for timeofday: 0 - means Day, 1 - means Night;
for weather: 0 - Fine (sunny), 1 - Rain, 2 - Snow;
for weather_effects: 2 - means enforce falling rain/snow, other values - unknown
for season: 0 - Summer, 1 - Winter
You handler function can either return nil, which means that other modules
can receive the event and process it. Or, the handler can return an stadium
id - an integer - to switch the stadium to another one. Be careful though:
sider doesn't check for correctness of values, so if you switch to a
non-existent stadium, the game will probably crash or go into infinite
"loading" loop. For an example usage - see stadswitch.lua module.
You handler function can either return nil, which means that other modules
can receive the event and process it. Or, the handler can return a table
of options, which are either modified or not. Returning a table of options
stops further propagation of the event. You cannot change the stadium id -
for that use "set_stadium" event. But you can change any of the other
three settings: just assign them different values.
For an example usage - see stadswitch.lua module.
This event fires, when the game prepares to display the ball name.
Your handler function can change it, if it returns a string instead of nil.
The string needs to be in UTF-8 encoding to correctly render non-ASCII
characters.
This event fires, when the game prepares to display the stadium name.
You handler function can change it, if it returns a string instead of nil.
(The "stadium_id" parameter is provided to handler function only as additional
information - for which stadium the name is being read/modified)
The string needs to be in UTF-8 encoding to correctly render non-ASCII
characters.
This event fires before the game checks if trophy scenes need to be shown
before (and after) the match. This is a specialized event, and is probably
not very useful for modules other than "trophy.lua". The "trophy.lua"
uses to enforce trophy scenes from specific tournaments. This makes it
possible to have trophy celebrations for tournaments that do not have
them in the original game content. (See trophy.lua, if you are really
interested in how this works)
All return values can be nil, and also the handler may return three or two,
or one, or not return anything at all.
1st return value: text to display
2nd return value: full filename of the image to display
3rd return value: table with layout options
When the overlay is on, and the current Lua module is in control of the
overlay, this event fires once for each frame that is displayed by the game engine
(So, normally - 60 times per second). The returned string is what will be
displayed by the overlay. The logic that generates this string should not be
too heavy: too much processing may affect the frame rate.
See examples in modules: overdemo.lua and camera.lua
The text and image are displayed side by side: text - on the left,
image - on the right. The layout options table allows to specify some
formatting for the image. The following ones are supported:
Final dimensions of the image on screen are calculated using two of the three
options: "image_width", "image_height", "image_aspect_ratio" - in this order
of priority. If only 1 (or none of the three) are specified, then a default
width of 0.1 of total screen width is assumed, and the original aspect ratio
of the image is used to calculate the height.
When the overlay is on, and the current Lua module is in control of the
overlay, this event fires when user presses down any key on the keyboard. The
so-called "virtual key code" will be passed as the value of "vkey" parameter.
Your function can that take appropriate action, if it wants to react to
such key events. A combination of "overlay_on" and "key_down" events can
be used to build simple UIs in the overlay itself.
See example of such UI in camera.lua
When the overlay is on, and the current Lua module is in control of the
overlay, this event fires when user presses or releases a button, or moves
a stick or d-pad of the game controller. (If you have multiple controllers
attached, only one will generate these input events). The "inputs" parameter
is always a table containing at least one, but possibly more than one mapping
of: input-name --> input-value. The "input-name" is a symbolic name that
identifies the source of input: a button, stick, d-pad. See the following
table for all possible combinations:
If your module is registered for "key_down" event, but not for the
"gamepad_input" event, then sider will automatically map gamepad events
into keyboard events, and emit those, for such gamepad events that are
defined in the "global input mapping" configuration - in gamepad.ini.
If you do not want this automatic mapping, make sure to register for
the "gamepad_input" event in your module, and then do whatever is needed
with the inputs. (You may choose to completely ignore them too, if you
want. See example of that in camera.lua)
IMPORTANT NOTE: Some events can fire multiple times for the same "action".
That is normal, it's just how the game works internally. Make sure your
module logic can handle such situations correctly.
5. Logging
~~~~~~~~~~
6. Module environment
~~~~~~~~~~~~~~~~~~~~~
Standard Lua:
assert, ipairs, pairs, tostring, tonumber, table,
string, math, unpack, type, error, io, os, _VERSION, _G
Sider:
log, memory, _FILE
You can also enable "ffi" and "bit" modules, which are LuaJIT
extensions. By default, they are disabled. To enable, modify your
sider.ini like this:
luajit.ext.enabled = 1
By the way, your module can "investigate" and find out what exactly
is available for it to use - this is not hard, and is left as an
exercise for the reader ;-) Or... you can cheat, and look at env.lua
module.
7. Memory library
~~~~~~~~~~~~~~~~~
**********************************************************************
**********************************************************************
IMPORTANT WARNING: PLEASE USE THIS LIBRARY WITH CARE AND CAUTION,
AND IF AND ONLY IF YOU KNOW WHAT YOU'RE DOING. REALLY.
THESE ARE POWERFUL TOOLS, BUT THERE ARE ALSO DANGEROUS, BECAUSE
WRITING INTO A WRONG PLACE IN MEMORY CAN HAVE DISASTROUS CONSEQUENCES.
ALWAYS TRY TO HAVE A BACKUP COPY OF YOUR EDIT DATA AND SAVEGAME FILES.
**********************************************************************
**********************************************************************
memory.read(addr, n)
memory.write(addr, str)
This function writes the string of bytes (str) at the address (addr).
Return value: nil
This function searches for the string of bytes (str), in the range
of memory addresses between start_addr and end_addr.
Return value: address, at which the string of bytes was found
or nil, if the string was not found.
memory.pack(format, number)
This function converts a Lua number into one of the supported binary
formats (little endian). The "format" parameter is a string that should
have one of the following values:
"f" : 32-bit float,
"d" : 64-bit double-precision float,
"i64" : 64-bit signed integer,
"u64" : 64-bit unsigned integer,
"i32" : 32-bit signed integer,
"u32" : 32-bit unsigned integer,
"i16" : 16-bit signed integer,
"u16" : 16-bit unsigned integer
Return value: string of bytes, representing the number in the format
specified by the "format" parameter
memory.unpack(format, str)
This function converts a string of bytes (str) into a Lua number, using
the format parameter to interpret the binary spec. The same values are
supported for "format" param as in memory.pack function.
Return value: a Lua number, converted from binary representation
memory.hex(value)
local s = 'Football'
log(memory.hex(s)) --> prints "466f6f7462616c6c" in the log
local n = 12345
log(memory.hex(n)) --> prints "0x3039" in the log
memory.get_process_info()
memory.search_process(s)
8. Zlib library
~~~~~~~~~~~~~~~
zlib.compress(data)
Takes the string of data and compresses it.
Returns 2 values:
1) string with compressed data, or nil - if an error occured
2) nil, or string with error message, if something went wrong
zlib.uncompress(compressed_data, uncompressed_size)
zlib.pack(data)
zlib.unpack(data)