This proposal defines a minimal Entity Component System (ECS). Entities are unique identities that are assigned to multiple components (data) and then processed using the system (logic). This proposal standardizes the interface specification for using ECS in smart contracts, providing a set of basic functions that allow users to freely combine and manage multi-contract applications.
ECS is a design pattern that improves code reusability by separating data from behavior. It is often used in game development. A minimal ECS consists of
Entity: a unique identifier.
Component: a reusable data container attached to an entity.
System: the logic for operating entity components.
World: a container for an entity component system.
This proposal uses smart contracts to implement an easy-to-use minimal ECS, eliminates unnecessary complexity, and makes some functional improvements that are consistent with contract interaction behavior. You can combine components and systems easily and freely.
As a smart contract developer, the benefits of adopting ECS include:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
World contracts are containers for entities, component contracts, and system contracts. Its core principle is to establish the relationship between entities and component contracts, where different entities will attach different components, and use system contracts to dynamically change the data of the entity in the component.
Usual workflow when building ECS-based programs:
IWorld interface to create a world contract.createEntity() of the world contract to create an entity.IComponent interface to create a Component contract.registerComponent() of the world contract to register the component contract.addComponent() of the world contract to attach the component to the entity.registerSystem() of the world contract to register the system contract.IWorld.solIComponent.solThe library Types.sol contains an enumeration of Solidity types used in the above interfaces.
This is to ensure the correctness of types when using components, in order to avoid potential errors and inconsistencies. External developers can clearly set and get based on the type.
We cannot judge whether an entity actually exists based on its state alone. External contributors can create components based on entities. If the entities he uses don't exist, the components he creates may not make sense. Component creators should first check if the entity exists, and if the entity does exist, it makes sense even if the entity's state is false. Because he can wait for the entity state to be true before attaching the component to the entity.
getEntityComponents function returns all addresses of components instead of all component ids?There are two designs for getEntityComponents. The other design is to add an additional mapping for the storage of component id and component address. Every time we call addComponent, the parameters of the function are the entity id and component id. When the user calls getEntityComponents, it will returning an array of component ids, they query the component address with each component id, and then query the data based on each component address. Because a entity may contain many component ids, this will cause the user to request the component address multiple times. In the end, we chose to use getEntityComponents directly for all addresses owned by the entity.
registerComponent and registerSystem provide external permissions?It depends on the openness of your application or game. If you encourage developers to participate, the state of the component and system they submit for registration should be false, and you need to check whether they have submitted malicious code before using setComponentState and setSystemState to enable them .
get with extra parameters in component?The component provides two get functions. One get function only needs to pass in the entity id, and the other has more _params parameters, which will be used as additional parameters for obtaining data. For example, you define a component that stores the HP corresponding to the level of an entity. If you want to get the HP of an entity that matches its level, then you call the get function with the entity level as _params.
Unless you want to implement special functions, do not provide the following methods directly to ordinary users, they should be set by the contract owner.
createEntity(),
setEntityState(),
addComponent(),
removeComponent(),
registerComponent(),
setComponentState(),
registerSystem(),
setSystemState()
Do not provide functions that modify entities other than set() in the component contract. And add a check in set() to check whether the entity is available and whether the operating system is available.
After the system is registered in the world, it will be able to operate the component data of all entities in the world. It is necessary to check and audit the code security of all system contracts before registering it in the world.
If the new version has deprecated some entities, component contracts and system contracts. They need to be disabled in time using setEntityState(), setComponentState(), and setSystemState().
Copyright and related rights waived via CC0.