Problem with Python app bundled as Flatpak

I’ve undertaken the (didn’t realize what I was in for!) task of making the Amulet Minecraft Map Editor into the Amulet Flatpak Edition, and for the most part, it runs.

However, there’s an error with some of the textures not loading, and I don’t understand why because I can create a VENV and Amulet runs fine.

I understand that there are certain directories that are expressly forbidden to Flatpak apps, but as far as I can tell, Amulet isn’t trying to use any of them and all files and directories used are relative to Amulet’s install location.

To provide some background, Minecraft’s official resource packs don’t contain certain key textures, so Amulet bundles them with itself, getting installed with alongside it, and downloads the rest from the web, the same way the official Minecraft launcher does.

Because the Flatpak sandbox runs the FreeDesktop.org implementation of Python 3.12.6, I also used 3.12.6 for my VENV.

When I run Amulet in the VENV, it works fine. When I run the Flatpak version, I get this error:

texture_relative_path = #rail
texture_path = /home/evilsupahfly/.var/app/io.github.evilsupahfly.amulet_flatpak/cache/AmuletMapEditor/resource_packs/java/vanilla/assets/minecraft/textures/block/rail_corner.png
texture_relative_path = #rail
texture_path = /home/evilsupahfly/.var/app/io.github.evilsupahfly.amulet_flatpak/cache/AmuletMapEditor/resource_packs/java/vanilla/assets/minecraft/textures/block/rail_corner.png
<minecraft_model_reader.api.mesh.block.block_mesh.BlockMesh object at 0xe7adc73bfb0> = copy.deepcopy(self._load_block_model(minecraft:block/rail_corner))
model_path_list = ['minecraft', 'block/water']

model_path_list = ['block/cube_all']

model_path_list = ['block/cube']

model_path_list = ['block/block']

