Oriented by Hugo Sereno e Rui Maranhão
May 28, 2016
This project was made under a college domain (Software Arquitecture), and we want achieve some goals like:
Our introduction chapter, will be divided under two sub-sections, an user and developer introductory view:
WorldEdit is a world editor for Minecraft which makes users life way more easy by helping on some essentials things in game, like building structures, spawning objects, and manipulate them, among others. In this analysis we will talk about how this in-game framework works, and how it is implemented "behind the hood". It holds some interesting features:
We aim to include different architectural views (more exactly, 4+1 Architecture view), and his stakeholders. After this analysis, we pretend to discuss about how we could improve this framework, contributing to the software and future developers.
Figure 1 - Selection for decorative purposes
In terms of developer view, and avoiding to spoil any conclusions, we want to get into some aspects of Minecraft itself.
Minecraft was made in Java, and it's world is composed by chunks. Chunks are 16x16x256 segments, and they are generated around players when they first enter the world. As they walk around, chunks are generated as needed. They can also be forced to load into memory by world management tools, such as WorldEdit.
Our architecture views along this documentation, will describe how the main components communicate with each other. Starting on the logical view up until the physical view, we can get really deeper into the structure of the project.
Figure 2 - Chunk
We will omit most command syntax. However, a detailed overview of all commands can be found in the official documentation.
It's possible to perform a long range of region operations with WorldEdit.
To perform those kind of operations, the user is required to select a Cuboid area. This is done by selecting two points which will create a rectangular selection (other shapes and formats can also be used).
Using the selected area, we can perform changes like:
Usage: //wand [Gives user an item that can be used to flag first position (left click) and second position (right click)]
Usage: //pos1 or //pos2 [Selects first and second positions]
Some examples of region operations are:
WorldEdit provides the ability to have a clipboard that allows users to copy, paste and even save an area.
These cuboid areas can be saved into a file (schematic files) under a storage at
\plugins\WorldEdit\schematics\
and loaded at any time with a simple command. The copy-paste
functionality saves the cuboid area in cache, in the user Local Session until we decide to paste it.
Some examples of clipboard operations are:
Generation tools provide us the ability to create a set of shapes (can be generated with mathematics) up to a full forest.
This section is focused in creating architectural elements, like for example a sphere or a pyramid. WorldEdit
is also able to generate a forest like /forestgen 10
:
The command searches the area around you, controlled by the size parameter, and it will descend in elevation a bit in order to find grass or dirt (trees will only be generated on top of those two blocks), but it will not ever go up above your feet.
The generation algorithm was made clever to develop a forest according to surrounding area conditions. However, it is not possible to get 100% density (a tree in every spot) as Notch's (creator of Minecraft) tree algorithm won't permit that.
If we saw a lot of interesting features, it doesn't end here! Utility joins a lot of useful functions we often manually struggle for. In sum, we want to distinguish tools like:
One of the most interesting ones is the brushes. As the name says, we cannot only paint with it but we can also generate blocks or shapes to where we are pointing to (terraforming). It also allows us to smooth rough mountains and level terrains.
WorldEdit is made of a large necessity from the community all around the world running vanilla minecraft or Bukkit/Forge servers. The project runs on a voluntary base and is featured in a large community of plugins called Bukkit.
It's almost impossible for the community to imagine world or block management without WorldEdit,
we can conclude this by the large amount of users depending on it 13,963,291 downloads on 28/05/2016
.
Figure 12 - Example of a complex construction which would be hard to develop manually. Credits: Everbloom Studios Team
WorldEdit has a large base of community contributors. We've grouped the main motors of this project below. The 4 users below are classified as authors and have the most commits and activity in the active development process of the software.
This stakeholder is made by a group of users that are part of communities like, Bukkit and Forge. These last two, are the important part that create the bounds between minecraft and plugin loading. Both conceive the required environment to load mods/plugins into the client. In our case, WorldEdit makes world management easy, like plug & play. It’s also part of the Bukkit community, along with lots of other plugins. WorldEdit, however, implements a clever project structure that molds into being multi-platform using adapters.
The users from communities like Bukkit or Forge, are the important part here, who need this plugin and contribute to it daily, by suggesting new features or helping to fix problems.
This is the core part of our documentation. We aim to demystify the big tree of packages this project consists on. Expand the software and provide other developers, the required knowledge to be aware of the pillars it holds.
In the following diagrams we tried to only represent the important classes or packages grouped among all the project as this is a very complex software. A full class diagram wouldn't be user-friendly, so we created a views joining all important related classes aiming to make it easier to understand and explain what's happening.
In sum, this project has three big packages (or Schemas):
As for these last two, they are required to support WorldEdit on Minecraft through other platforms, responsible for loading all the required plugins and create the functional environment. All the classes that we didn't represent here, are the ones that we don't consider as fundamental for the analysis.
WorldEdit's way of managing commands is very sagaciously organized. From our study and previous knowledge with Bukkit, we found out this command architecture is also featured in other author's plugins. One of the main reasons is the simplicity of organizing all the required commands without having them all in a class with a massive amount of if statements.
It consists in having a manager for commands (Command Manager Class) which will map each command string or alias with corresponding method. The manager can register any command class, and will fetch method annotations like the example below:
@Command(
aliases = { "/line" },
usage = "[block] [thickness]",
desc = "Draws a line segment between cuboid selection corners",
help = "Help will be described here",
flags = "h",
min = 1,
max = 2
)
@CommandPermissions("worldedit.region.line")
@Logging(REGION)
public void line(arguments) {
// Command method will describe here
}
Command Manager will register alias "/line" with method "line(args)". Next time the user inputs a command, manager will (call a dispatcher to) find which stored method is related to the input (methods that are mapped with their aliases in a data structure):
MappedCommands<String, Method> contains <"line", public void line(arguments)>
This architecture is extremely useful to avoid code pollution and keep different commands separated per class or category.
Local Session saves history from every command made by an user, and helps us to know on-the-fly configurations from him. However, sessions are per user but not user bounded.
Local Session is the object that contains our clipboard, a set of configurations, brushes/masks, position 1/2 location and Edit Session history.
Every time we make an operation, WorldEdit creates an Edit Session with the set of blocks we changed. This type of session is saved as a list inside Local Session. If a user wants to undo, or redo, local session has all the history saved as a list of Edit Session objects. So, we can easy go back in our history.
It's inside the Edit Session we find the main methods for world operations like:
editSession.replaceBlocks()
editSession.setBlocks()
We can have a better overview at the full diagram of this view:
The WorldEdit is a modular tool composed by a main package, free of dependencies, and by adapters that create bounds to the plugin.
Bukkit is the adapter that supports plugin connection to the game-server itself and implementing classes related to, the wide range of blocks, entities and biomes. There is also a library with useful tools for Bukkit commands and classes that provide an internal permissions system to WorldEdit.
Core is the main package of the project. As mentioned previously, it works without dependencies, meaning it's where adapters fit. Inside the core there are several sub-packages, such as "command-related" packages that implement the control commands (CommandManager).
Forge is composed by all classes that make up the main component loader. In addition there's a "GUI" package, whose function is to handle the graphical user interface in Minecraft client associated with the adapter.
In this process view, we decided to feature one of the most important diagrams, Activity.
When the user first inputs a command, the Command Manager is responsible for implementing a method for command detection. The command manager has a dispatcher which stores the command with respective method. After efficiently checking if the command exists it proceeds to call the session manager and fetch the user session.
If session manager doesn't have any entry for that user, we will create one and return to the command manager. Otherwise, the already created session will be returned.
Command manager will now call the dispatcher to run the method associated to the input command.
If not exceptions will be returned, caught to deliver a specific messaged associated to the error.
If no exceptions are returned, an Edit Session can now be created, this will save all our modifications for later undo/redo case. The command class will then receive the session and execute the commands through it (more about how this happens).
This diagram represent where the artifacts go after being successfully compiled and archived. After artifacts are ready, they are uploaded to multiples websites or communities, and then users can download them, and upload the correct version to their own server or client.
When we talk about scenarios we talk about every possible case that can happen by doing or using something, this diagram, explains what can be made with this tool in a very simple and short way.
A small example of a use case description:
YAML is a encoding format that makes data more readable by human beings, it was inspired in languages such as XML, Python, Perl, and Internet Message Protocol. It was created by Clark Evans, Brian Ingerson and Oren Ben-Kiki, by the year of 2000.
YAML (pronounced as "yammel"), was designed with a very proper idea, that every data could be represented by a set of list, hashes, and simple values. YAML only uses unicode characters (UTF-8 and UTF-16).YAML is relatively simple to use. The respective file holds the information in a indented format like the example below:
Blocks: - Grass - Sand - Stone Users: Pedro: '- Developer' Marco: '* Second Developer' João: '# Developer' Andreia: '\ Developer'
Within the domain of the Java language, YAML fields can be easily accessed using:
yamlFile.getStringList("Blocks");
// Will return a list ['Grass', 'Sand', 'Stone']
yamlFile.getConfigurationSection('Users').getKeys(false)
// Will return ['Pedro', 'Marco', 'Joao', 'Andreia']
// We can now do:
yamlFile.getString("Users.Pedro");
// Will return '- Developer'
In the context of the project that we have been reviewing during this semester, YAML is used to store some configurations related to multiple aspects such as:
WorldEdit is open to community contributions. It's also useful if you need to create an adapter for your software. However, your code must be efficient and tested.
Before cloning the project and start writing code, remember the checklist:
If you are not used to git, make sure to also be aware of their branching model.
To avoid consecutive merges, commit disorganization and code loss, WorldEdit follows successful branch model as the figure below.
The develop branch (integration) is the core part where developers are working. Every time a developer
needs to create a new feature, they open a new branch, for example feature/replaceblocks
.
After the new feature has been completed, that branch can now be merged back to develop.
Release branches support preparation of a new production release. They allow for last-minute dotting of i’s and crossing t’s. When we finish a release branch, we merge it to master. The changes made on the release branch also need to be merged back into develop, so that future releases also contain these bug fixes.
Hotfix branches are required for critical bugfixes. When completed they need to be merged back into master and develop must also receive those changes.
This is successful git production!
Figure 18 - Successful Git ModelThe build process uses Gradle, which isn't required to download.
./gradlew clean setupDecompWorkspace
./gradlew build
After that you will find:
worldedit-core/build/libs
worldedit-bukkit/build/libs
worldedit-forge/build/libs
Minecraft, more specific, the Bukkit Adapter has known limitations related to asynchronous world modification. If we try to run block modifications in a separate thread we will get an exception like:
[Timer-51/WARN]: java.lang.IllegalStateException: Asynchronous block remove!
Derived from this, WorldEdit core runs every task using the main thread. This decision can cause complications when performing tasks with a massive number of blocks. As for example, if we try to paste a schematic with 1 million blocks running WorldEdit in a server with the following specifications:
Intel Xeon E3 1225v2 3.2 GHz+ 4GB of RAM allocated to the server
The server will stop responding after a few seconds, possibly causing world chunk errors.
After studying the problem, it's possible to understand we can achieve an asynchronous operation and still avoid to use a separate thread. We can do this by scheduling the number of blocks per a unit of time. Let's take again our example of 1 million blocks. If we define a static constant of 5000 blocks per each second, we can schedule consecutive tasks and complete our task in about 3.33 minutes.
Task #1 Modify 5000 blocks Run now Task #2 Modify 5000 blocks Run after 1 second Task #3 Modify 5000 blocks Run after 2 seconds
After 200 tasks, we will complete our job. This method will give us the ability to see the progress of our operation and avoid any world/chunk errors if the server gets shutdown. The job can be saved to the disk and sustained any time.
WorldEdit is a very complex project. We enriched a lot of our knowledge about organized software architecture by getting deeper into this project. It leaves us surprised studying such clever code organization where most of the times people tend to find fixes.
In the logical and development architectures, more specifically, the managers and adapters, we were able to learn a new diverse way of how to create multi-platform compatible software using adapters. Along with that, we saw new ideas to centralize complex command classes, like the Command Manager does.
We, programmers, are used to, in our world, start something from scratch. We have to make lots of
choices while building software, some of them may not be the best if we are not used to the
environment we are programming in.
Most of the times we end up having to create a "recode" version of our project because we find out we can have a
better architecture than what we started in. How many times this doesn't happen?
In the real word, an architect cannot fail on this aspects. It can lead to a disaster and lots of repercussions.
Programmers are always able to mold their project. But the moral in all this is that we can greatly improve our workflow and save a lot of time if we make it right on our base software architecture. Having a solid architecture is the key to have a strong organized software.
Although software has no mass, it does have weight, weight that can ossify any system by creating inertia to change and introducing crushing complexity. Grady BoochAnd that's what we've learned!
Figure 21 - Complex Architecture
Website designed by Pedro Lourenço