Skip to content

[main/2026-10-08-1819] get_shader_colour_from_arrays:364 #1631

Open

Description

@lyra-the-bot

Your game just encountered an error!

System Details:

  • Date-Time: 8-10-2026 17:04:08
  • Game Version: main
  • Build Date: 2026-10-08-1819
  • Commit Hash: 0f17fbd

Save Details:

  • Chapter Name: None
  • Current Turn: ???
  • Game Seed: 0

Error Details:

{ line : 0, stacktrace : [ "gml_Script_get_shader_colour_from_arrays (line 357)","gml_Script_set_complex_shader_area (line 378)","gml_Script_setup_complex_livery_shader (line 298)","gml_Script_draw@anon@58984@ComplexSet@scr_ComplexSet (line 1541)","gml_Script_scr_draw_unit_image (line 408)","gml_Script_anon@19276@draw_base@anon@19228@ColourItem@ColourItem (line 746)","gml_Script_draw@anon@2143@DataSlate@scr_DataSlate (line 129)","gml_Script_draw_base@anon@19228@ColourItem@ColourItem (line 778)","gml_Script_scr_livery_setup (line 24)","gml_Object_obj_creation_Draw_0 (line 131)" ], script : "gml_Script_get_shader_colour_from_arrays", message : "I32 argument is undefined", longMessage : "ERROR in action number 1
of Draw Event for object obj_creation:
I32 argument is undefined" }

Stacktrace:

  • get_shader_colour_from_arrays:364
  • set_complex_shader_area:379
  • setup_complex_livery_shader:299
  • scr_ComplexSet:draw:1542
  • scr_draw_unit_image:409
  • ColourItem:anonymous:747
  • scr_DataSlate:draw:130
  • ColourItem:draw_base:779
  • scr_livery_setup:25
  • obj_creation_Draw_0:132

Reporter:

M60Wa

User Message:

hovering mouse over sapce marine armor customization

Activity

  1. added theissue type on Oct 8, 2026
  2. lyra-cactus commented on Oct 8, 2026

    @lyra-cactus
    Contributor

    Investigation Report - Issue #1631: get_shader_colour_from_arrays - "I32 argument is undefined"

    1. Summary

    Crash report from build main/2026-10-08-1819 (commit 0f17fbd34), reporter M60Wa:

    hovering mouse over space marine armor customization

    Error:

    gml_Script_get_shader_colour_from_arrays: "I32 argument is undefined"
    

    Stack (build line numbers, each is current main + 1):

    - get_shader_colour_from_arrays:364
    - set_complex_shader_area:379
    - setup_complex_livery_shader:299
    - scr_ComplexSet:draw:1542
    - scr_draw_unit_image:409
    - ColourItem:anonymous:747
    - scr_DataSlate:draw:130
    - ColourItem:draw_base:779
    - scr_livery_setup:25
    - obj_creation_Draw_0:132
    

    Root cause: setup_complex_livery_shader() iterates over every key in the livery struct and passes the raw value straight to set_complex_shader_area() -> get_shader_colour_from_arrays(), which indexes col_r[colour] / col_g[colour] / col_b[colour] with no validation. Any undefined, -1 sentinel, bool metadata flag, non-shader key (is_changed, block_company_colours, company_marks_loc), out-of-range int, or icon entry with missing .colour triggers arr[undefined] / arr[-1] / arr[out_of_range] inside the try block. The try/catch then calls ERROR_HANDLER.assert_popup(_exception), which is what generated this exact issue (callstack captured via debug_get_callstack, not an uncaught crash).

    Files:

    2. Full call-chain walkthrough

    2.1 obj_creation_Draw_0 -> scr_livery_setup

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/objects/obj_creation/Draw_0.gml#L129-L132

    } else if (slide == eCREATION_SLIDES.CHAPTERLIVERY) {
        /* Livery, Roles */
        scr_livery_setup();
    }

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_livery_setup/scr_livery_setup.gml#L24

    livery_picker.draw_base();

    2.2 ColourItem.draw_base -> DataSlate.draw -> inside_method -> dummy_marine.draw_unit_image()

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L650-L779

    draw_base sets data_slate.inside_method then calls:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L778

    data_slate.draw(0, 5, 0.45, 1);

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_DataSlate/scr_DataSlate.gml#L128-L130

    if (is_callable(inside_method)) {
        inside_method();
    }

    Inside inside_method, the preview marine is (re)drawn:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L742-L748

    if (dummy_marine ?? true) {
        dummy_marine = new DummyMarine();
    }
    if (!is_struct(dummy_image)) {
        dummy_image = dummy_marine.draw_unit_image();
    }

    DummyMarine is defined here:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_culture_visuals/scr_culture_visuals.gml#L2180-L2196

    function DummyMarine() constructor {
        // ...
        personal_livery = {};
        // ...
        static draw_unit_image = scr_draw_unit_image;

    2.3 scr_draw_unit_image -> ComplexSet.draw

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_draw_unit_image/scr_draw_unit_image.gml#L404-L408

    if (complex_livery) {
        if (struct_exists(complex_set, "armour")) {
            complex_set.x_surface_offset = x_surface_offset;
            complex_set.y_surface_offset = y_surface_offset;
            complex_set.draw();

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_ComplexSet/scr_ComplexSet.gml#L1535-L1542

    static draw = function() {
        // ...
        var _texture_draws = setup_complex_livery_shader(draw_unit.role(), draw_unit);

    draw_unit here is the DummyMarine instance, so unit != "none" is true.

    2.4 setup_complex_livery_shader - crash site

    Creation-screen branch:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L51-L68

    if (_in_creation) {
        _data_set = variable_clone(obj_creation.livery_picker.map_colour);
        if (obj_creation.livery_selection_options.current_selection == 2) {
            var _base = obj_creation.full_liveries[0];
            // fill missing / -1 entries from base
        }
    }

    Personal-livery overlay (empty {} for DummyMarine unless Deathwatch, but real marines can inject anything):

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L96-L102

    if (_is_unit) {
        var _names = struct_get_names(unit.personal_livery);
        for (var i = 0; i < array_length(_names); i++) {
            var _area = _names[i];
            _data_set[$ _area] = unit.personal_livery[$ _area];
        }
    }

    Main loop - no filtering, no -1 guard:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L292-L298

    for (var i = 0; i < _position_count; i++) {
        var _colour_position = spot_names[i];
        var _colour = variable_clone(_data_set[$ _colour_position]);
        if (!is_array(_colour)) {
            set_complex_shader_area(_colour_position, _colour);

    Icon branch - no .colour existence check:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L313-L342

    } else if (_colour[0] == "icon") {
        var _data = _colour[1];
        // ...
        set_complex_shader_area(_colour_position, _data.colour);
    }

    2.5 set_complex_shader_area -> get_shader_colour_from_arrays

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L369-L381

    function set_complex_shader_area(area, colour) {
        var _colour_set = [];
        if (is_array(area)) {
            for (var i = 0; i < array_length(area); i++) {
                var small_area = area[i];
                _colour_set = get_shader_colour_from_arrays(colour);
                shader_set_uniform_f_array(shader_get_uniform(full_livery_shader, small_area), _colour_set);
            }
        } else {
            _colour_set = get_shader_colour_from_arrays(colour);
            shader_set_uniform_f_array(shader_get_uniform(full_livery_shader, area), _colour_set);
        }
    }

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L349-L367

    function get_shader_colour_from_arrays(colour) {
        var _colours_instance = active_game_controller();
        var _colour_set = [0, 0, 0];
        try {
            _colour_set = [
                _colours_instance.col_r[colour] / 255,
                _colours_instance.col_g[colour] / 255,
                _colours_instance.col_b[colour] / 255,
            ];
        } catch (_exception) {
            ERROR_HANDLER.assert_popup(_exception);
        }
        return _colour_set;
    }

    active_game_controller() is:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_controller_helpers/scr_controller_helpers.gml#L3-L5

    function active_game_controller() {
        return instance_exists(obj_creation) ? obj_creation : obj_controller;
    }

    col_r/col_g/col_b are built here (40 entries, indices 0..39):

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_colors_initialize/scr_colors_initialize.gml#L44-L294

    Enum:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_colors_initialize/scr_colors_initialize.gml#L1-L42

    obj_creation arrays are initialised here:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/objects/obj_creation/Create_0.gml#L568-L573

    col = [];
    col_r = [];
    col_g = [];
    col_b = [];
    scr_colors_initialize();

    ERROR_HANDLER.assert_popup captures debug_get_callstack() and files the report, which is why the issue stack starts inside get_shader_colour_from_arrays:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ErrorHandler/ErrorHandler.gml#L169-L178

    3. Root cause - unvalidated colour reaches col_r[colour]

    col_r[colour] throws I32 argument is undefined exactly when colour itself is undefined. The code has at least four reachable ways to produce that (plus the -1/out-of-range near-miss that the same guard must fix):

    3.1 Primary: no -1 / metadata / unknown-key filtering in main loop

    map_colour (aka scr_unit_draw_data) contains non-colour entries:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L186-L222

    map_colour = {
        is_changed: false,
        block_company_colours: false,
        left_leg_lower: default_val,
        // ... all areas ...
        company_marks: default_val,
        company_marks_loc: default_val,
    };

    The shader only defines these uniforms (no is_changed, block_company_colours, company_marks_loc, no main_colour setter either):

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/shaders/full_livery_shader/full_livery_shader.fsh#L1-L31

    The cloth-variation collector explicitly skips -1 and arrays:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L115-L126

    var _colour = _data_set[$ spot_names[i]];
    if (_colour == -1 || is_array(_colour)) {
        continue;
    }

    ...but the main shader loop does not. It also does not skip is_changed / block_company_colours / company_marks_loc. Consequences:

    3.2 Icon entries with missing .colour

    Valid icon payload (only example in repo):

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/datafiles/main/chapters/7.JSON#L233

    "left_pauldron" : [ "icon",{ "icon" : "ultramarines", "colour" : 34, "type" : 2 } ],

    Writer:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourPicker/ColourPicker.gml#L203-L211

    chosen = ["icon", {icon: tex_data[1], colour: base_colour, type: markings_options.current_selection}];

    Reader does not verify _data is a struct or that .colour exists before dereferencing. An old save, hand-edited JSON, or truncated ["icon"] array makes _data.colour == undefined, and set_complex_shader_area(pos, undefined) -> col_r[undefined] -> exact reported error.

    3.3 Hover preview mutates the live map_colour

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L651-L671

    colour_return = [hover_pos, map_colour[$ hover_pos]];
    map_colour[$ hover_pos] = 0;

    and restore:

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L770-L776

    if (colour_return != false) {
        if (hover_pos != colour_return[0]) {
            map_colour[$ colour_return[0]] = colour_return[1];

    If hover_pos ever names a key absent from map_colour (new image_location_maps entry, renamed area, company_marks_loc confusion), map_colour[$ hover_pos] reads undefined, the struct gains an undefined value, variable_clone preserves it, spot_names includes it, and the next ComplexSet.draw in the same frame crashes. This matches "hovering mouse over armor customization" perfectly: every hover forces reset_image() -> immediate draw_unit_image() -> setup_complex_livery_shader() on the mutated struct.

    Hover areas vs livery keys drift independently:

    3.4 personal_livery overlay can inject undefined

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L96-L102

    blindly copies every personal_livery value over the base. A modded/legacy personal_livery entry with undefined overwrites a valid colour. DummyMarine uses {} (safe), real marines use e.g. irandom(30) (safe today):

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_culture_visuals/scr_culture_visuals.gml#L2187-L2190

    but there is no contract enforcement.

    Why the game shows a popup instead of hard-crashing

    The try/catch in get_shader_colour_from_arrays catches the indexing error and forwards the exception object to assert_popup, which builds the GitHub issue from debug_get_callstack(). So the report is the catch handler firing. The returned colour stays black [0,0,0], and shader_set_uniform_f_array(-1_or_valid, black) continues. Repeated hovers = repeated popups.

    4. Proposed fixes (in priority order)

    All line refs below are current main.

    Fix 1 (required): validate in get_shader_colour_from_arrays + set_complex_shader_area

    Never index col_r/col_g/col_b with undefined, -1, non-numeric, or out-of-range. Fail loud in debug, fall back to a safe colour at runtime.

    Location: https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L349-L381

    Suggested shape (idiomatic GML, ASCII only):

    function get_shader_colour_from_arrays(colour) {
        var _colours_instance = active_game_controller();
        var _fallback = [0, 0, 0];
        if (!is_numeric(colour)) {
            return _fallback;
        }
        var _idx = floor(colour);
        if (_idx < 0 || _idx >= array_length(_colours_instance.col_r)) {
            return _fallback;
        }
        try {
            return [
                _colours_instance.col_r[_idx] / 255,
                _colours_instance.col_g[_idx] / 255,
                _colours_instance.col_b[_idx] / 255,
            ];
        } catch (_exception) {
            ERROR_HANDLER.assert_popup(_exception);
        }
        return _fallback;
    }

    And in set_complex_shader_area, early-exit when the uniform does not exist (shader_get_uniform returns -1) so is_changed / block_company_colours / company_marks_loc stop issuing invalid uniform writes every frame:

    function set_complex_shader_area(area, colour) {
        if (is_array(area)) {
            for (var i = 0; i < array_length(area); i++) {
                var _handle = shader_get_uniform(full_livery_shader, area[i]);
                if (_handle == -1) {
                    continue;
                }
                shader_set_uniform_f_array(_handle, get_shader_colour_from_arrays(colour));
            }
        } else {
            var _handle = shader_get_uniform(full_livery_shader, area);
            if (_handle == -1) {
                return;
            }
            shader_set_uniform_f_array(_handle, get_shader_colour_from_arrays(colour));
        }
    }

    Fix 2 (required): iterate only real shader areas, skip sentinels

    In setup_complex_livery_shader, replace the blind struct_get_names(_data_set) loop with an allowlist derived from complex_colour_swaps (already static in the same function):

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L142-L288

    if (!struct_exists(complex_colour_swaps, _colour_position)) {
        continue; // skips is_changed, block_company_colours, company_marks_loc, stale keys
    }
    var _colour = variable_clone(_data_set[$ _colour_position]);
    if (_colour == -1) {
        continue; // unpainted company slot; keep current uniform / default
    }
    if (is_undefined(_colour)) {
        continue;
    }

    Apply the same -1/undefined guard already used by the cloth collector at #L120 to the main loop. Optionally share one is_valid_livery_colour() helper.

    Fix 3 (required): guard the icon branch

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_complex_colour_kit/scr_complex_colour_kit.gml#L313-L342

    } else if (_colour[0] == "icon") {
        if (array_length(_colour) < 2 || !is_struct(_colour[1]) || !struct_exists(_colour[1], "colour")) {
            continue;
        }
        var _data = _colour[1];
        // ... existing texture lookup ...
        set_complex_shader_area(_colour_position, _data.colour);
    }

    Also validate _colour[0] access itself: check array_length(_colour) > 0 before _colour[0] == "texture" / "icon" so a truncated [] entry cannot throw a different indexing error.

    Fix 4 (recommended): stop mutating live map_colour on hover

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L650-L776

    Hover preview writes map_colour[$ hover_pos] = 0 then relies on a later-frame restore. Any missed restore (rapid mouse move, popup open, point_and_click creating colour_pick mid-hover) leaves 0 or undefined in the struct that the very next setup_complex_livery_shader clones.

    Safer: keep map_colour immutable during hover and pass a preview override into the draw (e.g. clone once, apply highlight in the clone), or at minimum guard the save/restore:

    if (!struct_exists(map_colour, hover_pos)) {
        hover_pos = false;
    } else {
        // existing save/set logic, plus is_undefined(colour_return[1]) check
    }

    Fix 5 (cleanup): document array_create(n, struct) aliasing

    https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/objects/obj_creation/Step_0.gml#L275-L278 and https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/ColourItem/ColourItem.gml#L516

    array_create(11, variable_clone(map_colour)) fills all slots with the same struct reference. Current code happens to break sharing via later variable_clone on write, but one direct mutation (e.g. _comp_livs[i].foo = x without clone) would corrupt all 11 companies. Either loop-clone or add a comment explaining why every write path must clone. Same for array_create(eROLE.MARINEEND, ...).

    5. How to verify

    1. Open Chapter Creation -> Livery slide (covers obj_creation_Draw_0 #L131 path).
    2. Hover every armour hitbox (image_location_maps keys at ColourItem #L225-L380, including R Trim / L Trim / Lenses / Weapon Primary/Secondary / Company Marks buttons rewritten at #L716-L740). Before fix: I32 argument is undefined popup via assert_popup; after Fix 1+2: no popup, preview highlights normally.
    3. Set a company livery slot to -1 (fresh scr_unit_draw_data(-1) state) and switch livery_selection_options between 0/1/2. Before fix: popup on next ComplexSet.draw; after: skipped cleanly.
    4. Craft a livery with ["icon"] (no payload) and ["icon", {}] (no .colour) in full_liveries[0]. Before fix: same popup; after Fix 3: skipped, base colour kept.
    5. Regression: standard, bulk, and advanced livery modes in https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_livery_setup/scr_livery_setup.gml#L38-L98 still recolour all complex_colour_swaps areas; complex_helms paths at https://github.com/Adeptus-Dominus/ChapterMaster/blob/main/scripts/scr_ComplexSet/scr_ComplexSet.gml#L2176-L2227 still tint eye_lense / head / muzzle.

    6. Secondary observations (not the crash, worth a ticket)

    7. Confidence

    High that the mechanism (unvalidated colour -> col_r[undefined]) is correct: it is the only expression in the reported frame that takes an I32 (array index), and the try/catch -> assert_popup explains the exact issue format. Medium on which of the four producers fired for this specific user (no save attached, Chapter Name: None, creation screen), with hover-mutated / -1-sentinel / stray-metadata keys as most likely given "hovering mouse over armor customization". Fixes 1-3 cover all of them regardless of which one fired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions