Late to the party here but I had some ideas that I thought might be worth sharing.
The idea of a global palette is mentioned as just an optimisation but I think it might be a key part of the initial 1.13 implementation. IMO the easiest path to 1.13 support is to keep supporting the current block id + meta data pattern used throughout the game logic. This could allow the chunk representation to be changed for 1.13 support without needing to rewrite half of the game logic first.
My idea for a global block palette/registry is that when you register a new block you also specify how many unique block states are required. The palette then assigns two things:
I don't know if I've explained it well but in my mind this seems far more than just an optimisation.
The idea of a global palette is mentioned as just an optimisation but I think it might be a key part of the initial 1.13 implementation. IMO the easiest path to 1.13 support is to keep supporting the current block id + meta data pattern used throughout the game logic. This could allow the chunk representation to be changed for 1.13 support without needing to rewrite half of the game logic first.
My idea for a global block palette/registry is that when you register a new block you also specify how many unique block states are required. The palette then assigns two things:
- A BlockId that is just a unique number for that block name
- A contiguous range of BlockStateId values on the global palette which uniquely represent block name + block state
- The chunk local palette only stores a single index into the global palette, not name + index as your example shows.
- The "meta value" is still just dumb data like BlockMeta is currently and won't require any new data structures.
- The "meta value" can be calculated directly from the BlockStateId
BlockStateMeta = BlockStateId - (Lowest BlockStateId for this block type)
So it's really just a State index that's specific to that block type.
- When you need to access individual values from the block state it could be handled by the respective block handler. I pass in the meta value and it returns a struct with the components or alternatively just have query functions like many block handlers have currently.
enum class BlockId : UInt16 {}; // The new BlockType enum class BlockStateId : UInt32 {}; // Global Id specifies both Type & Meta enum class BlockStateMeta : UInt16 {}; // State index for a known block type class BlockPalette { public: struct BlockData { BlockId m_Id; BlockStateMeta m_State; }; BlockId GetBlockId(const AString & a_BlockName) const; BlockId GetBlockId(BlockStateId a_StateId) const; BlockData GetBlockData(BlockStateId a_StateId) const; BlockId RegisterBlock(const AString & a_BlockName, UInt16 a_NumStates); private: std::vector<BlockStateId> m_BlockIdToStateId; std::vector<AString> m_BlockIdToName; std::unordered_map<AString, BlockId> m_NameToBlockId; }; BlockPalette gBlockStatePalette; using ChunkPalette = std::map<UInt32, BlockStateId> mBlockStateMap; class BlockArea { UInt32 mBlocks[]; // To be replaced by smaller numbers when possible ChunkPalette mPalette; BlockData block(const Vector3i & aPos) { const UInt32 paletteIndex = mBlocks[...]; const BlockStateId StateId = mPalette.mBlockStateMap[paletteIndex]; return gPalette.GetBlockData(StateId); } };
I don't know if I've explained it well but in my mind this seems far more than just an optimisation.