Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype V2 #270

Merged
merged 520 commits into from
May 6, 2024
Merged

Prototype V2 #270

merged 520 commits into from
May 6, 2024

Conversation

gentlegiantJGC
Copy link
Member

@gentlegiantJGC gentlegiantJGC commented Sep 24, 2023

This is an experiment to rewrite some of the core classes to fix a number of long-standing issues.
This is also a chance to reevaluate the implementation having learnt quite a lot since I started writing this.
Here are the issues that I plan to tackle
#256 - chunk storage
#257 - chunk storage

Changes in Amulet Core 2.0

Module structure changes

The amulet.api module is going to get removed and its contents moved into the root.
amulet.api.block.Block > amulet.block.Block
amulet.api.entity.Entity > amulet.entity.Entity

The level classes are getting rewritten and moved around
amulet.level.formats.leveldb_world > amulet.level.bedrock.BedrockLevel
amulet.level.formats.anvil_world > amulet.level.java.JavaLevel

amulet.libs.leveldb will be removed. This was already depreciated.

Level construction changes

The level classes are now constructed through class methods. #258
The default constructor now just defines minimal data.

Level class changes

The level class is rather cluttered. I want to compartmentalise methods to declutter it.
This was prototyped in #261 and modified a bit since.
level.get_dimension() returns a dimension object within which all data from a dimension can be acquired.
dimension.get_chunk_handle returns a class containing methods to get, set, delete and do other things with a chunk.

Chunk class changes

In 1.0 we have one chunk class shared by all levels. The idea for this was that we would only need to write one operation which would work for all world types. I still like this idea but it has been a little limiting because not all data fits into the same format.
2.0 breaks the chunk class up into components (BlockComponent, EntityComponent, ...) and each level defines its own chunk class (or multiple) which is built from the common components and custom components.
Code then needs to check if the chunk is an instance of the required component before making the changes.

Universal format

The universal format is a custom format I created that is designed to be a superset of all the formats. This format is subject to change so that we can support new data as it gets added.
Currently only blocks are implemented but the plan is to extend this to items and entities.
In 1.0 the blocks in the chunk are stored in this format and the idea is that plugin writers should use the translator to convert their desired format to the universal format however I have seen multiple cases of hard coding in the universal format.
The universal format also obscures what the real data.
After quite a bit of thought I have decided to switch to storing the chunk data in its native format.
This means that a block in the chunk will now look like Block("minecraft", "stone") instead of Block("universal_minecraft", "stone")

The universal format will become a private implementation detail used to translate between different formats.

Data versioning

With the change to native data we need to know which platform and version the data is defined in.
Java chunks only support data defined in one format but Bedrock blocks can be defined in any historical format.
Any class that takes data which may change between platform or version must have a platform and version identifier.
This includes Chunk, Block, Entity, ...

Data storage changes

Previously when we loaded data (eg chunks) there was one instance that was stored in a dictionary. Every “get” call returned the same object.
We could only clear the dictionary when the changes were saved or the container was explicitly cleared by the app.
This is fine in a single threaded environment where the app manages the state and calls an operation.

I would like to support multi-threading and multiple plugins that don’t have to be aware of each other.
The new implementation will return a deep copy of the data for every “get” call. #260
The caller owns the data and once it loses the reference it will automatically get deleted.
To modify the data the caller needs to change the data and call the equivalent “set” method.

Thread locking

Usually when writing a library it is down to the user of the library to manage synchronisation but in this case we may have multiple plugins each running their own threads that are not aware of each other.
To solve this we have added locks associated with each piece of data which the caller must acquire if they wish to modify the data. #262

The code would look something like this.

level: BedrockLevel
with level.edit_parallel():  # If another thread has acquired the world for unique editing then this will block until it is finished
    dimension = level.get_dimension("minecraft:overworld")
    chunk_handle = dimension.get_chunk_handle(0, 0)

    with chunk_handle.edit() as chunk:  # If another thread has locked this chunk, this will block until it is finished.
        # edit the chunk
        # If the edit context manager exits without erroring it will automatically set the chunk

    # The above is equivalent to the following
    with chunk_handle.lock():
        chunk = chunk_handle.get()
        # edit the chunk
        chunk_handle.set(chunk)

    # Once level.edit_parallel() exits it will automatically create an undo point

History changes

The history system should be modified to support disabling of the undo point #255
I can't remember if I have done this or not.

Signals

Currently to tell if a piece of data has changed we need to periodically manually check. The chunk generator thread is constantly checking if the chunks have changed in order to know if it needs to rebuild them.

To improve this I have added a signal system that anything can subscribe to to get notified. #268
The chunk generator just needs to subscribe to the changed signal of all the chunks it is drawing and it will get automatically notified when they get changed.

Raw data editing

I would like to support editing/viewing the raw data as well as in the higher level representation. #259
To do so the code would have to uniquely lock the level so that no other threads can edit the level in the higher level representation.
This would allow raw viewing/editing of the leveldb database and the Java region data.

@gentlegiantJGC gentlegiantJGC force-pushed the merge-level-format-wrapper branch 2 times, most recently from 62e09f4 to d3ced61 Compare October 18, 2023 10:29
@gentlegiantJGC gentlegiantJGC force-pushed the merge-level-format-wrapper branch from 1097853 to c4eb012 Compare October 23, 2023 13:36
Added implementations for all bar one of the translation functions.
Refactored some of the read and write code
The plugin class now handles all formatting. Not just colour.
Bedrock does not support strikethrough or underline and uses the same section codes for colours.
The plugin class allows configuration of the different codes.
This still needs tests for raw text parsing
Fixed some bugs related to newlines
Simplified parsed section text output. If there is only one child, this replaces the parent.
@jevexendo jevexendo self-assigned this Jan 27, 2024
@gentlegiantJGC
Copy link
Member Author

I am going to merge this as is and add pull requests for further fixes.

@gentlegiantJGC gentlegiantJGC marked this pull request as ready for review May 6, 2024 10:08
@gentlegiantJGC gentlegiantJGC merged commit 375dc8c into 2.0 May 6, 2024
2 of 11 checks passed
@gentlegiantJGC gentlegiantJGC deleted the merge-level-format-wrapper branch May 6, 2024 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants