LCD Screen surface issue

Ouroborus shared this bug 1 month ago

LCD Screens implement both IMyTextDisplay (is a surface) and IMyTextDisplayProvider (has surfaces). The surface that is also the block itself is not the same as the surface that is made available through the block as a provider. In other words, LCD Panels have two surfaces: One that is also the block and is visible, and one that is supplied via its provider interface that is not visible.

Normally I would expect this code to work on any block that has surfaces:

var block = GridTerminalSystem.GetBlockWithName(someBlockName);
var surface = ((IMyTextSurfaceProvider)block).GetSurface(0);
surface.ContentType = ContentType.TEXT_AND_IMAGE;

But, for LCD Panels, it doesn't appear to do anything since it's operating on the hidden surface. In order to make this code generic enough to work on any block with surfaces, it would have to do something like:

var block = GridTerminalSystem.GetBlockWithName(someBlockName);
var surface = block is IMyTextSurface
    ? (IMyTextSurface)block
    : ((IMyTextSurfaceProvider)block).GetSurface(0);
surface.ContentType = ContentType.TEXT_AND_IMAGE;

Comments (9)


Hello, Engineer!

Thank you for your feedback! Your topic has been added between considered issues.

Please keep voting for the issue as it will help us to identify the most serious bugs.

We really appreciate your patience.

Kind Regards

Keen Software House: QA Department


if (block is IMyTextPanel) textSurfaces.Add((IMyTextSurface)block);
else if ((block is IMyProgrammableBlock) || (block is IMyCockpit)) textSurfaces.Add(((IMyTextSurfaceProvider)block).GetSurface(0));
IMyTextPanel does not have the IMyTextSurfaceProvider interface. You might as well be casting a cargo container as an IMyTextSurfaceProvider then wonder why nothing shows. Why would you need the panel to be a provider when you can cast it as an IMyTextSurface? My scripts add all IMyTextSurface instances to a single list using the checks above.




LCD Panels do implement both IMyTextSurface and IMyTextSurfaceProvider. (It's easy enough to verify in the game itself.) That IMyTextSurfaceProvider interface does contain one surface but it's not any visible surface.

Your example is essentially the same as my second example but with some unnecessary assumptions. Why check for both IMyProgrammableBlock or IMyCockpit when you could check for just IMySurfaceProvider? Should another type of block with displays become available, your strategy would require adding another check.

As for what I'd like, it would be a consistent API. I understand that having LCD Panels implement IMyTextSurface directly is helpful to those who have a shaky grasp of programming and are used to how things were before this change so having that helps with this transition. However, LCD Panels do also implement IMyTextSurfaceProvider so, for consistency, it would make sense that the surface provided by LCD Panels' provider is also the visible surface (i.e.: the same surface the block implements directly) rather than an extra, unused surface.

Thank you for finding this.


LCD Panels do not implement IMyTextSurfaceProvider. Did you look at my sources, picture of decompiled IMyTextPanel and Malware's API index?

I don't really use those checks, I only check if it's an IMyTextPanel when I pass a single IMyTerminalBlock to my output function, if it's not a panel I just cast it as a provider. Otherwise I fill a list of terminal blocks with a specific type, cast it appropriately, add the text surface to a list, and repeat with a different type.

I suppose it would be simpler and I could remove my checks. Screw it, I added my vote to that other topic.

Keen, add IMyTextSurfaceProvider to LCD Panels. Simplify my function and (as Ouroborus said) make the API more consistent.

Do you know if you can you do 'block is IMyTextSurfaceProvider', 'GetBlocksOfType<IMyTerminalBlock>', or 'GetBlocksOfType<IMyTextSurfaceProvider>'? I never tried them.


Don't know what to tell you. As I said, it's verifiable in-game that LCD Panels can be cast as IMyTextSurfaceProvider.

var blocks = new List<IMyTerminalBlock>();
GridTerminalSystem.SearchBlocksOfName("LCD Panel", blocks);

var block = blocks[0];
Echo((block is IMyTextSurface).ToString()); // True
Echo((block is IMyTextSurfaceProvider).ToString()); // True
Echo(((IMyTextSurfaceProvider)block).SurfaceCount.ToString()); // 1

var surface = ((IMyTextSurfaceProvider)block).GetSurface(0);
Echo(Object.ReferenceEquals(block,surface).ToString()); // False
Echo(block.Equals(surface).ToString()); // False


LCD Panels do not implement IMyTextSurfaceProvider. I do not know where the surface came from. IMyTextPanel is equal to IMyTextSurface. IMyTextSurface is equal to IMyTextSurfaceProvider. IMyTextPanel may be equal to IMyTextSurfaceProvider but that doesn't mean it is intentional.

    List<IMyTerminalBlock> panels = new List<IMyTerminalBlock>();
    IMyTextSurface surface = (IMyTextSurface)panels[0];
    Echo((surface is IMyTextSurfaceProvider).ToString()); // True
    Echo(((IMyTextSurfaceProvider)surface).SurfaceCount.ToString()); // 1
    IMyTextSurface extraSurface = ((IMyTextSurfaceProvider)surface).GetSurface(0);
    extraSurface.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE;
    Echo("Extra surface: " + extraSurface.GetText()); // Extra surface: text
    Echo("Surface: " + surface.GetText()); // Surface


Sandbox.Game.Entities.Blocks.MyTextPanel has the interface Sandbox.ModAPI.Ingame.IMyTextSurfaceProvider, this may be why you can cast it as an IMyTextSurfaceProvider.


LCD Panels can be found with the type IMyTextSurfaceProvider, getting the first surface works, applying the content type to text and image works, but writing to it does not work. I'm not sure why it partially works. Maybe because it is convenient to get a list of IMyTextSurfaceProvider stored as IMyTerminalBlock, cast IMyTextPanel as IMyTextSurface, and everything else as IMyTextSurfaceProvider.

    List<IMyTerminalBlock> providers = new List<IMyTerminalBlock>();
    for (int i = 0; i < providers.Count; i++) {
        IMyTextSurface surface = ((IMyTextSurfaceProvider)providers[i]).GetSurface(0);
        surface.ContentType = VRage.Game.GUI.TextPanel.ContentType.TEXT_AND_IMAGE;
        surface.WriteText("Panel test");
        try {
            ((IMyTextSurface)providers[i]).WriteText(Environment.NewLine + "Panel Test 2");
        } catch { Echo(providers[i].CustomName + " cannot be cast as an IMyTextSurface"); }


Yeah, I was just looking at where that was actually coming from, myself. I suspect it's available in-game because of the way the whitelist is configured and some side effects of how classes are compiled. In order to specifically block that, they'd have to use more fine-grained rules (and a lot of them) or add blacklisting capability.

Sandbox.ModAPI.Ingame.*, Sandbox.Common

Both IMyTextPanel and IMyTextSurfaceProvider are under Sandbox.ModAPI.Ingame and, even though the association is only defined in Sandbox.ModAPI, you can cast them back and forth anyway because the whitelist doesn't seem to restrict by parent/child, just by name/assembly.

So you're probably right, it's unintentional. Hopefully that's just a temporary situation and they add it in properly.