Save and Sync
In mod scripts there comes a time where you need to save and synchronize data, this combo is most often needed for terminal controls.
Ways to save and/or sync
CustomData
This can only be used on terminal blocks and it covers both save and sync. However it is player-facing and you pretty much have to use MyIni for compatibility with PB scripts and other mod scripts.
Usage is similar to PB so the linked guide above would cover most of it.
MyModStorageComponent
This is per-entity meaning it covers fat blocks (not deformable armor) and other entities like grids, characters, voxelmaps, etc.
It is only for saving but it is also sent with the entity when it's streamed to players which helps simplify the synchronization later on (if necessary).
Guid & sbc
First you need to generate a GUID to act as a unique key for your settings. Generally only one is needed per mod but you can have more if you wish (for example if block settings contain some very big amounts of data like a ship blueprint, you can separate them into two, settings and blueprint guids).
You can ask Visual Studio to generate a GUID for you or use an online tool like guidgenerator.com.
Next you need to claim said GUID as your mod's by creating a sbc file with:
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EntityComponents>
<EntityComponent xsi:type="MyObjectBuilder_ModStorageComponentDefinition">
<Id>
<TypeId>ModStorageComponent</TypeId>
<SubtypeId>YourModNameHere</SubtypeId>
</Id>
<RegisteredStorageGuids>
<guid>your-guid-string-here</guid>
</RegisteredStorageGuids>
</EntityComponent>
</EntityComponents>
</Definitions>
The <RegisteredStorageGuids>
can have multiple <guid>
elements in it if desired.
Storage property
All entities have a Storage
property, it defaults to null therefore your first step before writing is to:
if(Entity.Storage == null)
Entity.Storage = new MyModStorageComponent();
Then to write some data to it:
public static Guid SettingsKey = new Guid("your-guid-string-here");
//...
Entity.Storage[SettingsKey] = "stuff to save";
And that's it, once you save the world this string will be attached to the entity.
The saved data will be passed along to copies, blueprints and as mentioned before: to other players when they receive this entity from server.
This component is on the entity itself so that means all mods have access to the same instance, try not to break other mods.
Data format
You can use whatever you wish as long as it can be turned into a string and back.
Since the key is tied to your mod you don't need to worry about other mods writing there (unless they intentionally copy your GUID and do it).
One way is to use a Protobuf class serialized to binary (which can also be sent over the network to synchronize) and then that byte[]
array can be turned into base64 string (see #Gravity Collector below for details).
Can also use MyIni for it as well.
MyObjectBuilder_ModCustomComponent
This article or section is a stub.
It needs more content, you can help Space Engineers Wiki by expanding it. |
This is only for saving for entity components, similar to the #MyModStorageComponent but a different approach.
Because mods can't quite add new types (because scripts will load after the world is deserialized), there's an intermediary serializable objectbuilder called MyObjectBuilder_ModCustomComponent
which can be used in the Serialize() & Deserialize() methods in components.
Example
Not tested.
[MyComponentType(typeof(ExampleSerializableGamelogic))] // unsure if required
[MyEntityComponentDescriptor(typeof(MyObjectBuilder_Thrust), false)] // this is regular gamelogic stuff, look up usage elsewhere
public class ExampleSerializableGamelogic : MyGameLogicComponent
{
public override bool IsSerialized()
{
return true; // required for Serialize&Deserialize to be called
}
public override MyObjectBuilder_ComponentBase Serialize(bool copy = false)
{
return new MyObjectBuilder_ModCustomComponent
{
CustomModData = "your saved data",
// unsure how these affect anything
ComponentType = nameof(ExampleSerializableGamelogic),
SubtypeName = nameof(ExampleSerializableGamelogic),
// setting this true will remove all other components of this type, probably not going to go well with multiple mods using this method with this true
RemoveExistingComponentOnNewInsert = false,
};
}
public override void Deserialize(MyObjectBuilder_ComponentBase ob)
{
var custom = ob as MyObjectBuilder_ModCustomComponent;
if(custom != null)
{
// read custom.CustomModData
}
}
}
MySync
This article or section is a stub.
It needs more content, you can help Space Engineers Wiki by expanding it. |
This is for synchronizing some data on an entity, requires a component to link properly to the entity's sync layer.
It can only use blittable types but not all of them, for example byte[]
does not work. Also bool
is not blittable but does work.
Confirmed working types are: bool, int, float, double, enum, Color, Vector3, MyFixedPoint.
NOTE: Using gamelogic means it will add to the existing "sync properties" cap of 32 shared with blocks, some are very close to the limit such as rotors having around 30.
A separate component would have its own sync property limit but it is unclear how we're supposed to create&use those.
Usage
First your component needs to implement IMyEventProxy
.
Next, declare unassigned fields of MySync<T, SyncDirection>
, like:
MySync<float, SyncDirection.BothWays> SyncFloat;
MySync<Color, SyncDirection.BothWays> ExampleColor;
MySync<Base6Directions.Direction, SyncDirection.FromServer> DirOrWhatever;
The game will assign them using reflection.
The SyncDirection.BothWays allows clients to set the value and it be synchronized to server and from there to everyone else, otherwise SyncDirection.FromServer will not accept sync from clients on this property, only server can set it.
Wherever you load the values from their stored location, you can use SyncFloat.SetLocalValue()
to set it only locally.
Then reading and writing are done entirely with SyncFloat.Value
- writing to that will automatically synchronize it.
Keep in mind that this does not do a whole lot of validation for the value, you should ensure it's within the expected parameters.
There's also SyncFloat.ValueChanged
event if you need it.
Packet sending
This is for synchronizing any sort of data you wish, regardless if it's tied to a block/entity or not.
The one case it can't be used is if you have a server config and want clients to get it ASAP (in LoadData()), for that case use this instead: Example_ServerConfig_Basic.cs
A set of helper classes have been made to aid with the process: Example_NetworkProtobuf - instructions and everything are inside the files.
Examples
Gravity Collector
Uses MyModStorageComponent and Packet sending, the gist of where to look:
- A single Protobuf class that is used for both storage and sync.
- Using Gamelogic's IsSerialized to detect before entity gets serialized and SaveSettings() when that happens.
- When a setting changes it schedules for save and sync, to avoid spamming.
- The SyncSettings() is called in update10 to send changes to everyone when relevant.
- Then the packet is received by everyone else and assigned accordingly, ignoring entities that are not found.