Hidden SBC tags features - THDigi/SE-ModScript-Examples GitHub Wiki

Not all SBC tags are declared in vanilla files, this list has the ones I found/remembered.

Feel free to ask me in the Keen discord about

NOTE: All numbers are float (can have decimal points) unless otherwise specified in the comment next to it.

NOTE: Boolean values should either be true or false, any capitalized letters will cause XML errors.

Moved to SE wiki

Contents


Make a block unbuildable

(Back to top)

Blocks can be hidden from build menu by simply <Public>false</Public> in its definition.

Existing blocks in world can only be removed manually or by completely removing the block from existance as shown above in Remove Definition, but that can have problems with blocks depending on its references, try it though.

However, that does not fully prevent players from building it using projectors or if they have it in toolbar already (which they can also add to by using block picker mods).

To prevent those cases too you need to give it an unobtainable component as the first one and make it deconstruct to scrap (in block's definition).

For unobtainable component just make one up (see Components.sbc) and you also have to create a blueprint for it (Blueprints.sbc) otherwise it crashes, you don't need to add the blueprint to any blueprint class however, meaning it remains uncraftable (See zonechip component in both those files for reference).

For the deconstruction, from battery block definition as an example:

            <Components>
                <Component Subtype="SteelPlate" Count="20" />
                <Component Subtype="Construction" Count="10" />
                <Component Subtype="PowerCell" Count="80">   <!-- mind the ending with > not /> -->
                    <DeconstructId>
                        <TypeId>Ore</TypeId>
                        <SubtypeId>Scrap</SubtypeId>
                    </DeconstructId>
                </Component>
                <Component Subtype="Computer" Count="25" />
                <Component Subtype="Construction" Count="20" />
                <Component Subtype="SteelPlate" Count="60" />
            </Components>

The tag ending with /> means it self-closes, so a <Tag /> == <Tag></Tag>, but in this case <DeconstructId> has to go inside the <Component> tag therefore it should not self-close, like shown above.


<WheelPlacementCollider> explained

(Back to top)

Even though this is declared for all blocks, it's only used by blocks attached to MotorSuspension.
This defines a fake collision cylinder for determining what other blocks can be placed nearby as well as if the wheel can spawn when surrounded by blocks.
This is used instead of the actual wheel collider because that one has to be a sphere for performance and smoothness reasons.

Defaults to blank, instead I'm showing the possible tags and their defaults.

You can also see the fake collider by activating F11's Debug Draw (also turn on draw physics then off again to fix shenanigans) and try to place other blocks near suspension wheels.

<WheelPlacementCollider>
  <!-- fake wheel diameter, ratio of largest between X and Z from the metric boundingbox -->
  <ColliderDiameter>1.0</ColliderDiameter>
  <!-- fake wheel thickness, ratio of Y from the metric boundingbox -->
  <ColliderHeight>1.0</ColliderHeight>
  <!-- fake wheel offset on Y axis, ratio of Y from the metric boundingbox -->
  <ColliderOffset>0.0</ColliderOffset>
</WheelPlacementCollider>

Basically if Diameter and Height are both 1 and offset 0, then the fake wheel will perfectly fit inside the block boundingbox.

If the explanations don't make sense, here's an example of the math involved:
Given a wheel block that is LargeGrid 3x1x3, its metric boundingbox would be 2.5*3 = 7.5m X and Z, with 2.5*1 = 2.5m Y.
Which means:

  • ColliderDiameter of 0.5 would be 7.5 * 0.5 = 3.75m for the fake wheel diameter
  • ColliderHeight of 0.25 would be 2.5 * 0.25 = 0.625m for the fake wheel thickness

Change render distance on multi-LCD blocks

(Back to top)

For standalone LCD (<TypeId>TextPanel</TypeId>) it has the render distance right in the block definition:

<MaxScreenRenderDistance>180</MaxScreenRenderDistance>

(and that is the default if undeclared)

For blocks that have a different purpose and just so happen to support LCDs, they don't have a setting in the block definition, however using EntityComponents one can change it.

Here's an example of doing it to the big-screen cockpit:

<?xml version="1.0" encoding="utf-8" ?>
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <EntityContainers>
        <Container>
            <Id>
                <TypeId>Cockpit</TypeId>
                <SubtypeId>LargeBlockCockpit</SubtypeId>
            </Id>
            <DefaultComponents>
                <Component BuilderType="MyObjectBuilder_LcdSurfaceComponent" ForceCreate="true"/>
                <Component BuilderType="MyObjectBuilder_MultiTextPanelComponent" SubtypeId="LCD_500m" ForceCreate="true"/>
            </DefaultComponents>
        </Container>
    </EntityContainers>

    <EntityComponents>
        <EntityComponent xsi:type="MyObjectBuilder_MultiTextPanelComponentDefinition">
            <Id>
                <TypeId>MultiTextPanelComponent</TypeId>
                <SubtypeId>LCD_500m</SubtypeId>
            </Id>
            <MaxRenderDistance>500</MaxRenderDistance>
        </EntityComponent>
    </EntityComponents>

</Definitions>

<TieredUpdateTimes> explained

(Back to top)

You might've seen these tags on some blocks:

<TieredUpdateTimes>
    <unsignedInt>60</unsignedInt>
    <unsignedInt>120</unsignedInt>
    <unsignedInt>240</unsignedInt>
</TieredUpdateTimes>

What it affects is how frequent the block updates depending if the grid is present for any player or in some cases if other grids are nearby.
This of course impacts performance so if you're uncertain then don't change them.

If the tag is missing, they have hardcoded defaults (which so far seems to match the ones from vanilla SBC).

The numbers are ticks between updates (game runs at 60 ticks per game second).
Value 0 can be used to completely pause the block's update, only if it supports this system of course.

Then the game computes two things per grid:

  • Player Presence Tier:
    • Normal - grid synchronized to at least one player. NOTE: Single-player or player hosted will always have this set to Normal.
    • Tier1 - grid is no longer synchronized to anyone.
    • Tier2 - grid has been Tier1 for ~10 minutes.
  • Grid Presence: true when another grid is within ~3km (hardcoded distance; checked every ~16.6s), false otherwise. This includes ship's own attached grids like rotors, pistons, wheels

I will now refer to the <unsignedInt> in order as Value1, Value2 and Value3.

Behavior on a block type basis (SE v204):

"Standard" means it the values match 1:1 with Player Presence Tier (Value1 for normal, Value2 for tier1, Value3 for tier2), and it ignores Grid Presence.

  • Gatling/Missile/Interior Turrets:
    • Value1 used if PlayerPresenceTier is Normal or GridPresence is true
    • Otherwise Value2 is used (Value3 is not used)
  • Thrusters:
    • if PlayerPresenceTier is Normal, Value1 is used
    • Otherwise (Tier1 or Tier2), GridPresence decides if Value2 (true) is used or Value3 (false)
  • Connector:
    • Value1 if PlayerPresenceTier is Normal or GridPresence is true
    • Value2 if PlayerPresenceTier is Tier2 and GridPresence is false
    • Value3 otherwise
  • Projector: PlayerPresenceTier Normal is required for projection to be spawned.
  • Searchlight: Always uses Value1
  • Reactors: Standard
  • Refinery/Assembler/SurvivalKit: Standard
  • GasGenerator: Standard
  • Custom Turret Controller: Standard

What the updates actually affect on these block types is way more work to dig through.

And bringing it all together into an example:
If the XML snippet above is from a Refinery then it will update once per second in singleplayer/player host or if any player is roughly within sync distance of its grid in DS.
Otherwise, in DS if there's no players online or nobody within sync distance, then it will update once every 2 seconds for the first 10 minutes and then once every 4 seconds past that.

For digging through code to update this knowledge:
  • MyFunctionalBlock.TiersChanged() for the amount of values used + check overrides for types using it
  • MyFunctionalBlock.GetTimerTime() for extras that might be using it without Tiers (currently searchlight)
  • MySessionComponentSmartUpdater.UpdateBeforeSimulation() for grid presence tier

Planet CloudLayers and fake errors

(Back to top)

Simplified and moved to SE wiki: https://spaceengineers.wiki.gg/wiki/How_to_create_your_own_planet#6.8.1_Modded_CloudLayer_issues

In more detail

CloudLayers section from planet definitions is such a nightmare, let me break it down:

  • You're supposed to give it a Path\TextureName.dds and it automatically looks for Path\TextureName_cm.dds and Path\TextureName_alphamask.dds.
  • It cares not for your suffixes, if you give it Texture_cm.dds it will look for Texture_cm_cm.dds and Texture_cm_alphamask.dds.
  • It only reads the first <Texture> so don't bother listing more than one per CloudLayer.
  • You get an error in F11 menu for the Path\TextureName.dds not existing, which you should ignore as long as the texture actually shows up in game.
  • Depending on the definition style at the begining and end, can have different behaviors:
    • Using the old <Definition xsi:type="PlanetGeneratorDefinition">:
      • does not forcefully append mod folder to CloudLayers (see below for example).
      • can use textures from game folder.
      • can add a fake texture to get rid of the error (only if you have the real textures in your mod too).
      • can't read textures from mods unless you add the fake texture to get rid of the error.
    • Using the new <PlanetGeneratorDefinitions> + <PlanetGeneratorDefinition>:
      • forcefully adds mod folder to CloudLayers textures (regardless of them existing in mod folder or not).
        • (e.g. Textures\Things\Texture.dds becomes C:\Steam\WorkshopOrWhatever\YourModId\Textures\Things\Texture.dds)
      • because of the above, cannot use textures from game folder.
      • cannot add a fake texture to get rid of the error because then mod path is added twice, breaking it.
      • textures in mods are automatically found because of the first point, but previous point breaks it.

PlanetGeneratorDefinitions.sbc has the old definitions style.
Triton.sbc, Pertam.sbc etc have the new definition style.
Don't forget that the ending of the file also has to be changed to match.

So TL;DR my recomandation for linking CloudLayer textures:

  • Use old style definition (<Definition xsi:type="PlanetGeneratorDefinition">)
  • Depending on where you want to use textures from:
    • Game folder: just have the texture name as normal (without the suffixes), you can't avoid the missing texture error though, just ignore it and test it visually.
    • Mod folder: you must add a fake texture (a renamed empty .txt file works) so that the game finds it and appends the mod folder path to its path internally, otherwise it remains relative and looks in game folder. Bonus that the missing texture error will be gone too.

Block <VoxelPlacement> explained

(Back to top)

This part of a block definition:

<VoxelPlacement>
    <!--Possible settings Both,InVoxel,OutsideVoxel,Volumetric. If volumetric set than MaxAllowed and MinAllowed will be used.-->
    <StaticMode>
        <PlacementMode>Volumetric</PlacementMode>
        <MaxAllowed>0.2</MaxAllowed>
        <MinAllowed>0</MinAllowed>
    </StaticMode>
    <DynamicMode>
        <PlacementMode>Volumetric</PlacementMode>
        <MaxAllowed>0.2</MaxAllowed>
        <MinAllowed>0</MinAllowed>
    </DynamicMode>
</VoxelPlacement>

Is what determines how the block can be placed inside voxels or outside.

If this tag is not defined on a block it will fall back to the settings from SessionComponents.sbc.

Now to explain how it works.

For <PlacementMode> values:

  • None = can't be placed regardless of voxels.
  • Both = fully permissive, allowed both inside and outside of voxels.
  • InVoxel = can only be placed if any boundingbox corner is inside voxels.
  • OutsideVoxel = can only be placed if none of its boundingbox corners are inside voxels.
    NOTE: it can still be placed inside with its middle if the block is large enough, if you wish for any part to not touch voxels use Volumetric with both min and max as 0 instead.
  • Volumetric = determines how much inside or outside voxels it can be, configured using <MinAllowed>/<MaxAllowed> tags, see blow for details.

For the <MinAllowed>/<MaxAllowed> tags:

Only used if <PlacementMode>Volumetric</PlacementMode>.
Range for these tags are 0.0 to 1.0 equivalent to 0% to 100% of block's volume intersecting voxels.
NOTE: the code calculating volume ratio only gets to 0.5-0.75 tops (jitters between these) in best case scenario, but with cubebuilder raycasting to surface it realistically goes up to 0.5.

  • Min is how much is required be inside voxels meaning it can't be placed outside of voxels if this is above 0.
  • Max is how much to allow inside voxels.
    Both 0 will act similar to OutsideVoxel but with a volume check instead of checking corners.

What the <StaticMode> vs <DynamicMode> mean:

  • It has nothing to do with a cubegrid's physical static-ness.
  • Static is used when aiming at a cubegrid or at a voxel with "local grid mode", or basically if your block is restricted to a 3D grid then it's likely using Static mode in this.
  • Dynamic is opposite, if you can freely move the block without a 3D grid, even when aiming at a voxel, then it uses Dynamic mode.

I recommend to not care about the distinction between Static or Dynamic and set both to the same exact settings. This is what Keen also does.

⚠️ **GitHub.com Fallback** ⚠️