texture_relative_path = #down
texture_path = /app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png
ERROR - Failed to load block model {'model': 'minecraft:block/water'}
'/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png'
Traceback (most recent call last):
  File "/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/resource_pack_manager.py", line 246, in _get_model
    return self._load_blockstate_model(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/resource_pack_manager.py", line 337, in _load_blockstate_model
    model = copy.deepcopy(self._load_block_model(model_path))
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/resource_pack_manager.py", line 437, in _load_block_model
    if self._texture_is_transparent[texture_path][1]:
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
KeyError: '/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png'
ERROR - Failed to load block model {'model': 'minecraft:block/water'}
'/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png'
Traceback (most recent call last):
  File "/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/resource_pack_manager.py", line 246, in _get_model
    return self._load_blockstate_model(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/resource_pack_manager.py", line 337, in _load_blockstate_model
    model = copy.deepcopy(self._load_block_model(model_path))
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/resource_pack_manager.py", line 437, in _load_block_model
    if self._texture_is_transparent[texture_path][1]:
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
KeyError: '/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png'
Texture path retrieved: /app/lib/python3.12/site-packages/minecraft_model_reader/api/image/missing_no.png
BlockMesh created with texture path: /app/lib/python3.12/site-packages/minecraft_model_reader/api/image/missing_no.png
model_path_list = ['minecraft', 'block/iron_door_bottom_right']

model_path_list = ['minecraft', 'block/door_bottom_right']

texture_relative_path = #bottom
texture_path = /home/evilsupahfly/.var/app/io.github.evilsupahfly.amulet_flatpak/cache/AmuletMapEditor/resource_packs/java/vanilla/assets/minecraft/textures/block/iron_door_bottom.png
texture_relative_path = #bottom
texture_path = /home/evilsupahfly/.var/app/io.github.evilsupahfly.amulet_flatpak/cache/AmuletMapEditor/resource_packs/java/vanilla/assets/minecraft/textures/block/iron_door_bottom.png
texture_relative_path = #bottom
texture_path = /home/evilsupahfly/.var/app/io.github.evilsupahfly.amulet_flatpak/cache/AmuletMapEditor/resource_packs/java/vanilla/assets/minecraft/textures/block/iron_door_bottom.png
texture_relative_path = #bottom
texture_path = /home/evilsupahfly/.var/app/io.github.evilsupahfly.amulet_flatpak/cache/AmuletMapEditor/resource_packs/java/vanilla/assets/minecraft/textures/block/iron_door_bottom.png
texture_relative_path = #bottom
texture_path = /home/evilsupahfly/.var/app/io.github.evilsupahfly.amulet_flatpak/cache/AmuletMapEditor/resource_packs/java/vanilla/assets/minecraft/textures/block/iron_door_bottom.png
<minecraft_model_reader.api.mesh.block.block_mesh.BlockMesh object at 0xe7adc1e1e50> = copy.deepcopy(self._load_block_model(minecraft:block/iron_door_bottom_right))

Initially I thought the textures weren’t accessible, but after checking the directory structure inside the Flatpak, I can see that it’s fine. The .JSON files and the .PNG files are all present where they’re supposed to be (as you can see from the fact that iron_door and rail were loaded without issue), but water and lava consistently fail:

[~/Downloads/flatpak-stuff/Amulet-Flatpak] flatpak-builder --run amulet-flatpak_build_dir io.github.evilsupahfly.amulet_flatpak.yaml sh

sh-5.2$ python --version
Python 3.12.6

sh-5.2$ cd /app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block

sh-5.2$ ls -Fla
total 24
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  150 Dec 31  1969 ./
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   10 Dec 31  1969 ../
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  528 Dec 31  1969 banner/
-rw-r--r--+ 1 evilsupahfly evilsupahfly  651 Dec 31  1969 barrier.png
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1265 Dec 31  1969 end_portal.png
-rw-r--r--+ 1 evilsupahfly evilsupahfly  213 Dec 31  1969 grass.png
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1286 Dec 31  1969 lava.png
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1405 Dec 31  1969 structure_void.png
-rw-r--r--+ 1 evilsupahfly evilsupahfly  927 Dec 31  1969 water.png

sh-5.2$ echo $PWD
/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block

sh-5.2$ cd ..

sh-5.2$ echo $PWD
/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/textures

sh-5.2$ ls -Fla
total 0
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  10 Dec 31  1969 ./
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  50 Dec 31  1969 ../
drwxr-xr-x+ 1 evilsupahfly evilsupahfly 150 Dec 31  1969 block/

sh-5.2$ cd ..

sh-5.2$ echo $PWD
/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft

sh-5.2$ ls -Fla
total 0
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   50 Dec 31  1969 ./
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   18 Dec 31  1969 ../
drwxr-xr-x+ 1 evilsupahfly evilsupahfly 3080 Dec 31  1969 blockstates/
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   10 Dec 31  1969 models/
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   10 Dec 31  1969 textures/

sh-5.2$ cd models

sh-5.2$ echo $PWD
/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/models

sh-5.2$ ls -Fla
total 0
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   10 Dec 31  1969 ./
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   50 Dec 31  1969 ../
drwxr-xr-x+ 1 evilsupahfly evilsupahfly 1624 Dec 31  1969 block/

sh-5.2$ cd block

sh-5.2$ echo $PWD
/app/lib/python3.12/site-packages/minecraft_model_reader/api/resource_pack/java/java_vanilla_fix/assets/minecraft/models/block

sh-5.2$ ls -Fla
total 168
drwxr-xr-x+ 1 evilsupahfly evilsupahfly 1624 Dec 31  1969 ./
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   10 Dec 31  1969 ../
drwxr-xr-x+ 1 evilsupahfly evilsupahfly 1704 Dec 31  1969 banner/
-rw-r--r--+ 1 evilsupahfly evilsupahfly  133 Dec 31  1969 barrier.json
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  992 Dec 31  1969 bed/
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1525 Dec 31  1969 bed_foot.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1535 Dec 31  1969 bed_head.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1610 Dec 31  1969 bell_between_walls.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1507 Dec 31  1969 bell_ceiling.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 2669 Dec 31  1969 bell_floor.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1592 Dec 31  1969 bell_wall.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  153 Dec 31  1969 black_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  151 Dec 31  1969 blue_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  153 Dec 31  1969 brown_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1855 Dec 31  1969 chest_base.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  105 Dec 31  1969 chest.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1537 Dec 31  1969 chest_left_base.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  115 Dec 31  1969 chest_left.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1527 Dec 31  1969 chest_right_base.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  117 Dec 31  1969 chest_right.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  579 Dec 31  1969 conduit.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  151 Dec 31  1969 cyan_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  104 Dec 31  1969 ender_chest.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  139 Dec 31  1969 end_portal.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  109 Dec 31  1969 grass.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  151 Dec 31  1969 gray_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  153 Dec 31  1969 green_shulker_box.json
drwxr-xr-x+ 1 evilsupahfly evilsupahfly   18 Dec 31  1969 head/
-rw-r--r--+ 1 evilsupahfly evilsupahfly  134 Dec 31  1969 lava.json
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  396 Dec 31  1969 light/
-rw-r--r--+ 1 evilsupahfly evilsupahfly  163 Dec 31  1969 light_blue_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  163 Dec 31  1969 light_gray_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  151 Dec 31  1969 lime_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  157 Dec 31  1969 magenta_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  151 Dec 31  1969 moving_piston.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  155 Dec 31  1969 orange_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  151 Dec 31  1969 pink_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  155 Dec 31  1969 purple_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  149 Dec 31  1969 red_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  141 Dec 31  1969 shulker_box.json
drwxr-xr-x+ 1 evilsupahfly evilsupahfly 1032 Dec 31  1969 sign/
-rw-r--r--+ 1 evilsupahfly evilsupahfly  147 Dec 31  1969 structure_void.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  106 Dec 31  1969 trapped_chest.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  116 Dec 31  1969 trapped_chest_left.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  118 Dec 31  1969 trapped_chest_right.json
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  336 Dec 31  1969 wall_banner/
-rw-r--r--+ 1 evilsupahfly evilsupahfly 1208 Dec 31  1969 wall_banner.json
drwxr-xr-x+ 1 evilsupahfly evilsupahfly  228 Dec 31  1969 wall_sign/
-rw-r--r--+ 1 evilsupahfly evilsupahfly  135 Dec 31  1969 water.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  153 Dec 31  1969 white_shulker_box.json
-rw-r--r--+ 1 evilsupahfly evilsupahfly  155 Dec 31  1969 yellow_shulker_box.json

sh-5.2$ 

So, can somebody who is good with both Python and Flatpaks explain to me what’s going on? What differences exist between “Flatpak Python 3.12.6” and “Normal Python 3.12.6” that could be causing my problem, and how do I resolve it?

This kind of key error would occur when something is missing. However just because the files are there, doesn’t mean the code is looking in the correct place for them.

The : and / is an unusual use of Python type annotations. Should that whole arg be a string literal (i.e. quoted)?

This is the block of code that comes from:

        """Load the model(s) associated with a block state and apply rotations if needed."""
        if isinstance(blockstate_value, list):
            blockstate_value = blockstate_value[0]
        if "model" not in blockstate_value:
            return self.missing_block
        model_path = blockstate_value["model"]
        rotx = int(blockstate_value.get("x", 0) // 90)
        roty = int(blockstate_value.get("y", 0) // 90)
        uvlock = blockstate_value.get("uvlock", False)

        model = copy.deepcopy(self._load_block_model(model_path))

        # TODO: rotate model based on uv_lock
        return model.rotate(rotx, roty)

The whole thing is located here.

The resource pack manager consults the .JSON files for meta information about the blocks, and uses it to pull the appropriate .PNG for the 3D editor - if the stairs are “top_right” you don’t want “bottom_left” being rendered, right?

As I said before, all the paths are supposed to be relative to Amulet’s install location, and this actually works properly in a VENV when installed through PIP - no missing textures.

Since all the files are read in from the directory they’re in, I don’t see how some work while some don’t, when all .PNGs are in one folder and all .JSONs are in their own and the resource manager API module simply pairs them by their names - unless I’ve missed something crucial and obvious. Or maybe it’s something passed to this module by another module within Amulet?

That being said, I didn’t write this - I’m just the guy trying to make it work as a Flatpak. The output was obtained by me just adding some extra “print” statements to capture the string contents as it looped through the file sets to see what was happening inside the loop without running through PDB every time.

If problems are encountered with simple file paths, it’s well worth double checking what any build tool has done to your file paths.

Otherwise hopefully someone from the Flatpaks team can help you, or someone who knows what (if anything) has been changed in freedesktop.org’s Python build.

1 Like

Funny story about that, actually. :sweat_smile:

I went to Flatpak first because this only happens to the Flatpak version and they said, “The Flatpak builds and the app launches? That’s good. Path or coding errors? Ah - looks like you need some Python help. Try them.”

Even Amulet’s dev team isn’t sure what the problem is.

So probably what I should do is go back to the Flatpak thread and ask someone for the specific differences between “Flatpak Python” and “Normal Python.”

Don’t get me wrong though. I really appreciate how fast and thorough your responses were! If only the other guys were like this!

Perhaps I should be looking for a Flatpak Simulator, if such a thing even exists? :stuck_out_tongue_winking_eye:

Haha!

Good on you, if you’ve just decided to be a third party repackager. Those people are the unsung heroes behind many Linux distros.

But honestly, if a normal Python install already works with pip, then instead of trying to repackage Amulet for every alternative package manager on the internet and having to support all those too, just to indulge the users of that package manager’s preference, I would personally ask for strong justification, or even a commercial arrangement, from whoever the requirement to use Flatpak came.

1 Like

Actually… Funny story about that too. :sweat_smile: The Flatpak was actually my idea because they had a “proper executable” build for Windows, so I figured Flatpak would be similar - more end-user friendly, compared to mucking around with dependencies and PIP and virtual environments - at least for the casual user (whose numbers seem to be growing more and more each year as the Big Guys become more and more monetized and invasive). And with Github actions - which they already use, a Flatpak build can be integrated into their normal release path. Or, once it’s working, anyway. With Flatpak being nearly universal and distro-agnostic, it seemed like a good idea at the time. Literally just a double-click install.

So after a few trial runs to work out the bugs as I familiarized myself with Flatpak’s manifest system, I can now produce locally and with Actions on GitHub, a stable Flatpak from any release of Amulet. The only downside being this [monster trucking] textures thing. And with Visual Studio, much as I hate it for being a M$ product, it DOES integrate Git quite well, and it can run Python projects in a VENV so you can see what works and what doesn’t between different releases.

And with a Flatpak version available, on distros whose package manager has the Flathub integrated (like Mint, Fedora, Manjaro, etc), or for users who add Flathub access themselves, Amulet becomes both more accessible, and more recognized, and I felt was a win for Amulet, which I have been using it since the old days when it was still MCEdit.

I didn’t realize how much I had bitten off because I’m not well-versed in Python (though I think I still have a “Hello World” script from '02 on a floppy, in a shoebox of 70-something other floppies somewhere in my storage unit). I assumed that since it worked through Normal Python, it would work in Flatpak Python. I probably mentioned this already, but it’s why I set my dev VENV to the same version Flatpak uses - just in case.

Lesson learned though: Never assume anything actually works just because it looks like it works? :face_with_raised_eyebrow: :person_shrugging:t3:

2 Likes

Side note, this is the function. Is there anything that stands out as being potentially problematic? Anything I can change to better understand what’s happening, other than loading it down with print(f"Tell me {whats_going_on} here!") after every transaction?

    def _get_model(self, block: Block) -> BlockMesh:
        """Find the model paths for a given block state and load them."""
        if (block.namespace, block.base_name) in self._blockstate_files:
            blockstate: dict = self._blockstate_files[
                (block.namespace, block.base_name)
            ]
            if "variants" in blockstate:
                for variant in blockstate["variants"]:
                    if variant == "":
                        try:
                            return self._load_blockstate_model(
                                blockstate["variants"][variant]
                            )
                        except Exception as e:
                            log.exception(
                                f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
                            )
                    else:
                        properties_match = Block.properties_regex.finditer(
                            f",{variant}"
                        )
                        if all(
                            block.properties.get(
                                match.group("name"),
                                amulet_nbt.TAG_String(match.group("value")),
                            ).py_data
                            == match.group("value")
                            for match in properties_match
                        ):
                            try:
                                return self._load_blockstate_model(
                                    blockstate["variants"][variant]
                                )
                            except Exception as e:
                                log.exception(
                                    f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
                                )

            elif "multipart" in blockstate:
                models = []

                for case in blockstate["multipart"]:
                    try:
                        if "when" in case:
                            if "OR" in case["when"]:
                                if not any(
                                    all(
                                        block.properties.get(prop, None)
                                        in self.parse_state_val(val)
                                        for prop, val in prop_match.items()
                                    )
                                    for prop_match in case["when"]["OR"]
                                ):
                                    continue
                            elif "AND" in case["when"]:
                                if not all(
                                    all(
                                        block.properties.get(prop, None)
                                        in self.parse_state_val(val)
                                        for prop, val in prop_match.items()
                                    )
                                    for prop_match in case["when"]["AND"]
                                ):
                                    continue
                            elif not all(
                                block.properties.get(prop, None)
                                in self.parse_state_val(val)
                                for prop, val in case["when"].items()
                            ):
                                continue

                        if "apply" in case:
                            try:
                                models.append(
                                    self._load_blockstate_model(case["apply"])
                                )

                            except Exception as e:
                                log.exception(
                                    f"Failed to load block model {case['apply']}\n{e}"
                                )

                    except Exception as e:
                        log.exception(f"Failed to parse block state for {block}\n{e}")

                return BlockMesh.merge(models)

        return self.missing_block

Is the output you show here just what’s generated by running the flatpak as-is? I ask because you mentioned putting prints in and the output has a bunch of stuff like model_path_list = ['minecraft', 'block/water'] which seem to be some kind of debugging output showing the values of certain variables. Do you know what is generating that output?

Also, the error appears twice in the output, which is peculiar.

The ultimate error is a KeyError arising because it has self._texture_is_transparent which is presumably a dictionary and it’s trying to access texture_path as a key of that, and that that long path ending in “/block/water.png” isn’t found as a key. If you’re putting debug prints in, you could try printing the value of that dictionary at that point. Who knows what it will tell you, but for instance maybe it would be meaningful to see that it (for some reason) contains everything else you expect except this one thing, versus it containing nothing at all.

Edit: Also, not sure if you already realized this, but the bottom-level triggering error is not in Amulet but in this other library which Amulet apparently depends on.

I think that’s an artifact of whatever is producing those debug messages. It’s not actual Python code. It looks like some kind of debugging print that’s suppressing quotes on strings or otherwise munging the output a bit.

2 Likes

Absolutely. It would’ve been a good idea if the framework worked perfectly with Amulet’s data files. Alas, like so many frameworks, it still requires work for anything that doesn’t quite fit it.

Also I’m jealous of you - I’ve lost all my old 3.5" floppies :frowning: .

1 Like

The output was generated by the print statements I added. Literally just print(f"some_variable = {some_variable}").

Actually, no. I didn’t realize it. Worth checking out though.

My prints maybe? Amulet has a debug mode which sets the log level to debug, but it doesn’t really change much, which is why I was running it in PDB.

Now that I’m home from work, and had a chance to look at this properly, I realize that’s actually the file I added all the prints to. This module is the one that reads the .JSON and maps the corresponding .PNG files for the 3D editor - things like which direction a sign or rail block is facing, or what level a water or lava block is (from 1 to 8, as this image demonstrates).

I guess I’m confused because all the .JSON meta data is together in one folder and all the .PNG block tiles are together in another, and both directories are successfully read, yet, some files always fail, consistently. If it was a build tool issue, wouldn’t everything along that path be impacted rather than just some very consistently specific things?

Clearly, I need to take a much more detailed look at what’s going on while I await answers from the Flatpak camp about possible and potential differences between their Python and “Normal” Python.

In my Flatpak manifest, I have some debug environment variables set for Python:

  - --env=PYTHONDEBUG=3
  - --env=PYTHONVERBOSE=3
  - --env=PYTHONTRACEMALLOC=10

Would that account for the duplication?

According to them, there shouldn’t be any difference:

Meaning there’s something going on with the code itself, possibly inherited from another module.

This is the correct advice to properly figure out what’s going on

The dict self._texture_is_transparent gets populated in the _load_iter method

For some reason (there’s a handful of ifs that could be filtering it out), that particular path isn’t being added to that dict which is causing the KeyError you see

In addition to checking that the files themselves exist, you should check the values of that dictionary (and maybe add debugging prints in else blocks in that load method to see which files are being excluded and why)

1 Like

You guys are amazing. This has so far been the most productive and insightful help I’ve been given since I started this project!

My only regret now is not coming here sooner!

:grinning:

I had initially only changed how the error was logged, from error to exception then started adding prints as the root cause continued to elude me based on feedback from the devs who, it turns out, don’t actually use Linux as a daily driver and were making some VM-assisted guesses based on the results of my tweaks. But now I have clear direction and feedback that’s useful and detailed giving me a good foundation from which to investigate.

Seriously. Thanks again! :pray:

3 Likes

After much tediousness, I still couldn’t narrow down why the textures weren’t loading properly so I brute forced it by adding this:

if texture_path not in self._texture_is_transparent:
    log.warning(f"Texture path not found: {texture_path}. Defaulting to transparent.")
    self._texture_is_transparent[texture_path] = (0, True)  # Default to transparent if not found

– (Modified Minecraft Model Reader)

Problem solved, textures loading. The .JSON files were all where they were supposed to be, and the .PNG files were all right where they belonged too, and that’s the only reason this seems to work. As for why this ran fine as a “proper desktop app” but not as a Flatpak is still a mystery to me. For now, though, this work-around does the trick.

Edit: While forcing True and False both work, the water isn’t treated properly when False (which I originally had), which, in retrospect, is pretty obvious.