diff --git a/.github/actions/restore_or_install_byond/action.yml b/.github/actions/restore_or_install_byond/action.yml index 4b07a612558..17905bb3f6b 100644 --- a/.github/actions/restore_or_install_byond/action.yml +++ b/.github/actions/restore_or_install_byond/action.yml @@ -29,6 +29,14 @@ runs: echo "BYOND_MAJOR=$BYOND_MAJOR" >> $GITHUB_ENV echo "BYOND_MINOR=$BYOND_MINOR" >> $GITHUB_ENV + # BYOND 516.1664 and later require libcurl on linux + - name: Install BYOND Dependencies + shell: bash + run: | + sudo dpkg --add-architecture i386 + sudo apt update + sudo apt install -y libcurl4 libcurl4:i386 + # The use of `actions/cache/restore` and `actions/cache/save` here is deliberate, as we want to # save the BYOND install to a cache as early as possible. If we used just `actions/cache`, it # would only attempt to save the cache at the end of a job. This ensures that if a workflow run diff --git a/.github/actions/setup_node/action.yml b/.github/actions/setup_node/action.yml index 120dbf4639b..d017766117f 100644 --- a/.github/actions/setup_node/action.yml +++ b/.github/actions/setup_node/action.yml @@ -5,7 +5,7 @@ description: Install Node using the version specified in `dependencies.sh`; addi inputs: restore-yarn-cache: - description: 'If `true`, restores the Yarn cache alongside installing node.' + description: "If `true`, restores the Yarn cache alongside installing node." required: false type: boolean default: false diff --git a/code/__defines/rust_g.dm b/code/__defines/rust_g.dm index f332db15511..1941b279e37 100644 --- a/code/__defines/rust_g.dm +++ b/code/__defines/rust_g.dm @@ -19,21 +19,25 @@ /* This comment bypasses grep checks */ /var/__rust_g /proc/__detect_rust_g() - if (world.system_type == UNIX) - if (fexists("./librust_g.so")) - // No need for LD_LIBRARY_PATH badness. - return __rust_g = "./librust_g.so" - else if (fexists("./rust_g")) - // Old dumb filename. - return __rust_g = "./rust_g" - else if (fexists("[world.GetConfig("env", "HOME")]/.byond/bin/rust_g")) - // Old dumb filename in `~/.byond/bin`. - return __rust_g = "rust_g" - else - // It's not in the current directory, so try others - return __rust_g = "librust_g.so" + var/arch_suffix = null + #ifdef OPENDREAM + arch_suffix = "64" + #endif + if (world.system_type == UNIX) + if (fexists("./librust_g[arch_suffix].so")) + // No need for LD_LIBRARY_PATH badness. + return __rust_g = "./librust_g[arch_suffix].so" + else if (fexists("./rust_g[arch_suffix]")) + // Old dumb filename. + return __rust_g = "./rust_g[arch_suffix]" + else if (fexists("[world.GetConfig("env", "HOME")]/.byond/bin/rust_g[arch_suffix]")) + // Old dumb filename in `~/.byond/bin`. + return __rust_g = "rust_g[arch_suffix]" else - return __rust_g = "rust_g" + // It's not in the current directory, so try others + return __rust_g = "librust_g[arch_suffix].so" + else + return __rust_g = "rust_g[arch_suffix]" #define RUST_G (__rust_g || __detect_rust_g()) #endif @@ -105,7 +109,7 @@ * * height: The height of the grid. */ #define rustg_cnoise_generate(percentage, smoothing_iterations, birth_limit, death_limit, width, height) \ - RUSTG_CALL(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height) + RUSTG_CALL(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height) /** * This proc generates a grid of perlin-like noise @@ -121,7 +125,7 @@ * * upper_range: upper bound of values selected for. (exclusive) */ #define rustg_dbp_generate(seed, accuracy, stamp_size, world_size, lower_range, upper_range) \ - RUSTG_CALL(RUST_G, "dbp_generate")(seed, accuracy, stamp_size, world_size, lower_range, upper_range) + RUSTG_CALL(RUST_G, "dbp_generate")(seed, accuracy, stamp_size, world_size, lower_range, upper_range) #define rustg_dmi_strip_metadata(fname) RUSTG_CALL(RUST_G, "dmi_strip_metadata")(fname) @@ -142,8 +146,8 @@ #define rustg_file_seek_line(fname, line) RUSTG_CALL(RUST_G, "file_seek_line")(fname, "[line]") #ifdef RUSTG_OVERRIDE_BUILTINS - #define file2text(fname) rustg_file_read("[fname]") - #define text2file(text, fname) rustg_file_append(text, "[fname]") + #define file2text(fname) rustg_file_read("[fname]") + #define text2file(text, fname) rustg_file_append(text, "[fname]") #endif /// Returns the git hash of the given revision, ex. "HEAD". @@ -154,7 +158,7 @@ * Defaults to returning %F which is YYYY-MM-DD. */ /proc/rustg_git_commit_date(rev, format = "%F") - return RUSTG_CALL(RUST_G, "rg_git_commit_date")(rev, format) + return RUSTG_CALL(RUST_G, "rg_git_commit_date")(rev, format) /** * Returns the formatted datetime string of HEAD using the provided format. @@ -162,7 +166,7 @@ * This is different to rustg_git_commit_date because it only needs the logs directory. */ /proc/rustg_git_commit_date_head(format = "%F") - return RUSTG_CALL(RUST_G, "rg_git_commit_date_head")(format) + return RUSTG_CALL(RUST_G, "rg_git_commit_date_head")(format) #define rustg_hash_string(algorithm, text) RUSTG_CALL(RUST_G, "hash_string")(algorithm, text) #define rustg_hash_file(algorithm, fname) RUSTG_CALL(RUST_G, "hash_file")(algorithm, fname) @@ -182,7 +186,7 @@ #define rustg_decode_base64(str) RUSTG_CALL(RUST_G, "decode_base64")(str) #ifdef RUSTG_OVERRIDE_BUILTINS - #define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing)) + #define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing)) #endif #define RUSTG_HTTP_METHOD_GET "get" @@ -200,7 +204,7 @@ /// All icons have the same y coordinate, and their x coordinate is equal to `icon_width * position`. /// /// hash_icons is a boolean (0 or 1), and determines if the generator will spend time creating hashes for the output field dmi_hashes. -/// These hashes can be helpful for 'smart' caching (see rustg_iconforge_cache_valid), but require extra computation. +/// These hashes can be heplful for 'smart' caching (see rustg_iconforge_cache_valid), but require extra computation. /// /// Spritesheet will contain all sprites listed within "sprites". /// "sprites" format: @@ -249,6 +253,20 @@ #define rustg_iconforge_cache_valid(input_hash, dmi_hashes, sprites) RUSTG_CALL(RUST_G, "iconforge_cache_valid")(input_hash, dmi_hashes, sprites) /// Returns a job_id for use with rustg_iconforge_check() #define rustg_iconforge_cache_valid_async(input_hash, dmi_hashes, sprites) RUSTG_CALL(RUST_G, "iconforge_cache_valid_async")(input_hash, dmi_hashes, sprites) +/// Provided a /datum/greyscale_config typepath, JSON string containing the greyscale config, and path to a DMI file containing the base icons, +/// Loads that config into memory for later use by rustg_iconforge_gags(). The config_path is the unique identifier used later. +/// JSON Config schema: https://hackmd.io/@tgstation/GAGS-Layer-Types +/// Unsupported features: color_matrix layer type, 'or' blend_mode. May not have BYOND parity with animated icons or varying dirs between layers. +/// Returns "OK" if successful, otherwise, returns a string containing the error. +#define rustg_iconforge_load_gags_config(config_path, config_json, config_icon_path) RUSTG_CALL(RUST_G, "iconforge_load_gags_config")("[config_path]", config_json, config_icon_path) +/// Given a config_path (previously loaded by rustg_iconforge_load_gags_config), and a string of hex colors formatted as "#ff00ff#ffaa00" +/// Outputs a DMI containing all of the states within the config JSON to output_dmi_path, creating any directories leading up to it if necessary. +/// Returns "OK" if successful, otherwise, returns a string containing the error. +#define rustg_iconforge_gags(config_path, colors, output_dmi_path) RUSTG_CALL(RUST_G, "iconforge_gags")("[config_path]", colors, output_dmi_path) +/// Returns a job_id for use with rustg_iconforge_check() +#define rustg_iconforge_load_gags_config_async(config_path, config_json, config_icon_path) RUSTG_CALL(RUST_G, "iconforge_load_gags_config_async")("[config_path]", config_json, config_icon_path) +/// Returns a job_id for use with rustg_iconforge_check() +#define rustg_iconforge_gags_async(config_path, colors, output_dmi_path) RUSTG_CALL(RUST_G, "iconforge_gags_async")("[config_path]", colors, output_dmi_path) #define RUSTG_ICONFORGE_BLEND_COLOR "BlendColor" #define RUSTG_ICONFORGE_BLEND_ICON "BlendIcon" @@ -270,26 +288,26 @@ * Generates a 2D poisson disk distribution ('blue noise'), which is relatively uniform. * * params: - * `seed`: str - * `width`: int, width of the noisemap (see world.maxx) - * `length`: int, height of the noisemap (see world.maxy) - * `radius`: int, distance between points on the noisemap + * `seed`: str + * `width`: int, width of the noisemap (see world.maxx) + * `length`: int, height of the noisemap (see world.maxy) + * `radius`: int, distance between points on the noisemap * * returns: - * a width*length length string of 1s and 0s representing a 2D poisson sample collapsed into a 1D string + * a width*length length string of 1s and 0s representing a 2D poisson sample collapsed into a 1D string */ #define rustg_noise_poisson_map(seed, width, length, radius) RUSTG_CALL(RUST_G, "noise_poisson_map")(seed, width, length, radius) /** * Register a list of nodes into a rust library. This list of nodes must have been serialized in a json. * Node {// Index of this node in the list of nodes - * unique_id: usize, - * // Position of the node in byond - * x: usize, - * y: usize, - * z: usize, - * // Indexes of nodes connected to this one - * connected_nodes_id: Vec} + * unique_id: usize, + * // Position of the node in byond + * x: usize, + * y: usize, + * z: usize, + * // Indexes of nodes connected to this one + * connected_nodes_id: Vec} * It is important that the node with the unique_id 0 is the first in the json, unique_id 1 right after that, etc. * It is also important that all unique ids follow. {0, 1, 2, 4} is not a correct list and the registering will fail * Nodes should not link across z levels. @@ -313,53 +331,6 @@ */ #define rustg_generate_path_astar(start_node_id, goal_node_id) RUSTG_CALL(RUST_G, "generate_path_astar")("[start_node_id]", "[goal_node_id]") -#define RUSTG_REDIS_ERROR_CHANNEL "RUSTG_REDIS_ERROR_CHANNEL" - -#define rustg_redis_connect(addr) RUSTG_CALL(RUST_G, "redis_connect")(addr) -/proc/rustg_redis_disconnect() return RUSTG_CALL(RUST_G, "redis_disconnect")() -#define rustg_redis_subscribe(channel) RUSTG_CALL(RUST_G, "redis_subscribe")(channel) -/proc/rustg_redis_get_messages() return RUSTG_CALL(RUST_G, "redis_get_messages")() -#define rustg_redis_publish(channel, message) RUSTG_CALL(RUST_G, "redis_publish")(channel, message) - -/** - * Connects to a given redis server. - * - * Arguments: - * * addr - The address of the server, for example "redis://127.0.0.1/" - */ -#define rustg_redis_connect_rq(addr) RUSTG_CALL(RUST_G, "redis_connect_rq")(addr) -/** - * Disconnects from a previously connected redis server - */ -/proc/rustg_redis_disconnect_rq() return RUSTG_CALL(RUST_G, "redis_disconnect_rq")() -/** - * https://redis.io/commands/lpush/ - * - * Arguments - * * key (string) - The key to use - * * elements (list) - The elements to push, use a list even if there's only one element. - */ -#define rustg_redis_lpush(key, elements) RUSTG_CALL(RUST_G, "redis_lpush")(key, json_encode(elements)) -/** - * https://redis.io/commands/lrange/ - * - * Arguments - * * key (string) - The key to use - * * start (string) - The zero-based index to start retrieving at - * * stop (string) - The zero-based index to stop retrieving at (inclusive) - */ -#define rustg_redis_lrange(key, start, stop) RUSTG_CALL(RUST_G, "redis_lrange")(key, start, stop) -/** - * https://redis.io/commands/lpop/ - * - * Arguments - * * key (string) - The key to use - * * count (string|null) - The amount to pop off the list, pass null to omit (thus just 1) - * - * Note: `count` was added in Redis version 6.2.0 - */ -#define rustg_redis_lpop(key, count) RUSTG_CALL(RUST_G, "redis_lpop")(key, count) - /* * Takes in a string and json_encode()"d lists to produce a sanitized string. * This function operates on whitelists, there is currently no way to blacklist. @@ -372,33 +343,33 @@ /// Provided a static RSC file path or a raw text file path, returns the duration of the file in deciseconds as a float. /proc/rustg_sound_length(file_path) - var/static/list/sound_cache - if(isnull(sound_cache)) - sound_cache = list() + var/static/list/sound_cache + if(isnull(sound_cache)) + sound_cache = list() + . = 0 + + if(!istext(file_path)) + if(!isfile(file_path)) + CRASH("rustg_sound_length error: Passed non-text object") + + if(length("[file_path]")) // Runtime generated RSC references stringify into 0-length strings. + file_path = "[file_path]" + else + CRASH("rustg_sound_length does not support non-static file refs.") + + var/cached_length = sound_cache[file_path] + if(!isnull(cached_length)) + return cached_length + + var/ret = RUSTG_CALL(RUST_G, "sound_len")(file_path) + var/as_num = text2num(ret) + if(isnull(ret)) . = 0 + CRASH("rustg_sound_length error: [ret]") - if(!istext(file_path)) - if(!isfile(file_path)) - CRASH("rustg_sound_length error: Passed non-text object") - - if(length("[file_path]")) // Runtime generated RSC references stringify into 0-length strings. - file_path = "[file_path]" - else - CRASH("rustg_sound_length does not support non-static file refs.") - - var/cached_length = sound_cache[file_path] - if(!isnull(cached_length)) - return cached_length - - var/ret = RUSTG_CALL(RUST_G, "sound_len")(file_path) - var/as_num = text2num(ret) - if(isnull(ret)) - . = 0 - CRASH("rustg_sound_length error: [ret]") - - sound_cache[file_path] = as_num - return as_num + sound_cache[file_path] = as_num + return as_num #define RUSTG_SOUNDLEN_SUCCESSES "successes" @@ -424,51 +395,40 @@ #define rustg_time_milliseconds(id) text2num(RUSTG_CALL(RUST_G, "time_milliseconds")(id)) #define rustg_time_reset(id) RUSTG_CALL(RUST_G, "time_reset")(id) +/// Returns the current timestamp (in local time), formatted with the given format string. +/// See https://docs.rs/chrono/latest/chrono/format/strftime/index.html for documentation on the formatting syntax. +#define rustg_formatted_timestamp(format) RUSTG_CALL(RUST_G, "formatted_timestamp")(format) + +/// Returns the current timestamp (with the given UTC offset in hours), formatted with the given format string. +/// See https://docs.rs/chrono/latest/chrono/format/strftime/index.html for documentation on the formatting syntax. +#define rustg_formatted_timestamp_tz(format, offset) RUSTG_CALL(RUST_G, "formatted_timestamp")(format, offset) + /// Returns the timestamp as a string /proc/rustg_unix_timestamp() - return RUSTG_CALL(RUST_G, "unix_timestamp")() + return RUSTG_CALL(RUST_G, "unix_timestamp")() #define rustg_raw_read_toml_file(path) json_decode(RUSTG_CALL(RUST_G, "toml_file_to_json")(path) || "null") /proc/rustg_read_toml_file(path) - var/list/output = rustg_raw_read_toml_file(path) - if (output["success"]) - return json_decode(output["content"]) - else - CRASH(output["content"]) + var/list/output = rustg_raw_read_toml_file(path) + if (output["success"]) + return json_decode(output["content"]) + else + CRASH(output["content"]) #define rustg_raw_toml_encode(value) json_decode(RUSTG_CALL(RUST_G, "toml_encode")(json_encode(value))) /proc/rustg_toml_encode(value) - var/list/output = rustg_raw_toml_encode(value) - if (output["success"]) - return output["content"] - else - CRASH(output["content"]) - -#define rustg_unzip_download_async(url, unzip_directory) RUSTG_CALL(RUST_G, "unzip_download_async")(url, unzip_directory) -#define rustg_unzip_check(job_id) RUSTG_CALL(RUST_G, "unzip_check")("[job_id]") + var/list/output = rustg_raw_toml_encode(value) + if (output["success"]) + return output["content"] + else + CRASH(output["content"]) #define rustg_url_encode(text) RUSTG_CALL(RUST_G, "url_encode")("[text]") #define rustg_url_decode(text) RUSTG_CALL(RUST_G, "url_decode")(text) #ifdef RUSTG_OVERRIDE_BUILTINS - #define url_encode(text) rustg_url_encode(text) - #define url_decode(text) rustg_url_decode(text) + #define url_encode(text) rustg_url_encode(text) + #define url_decode(text) rustg_url_decode(text) #endif - -/** - * This proc generates a noise grid using worley noise algorithm - * - * Returns a single string that goes row by row, with values of 1 representing an alive cell, and a value of 0 representing a dead cell. - * - * Arguments: - * * region_size: The size of regions - * * threshold: the value that determines wether a cell is dead or alive - * * node_per_region_chance: chance of a node existiing in a region - * * size: size of the returned grid - * * node_min: minimum amount of nodes in a region (after the node_per_region_chance is applied) - * * node_max: maximum amount of nodes in a region - */ -#define rustg_worley_generate(region_size, threshold, node_per_region_chance, size, node_min, node_max) \ - RUSTG_CALL(RUST_G, "worley_generate")(region_size, threshold, node_per_region_chance, size, node_min, node_max) diff --git a/dependencies.sh b/dependencies.sh index 6a93e6bb82c..2adb65d031d 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -5,13 +5,13 @@ # byond version export BYOND_MAJOR=516 -export BYOND_MINOR=1662 +export BYOND_MINOR=1664 # Macro Count export MACRO_COUNT=8 #rust_g git tag -export RUST_G_VERSION=3.8.0 +export RUST_G_VERSION=3.11.0 #node version export NODE_VERSION_LTS=22.14.0 diff --git a/librust_g.so b/librust_g.so old mode 100755 new mode 100644 index c12a7196a82..926a50a7f53 Binary files a/librust_g.so and b/librust_g.so differ diff --git a/libvchatlog.so b/libvchatlog.so index a427899fa8a..a0b04f523e3 100755 Binary files a/libvchatlog.so and b/libvchatlog.so differ diff --git a/libverdigris.so b/libverdigris.so index eff1cbc1dee..c6f19aacbc9 100644 Binary files a/libverdigris.so and b/libverdigris.so differ diff --git a/rust_g.dll b/rust_g.dll index adc46f26599..03b24ab677e 100644 Binary files a/rust_g.dll and b/rust_g.dll differ diff --git a/tools/ci/install_byond.sh b/tools/ci/install_byond.sh index 0e89e1b9184..1737444dc8b 100755 --- a/tools/ci/install_byond.sh +++ b/tools/ci/install_byond.sh @@ -12,6 +12,15 @@ else mkdir -p "$HOME/BYOND" cd "$HOME/BYOND" curl -H "User-Agent: vstation/1.0 CI Script" "http://www.byond.com/download/build/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -o byond.zip + if [ $? -ne 0 ] || !(unzip -qt byond.zip); then + echo "Attempting fallback mirror..." + rm byond.zip + curl -H "User-Agent: vstation/1.0 CI Script" "https://spacestation13.github.io/byond-builds/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -o byond.zip + if [ $? -ne 0 ] || !(unzip -qt byond.zip); then + echo "Failure!" + exit 1 + fi + fi unzip byond.zip rm byond.zip cd byond diff --git a/vchatlog.dll b/vchatlog.dll index 91f1071a1d2..d43fbb74821 100644 Binary files a/vchatlog.dll and b/vchatlog.dll differ diff --git a/verdigris.dll b/verdigris.dll index 489b9e11ccd..54df9f49713 100644 Binary files a/verdigris.dll and b/verdigris.dll differ diff --git a/verdigris/Cargo.toml b/verdigris/Cargo.toml index 90aa612ad50..1d9df52e4dd 100644 --- a/verdigris/Cargo.toml +++ b/verdigris/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "verdigris" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] crate-type = ["cdylib"] @@ -18,7 +18,7 @@ opt-level = 0 [dependencies] const_format = "0.2.33" eyre = "0.6.12" -meowtonin = { git = "https://github.com/Absolucy/meowtonin.git" } +meowtonin = { git = "https://github.com/Absolucy/meowtonin.git", branch = "516", features = ["byond-1664"] } rand = "0.8.5" rayon = "1.10.0"