Extensions (New) - mkxp-z/mkxp-z GitHub Wiki

System

The System module contains functions and constants that don't fit in many other places. It's mostly OS and virtual filesystem access.

OS

  • .data_directory returns the assigned directory for saving data. This will be in %APPDATA% on Windows, $HOME/Application Support on macOS, or $HOME/.local/share on Linux.
  • .window_title/.window_title=(str: String) gets and sets the window's title to str respectively.
  • .launch(cmd: String[, args: Array[String]]) opens the path or link address with the user's default program. For instance, System.launch("https://www.google.com") would open Google in the default web browser, System.launch(System.data_directory) would open the game's save folder in the file explorer, and System.launch("log.txt") would open a plaintext file using whatever the user has assigned to open plaintext files with. On Windows and macOS you can also optionally supply args for programs. Raises an MKXPError if it fails for whatever reason.
  • .user_language returns the user's locale as a string (e.g. "en_US"). This might be empty on Linux.
  • .game_title returns the game's title.
  • .show_settings displays the settings (keybind) menu.

Device Battery

  • .power_state returns a hash containing three entries, all of them able to be nil:
    • :discharging (Boolean): whether the machine is connected to sufficient power
    • :percent (Integer): the remaining percentage of the battery
    • :seconds (Integer): the estimated remaining time for the battery in seconds

Telling Time

  • .uptime returns the amount of time passed since the application was started (in seconds). The value is a float with the precision of microseconds, and is also not affected by the system clock, so if you need to measure a span of time accurately and Time.now is not good enough, use this.

Platform Detection

  • .platform returns a string describing the platform mkxp-z is currently running on.
  • .is_windows?/.is_mac?/.is_linux? return true if mkxp-z was built for the respective operating system.
  • .is_rosetta? returns true if mkxp-z is being run over Rosetta 2 -- in other words, if it is being run as Intel code on an ARM Mac.
  • .is_wine? returns true if mkxp-z is being run on Linux or macOS through Wine.
  • .is_really_windows?/.is_really_mac?/.is_really_linux? are similar to the functions above, but unlike those, are not tricked by Wine, unless its exports are explicitly hidden. It may seem confusing (and part of that is due to the fact that I could have simplified this a lot), but .is_mac? would return false if the game was being run on a Mac through Wine. .is_really_mac? wouldn't.

The Path Cache

MKXP's classes do not directly use the native filesystem. Instead, by default, the game's directory is mounted to appear as the current directory (System.mount('.')), along with the game's archive if it has one (System.mount('Game.rgssad')), and any locations specified as RTPs in the configuration file.

If you wish to create Bitmaps, play audio, use load_data, etc. on files or archives not initially indicated to MKXP, or on files or archives that were not pre-existing at the time MKXP was started, these are for you.

Also makes for a good base for a mod system, as load_data now permits loading raw binary data without trying to de-Marshal it first.

  • .desensitize(path: String) uses MKXP's internal path cache to find the correct case for a filename. Useful for scripting filesystem operations on platforms with case-sensitive filesystems (Linux). Returns the input string if there was no match, or if the path cache is disabled.
  • .reload_cache causes MKXP to regenerate its path cache, reflecting changes made in the mounted filesystem since it was last called.
  • .mount(path: String[, mountpoint: String[, reload: Boolean = true]]) will mount path to mountpoint. Multiple locations can be mounted to the same mount point, and any archive formats you can use with the RTPs option in mkxp's json configuration can also be mounted as directories (e.g. zip, rgssad, iso). If mountpoint is not provided, it will default to the game's root, not the same path specified in path. Automatically reloads the path cache if reload is not specifically set false.
  • .unmount(path: String[, reload: Boolean = true]) removes path from MKXP's search path. This is the same path given to the first argument of mount, not the mount point. Automatically reloads the path cache if reload is not specifically set false.

CFG (Overriding, reading from and saving to the JSON)

mkxp-z supports having mkxp.json overriden per-user by a separate mkxp.json stored in the data directory (System.data_directory). Most of the settings can be changed this way, but there are some that can't:

  • The game's startup folder (gameFolder)
  • The game's data directory path (dataPathOrg, dataPathApp)
  • The game's icon (iconPath, this only works on Linux by the way)
  • The game executable's name (execName)
  • Whether or not to allow symbolic links (allowSymlinks)
  • Whether or not the path cache is enabled (pathCache)
  • All Ruby JIT settings
  • The RGSS version (rgssVersion)
  • The screen size (defScreenW, defScreenH)

Everything else is fair game.

In addition, mkxp-z allows interaction with its own configuration (through the JSON) with the CFG module. It's a little similar to the ENV module, only with less functions because I honestly didn't know that ENV had as many as it does.

The CFG module (currently) has three functions:

  • .[] looks up a value from the config. In order of priority, it will try:
    • the user mkxp.json, if it exists
    • the default mkxp.json in the game's directory, if it exists
    • program defaults
    • if a value cannot be found in any of these, the function returns nil
  • .[]=(key: String, val: Object) stores the value val into key. val can be any object mkxp-z's built-in JSON binding (the same as HTTPLite::JSON) can process. All changes are stored in the user configuration (the default one may be somewhere that is not writable, like Program Files on Windows, or in an undesirable location like the inside of the app bundle on macOS). Every change is written immediately.
  • .to_hash returns every recorded value in the config as a hash.

You can also store custom values freely. This way, you can finally store game values (like, most importantly, options) without having to rely on an annoying binary-only format like Marshal.

CFG["enableBlitting"] = true                 # Enable hardware blits on next reboot

CFG["someCustomSetting"] = "someCustomValue" # You can store your own data
CFG["someCustomHash"] = {"inner" => true}    # ...as anything HTTPLite::JSON works with

val = CFG["mightBeMissing"] || "default"     # Things return nil if not present, so you can one-line defaults
                                             # (be careful for types, though)

Win32API

It works.

It might not work the way you want it to, but it works.

Win32API was actually a really dead simple extension, so I wrote my own version of it directly into MKXP. It will run on all Ruby versions because of this.

I'll assume you already know how Win32API works, so I'll skip telling you that bit and stick to what's different.

  • Something extremely important to remember is that, in MKXP, the Ruby scripts are not run from the main thread. There are some functions that just don't work the way you expect (or at all) because of this -- Notably, anything involving window handling, generally.

  • If mkxp-z is built as a 64-bit application, 32-bit DLLs will not work. Judging by how hacky some RGSS 'libraries' tended to be though, this is probably a good thing, since they'd just crash the program anyway.

  • I've only checked that it works on x86_64 and ARM64 platforms (fastcalls, the compiler default) and 32-bit Windows (cdecl, stdcall). Anything else and you're probably on your own.
  • It runs on Linux and macOS. This doesn't mean you can run Windows libraries on those platforms, nor can you access any of the typical Windows APIs. This class isn't Wine. It does mean, though, that you can use libraries built for each respective platform (shared objects with linux, .dylibs and frameworks on macOS), so if you want to use libraries that are already cross-platform or you want to write your own, it should be fine.
  • B and b can be used to specify Boolean arguments.

  • On 64-bit platforms, I and i specifically refer to 32-bit integers. Use L and l for 64-bit ones (pointers)

HTTPLite

mkxp-z includes minimal support for communicating with HTTP servers. Support for unencrypted HTTP is always present; HTTPS requires building with the use_https=true option, and OpenSSL installed onto the system.

This is basically for anyone who ever thinks "Look, I just want to send a POST..."

JSON and HTTP support are built on top of json5pp and cpp-httplib respectively. All functions can raise MKXPError if something goes wrong.

JSON

  • HTTPLite::JSON.stringify(obj: Object) takes one object as an argument, transforming it into a JSON string. Accepts Nil, Float, Integer, String, Array, Boolean and Hash.
  • HTTPLite::JSON.parse(str: String) takes one JSON string as an argument, transforming it back into its Ruby equivalent.

HTTP

The HTTP methods all return a hash as a response. This hash contains 3 members (at the moment):

  • :status (Int) contains the response status (e.g. 200, 404)
  • :body (String) contains the body portion of the server's response.
  • :headers (String => String) contains a hash with all the headers contained in the server's response.

The HTTPLite module contains three functions. All return a hash formed in the way described above. They can all be optionally passed headers, as a hash with String values and String keys.

By default, these functions will attempt to follow redirect responses. This can be disabled by passing false to follow_redirects.

  • HTTPLite.get(url: String[, headers: String => String[, follow_redirects: Boolean = true]]) takes a url as a string, performing a GET request.
  • HTTPLite.post(url: String, stringhash: String=>String[, headers: String=> String[, follow_redirects: Boolean = true]]) takes a url as a string, and post data contained as a hash of string keys and string values, performing a POST request.
  • HTTPLite.post_body(url: String, bodycontent: String, contenttype: String[, headers: String => String[, follow_redirects: Boolean = true]]) takes a url as a string, the request's body as a string, and the body's MIME-type as a string, performing a POST request.
# Perform a GET request on any URL
response = HTTPLite.get("https://httpbin.org/encoding/utf8")
if response[:status] == 200 #OK
    p response[:body]
else
    p "You got something other than an OK: #{response[:status]}"
end

# Perform a POST request on any URL
postdata = {
    "key1" => "value1",
    "key2" => "value2"
}
response = HTTPLite.post("https://some.url", postdata)
# Perform a POST request on any URL (using a body as data)
postdata = HTTPLite::JSON.stringify(postdata)
response = HTTPLite.post_body("https://some.url", postdata, "application/json")

SteamLite

This module is only defined if the Steamworks SDK was configured while building. It's missing otherwise.

This is primarily just a built-in implementation of achievements, very similar to the SteamUserStatsLite script. Because MKXP is GPL licensed (and therefore isn't actually allowed to directly link to Steamworks, probably even using the aforementioned script), this functionality is implemented through Steamshim.

The only significant differences from SUSL should be:

  • Functions are accessed through the SteamLite module
  • get_stat_float and get_stat_int are now get_stat_f and get_stat_i respectively
  • the is_subscribed , is_dlc_present, update_avg_rate_stat , and get_achievement_display_info methods are not present (because Steamshim doesn't support them and I didn't feel like adding them)

It is a little simpler to use than SteamUserStatsLite due to initialization, shutdown, and updating being handled internally. Using it is as simple as:

SteamLite.set_achievement 'YOUR_ACH_ID_HERE'

# Send changes to server and display achievement notifications
SteamLite.store_stats

The Ruby Standard Library

If you built mkxp-z with Ruby 3 and using the build scripts, you have the option of using the Ruby stdlib in a completely static manner (besides the Ruby library itself). On macOS, the standard library is already included in the app bundle and in Ruby's load path by default. You can just skip to requireing things. On Linux and Windows, a folder containing it is given along with the release builds, but it is not in the load path by default. You have to add it yourself:

# Assuming the folder is in the same directory as the executable,
# and it's named "3.1.0"
$:.push(File.join(Dir.pwd, "3.1.0")) unless System.is_mac?

# Now you can require stuff in it:
require 'stringio'
require 'yaml'

Not everything is present, but a bunch of things are.

Also, any extension that is written entirely in C can be required without the rest of the standard library. socket and zlib (the latter of which is already imported by default) are examples of these.