Fractal Block World
Version 1.02.00
Back
The engine has been expanded to include
"Icarus mode".
Updates
Version 1.02.02:
- Date: Not released yet
- Steam workshop support is now in beta!
See the guide here
for using mods that other people have created.
That guide also describes how to create your own mods.
To use mods from the Steam workshop,
you must enable the Steam workshop from inside the game.
You subscribe to mods via Steam Workshop website,
and the game will automatically download them.
Then within the game, you select which of these downloaded
mods you want to use.
- Xar stick and ball caves are now much faster.
What made them slow originally was the way seeds of adjacent chunks were computed.
The seeds of adjacent chunks were computed by first computing the paths
of the adjacent chunks.
The new method uses "tail seeds".
Tail seeds are better than ordinary path seeds in every way,
except for the fact that with tail seeds the world can repeat itself (wrap around).
The tail "coordinates" (x,y,z) of a chunk are computed from the last
7 local block positions in the block path of the chunk.
That is, each tail coordinate is an unsigned 28 bit integer (not a typo).
Given the tail coordinates of a chunk, it is trivial to compute the
tail coordinates of adjacent chunks (without having to compute the paths
of adjacent chunks).
Then given tail coordinates of a chunk, we can compute a seed (the "tail seed").
You can visit the branches of Apple Trees to see the faster cave engine.
- Instead of outright replacing the old cave engine with the new one,
we are grandfathering old save files.
That is, every saved game that was originally created strictly before
version 1.02.02 of the engine will use the old cave engine
in all places where waypoints could be broken.
Saved games created in or after version 1.02.02 use the new cave engine.
This is all tracked in the variable
game.version.first_engine_version in the env_vars.txt
file of the saved game.
How is it possible to modify worldgen code but in some situations still
grandfather old save files, even though worldgen code is hard coded?
Well, if a sane person looked at the worldgen code their face would melt off
and their head would explode, like the bad guys at the end of
Raiders of the Lost Ark.
- Another improvement with the new cave engine is that instead
of querying the 5x5x5 region around a chunk (for 5x5x5 caves), it
just queries the 3x3x3 many 2x2x2 chunk regions
(virtual chunks).
This might seem worse because it is effectively looking at a
6x6x6 region instead of a 5x5x5 region. However, it is probably faster
because it only requires looking at 27 virtual chunks.
Also, this new method would allow for fast 12x12x12 caves
or even 24x24x24 caves.
Both of these would still only require looking at 27 virtual chunks.
- The following is tricky. As you may know, in the xar package, the world
seed only determines your initial starting location, it does not actually
affect the worldgen itself.
This is because the engine's random number functions ignore the world seed.
The problem with this is it also applies to people making their
own worlds from scratch.
So, starting in this update the engine's random
number functions will
use the world seed.
In order to not break xar worldgen, the xar package will
forcibly set the world seed to zero each time the xar package is loaded
(and it will store the old world seed, if it was non-zero, in a
xar package global variable called
worldgen.state.seed_original).
Note that xar worldgen is still free to use the original seed in special situations,
like for the "Mylantis Underground" where we would want it to be randomized.
- If you run the command "
path seed",
it will print to the console the
player's path together with the seed of the world in one string.
Call this a "seeded path".
An example of a seeded path is SEED_123456_7e4_68f.
You can use a seeded path with the teleport console command,
but be careful because this will override the game's world seed.
In an emergency, you can look at
game.package.seed_original for the original
world seed that your saved game used,
or you could look at
game.globals.worldgen.state.seed_original.
- Avoid sharing block paths with your friends for
players created before 1.02.02 and players created after 1.02.02
because of engine worldgen changes described earlier.
- You can override the world seed (in Lua worldgen) for whatever chunk is being generated.
Use the function
override_world_seed.
- In general, functions that compute seeds from chunk generating input
are more optimized.
- "Tail seed" and "tail coordinate"
functions are now exposed as part
of the chunk generation API.
For example, let's say you want to compute the "tail seeds" of all of the 27
nearby chunks around the chunk being generated.
You would first call
get_tail_coords(), which returns
a triple (x,y,z) of 28 bit unsigned ints (not a typo).
That function is a bit slow.
Then you would call the functions
add_to_tail_coords(tx,ty,tz,dx,dy,dz)
and tail_coords_to_seed(tx,ty,tz) 27 times,
and those functions are fast.
The function add_to_tail_coords just adds the two triples of ints
together and possibly wraps around to make sure each coordinate
is betwen 0 and 2^28 - 1 inclusive.
Thanks to the user balt for input on the
tail_coords_to_seed function.
The great thing about tail seeds is that they can be computed quickly
for adjacent chunks, whereas regular path seeds take much longer to compute
for adjacent chunks.
One might be able to make Lua worldgen code that is similar to a traditional
(non-hierarchical) block game using tail coordinates.
That is, tail coordinates simulate a traditional block game
coordinate system.
- Tail seed functions have also been added to the game API.
- Now Lua modules other than
top.lua can have a
__new_game function.
Also, the top.lua module and other game modules can have a
__close_game function, which is called when the game is closed
(including when the game is reloaded).
Similarly, the top.lua module and other game modules can have a
__save_game function, which is called when the game is about to be saved,
before the engine actually performs the saving.
Window scripts can also have the following functions:
__new_game,
__load_game_early,
__load_game,
__save_game, and
__close_game.
- When you install the game, the default is now to have the cursor
enabled in the main menu.
Also, the mod selecting GUI uses the cursor.
You can still use the mod selecting GUI without the cursor,
in which case you press enter to move items left and right,
and you can also hold down shift and press up/down/left/right
to move an item.
- Ying Forest Air worldgen is much faster.
Before it was slow to fly through Ying Forests.
Modders will find the following technique useful.
Originally, the outer shell of each Ying Forest Air chunk
had a dummy block type to prevent cheesing by shrinking
many times on the boundary of an adjacent chunk.
The problem was that all these dummy blocks were making the worldgen slow,
because there was a huge number of these blocks
(because air is 3 dimensional).
Now each air block, when it is subdivided into a chunk,
checks if it is on the boundary of its kilochunk.
This is faster because there are significantly
fewer chunks than blocks.
- Now right before the engine renders a window,
it will set the render color to white with alpha "1.0".
- There is now (the start of) a new portion of the game API
designed to directly read and write to chuck files,
bypassing chunks.
The new functions added so far are designed for
removing moving entities from chuck files.
These functions can be used to create a multiplayer mod where
a player on one computer can kill a monster and this
will remove the corresponding monster on everyone else's
computer.
- Text boxes now allow you to scroll if there is too much text
to fit in one screen.
However as a modder,
just because you can make text boxes that scroll,
doesn't mean you should.
Players might not want to scroll when they open a text box.
- The way Lua scripts are loaded is now different.
In the new version, all scripts are buffered into memory
early on, and then later that buffer is used multiple times
to create Lua states.
Also, this buffering of scripts is asynchronous,
so you can see how many scripts have been buffered so far
when you load a game.
- Recall that in modding there are the global variables of types
b,i,f,v,s which stand for
bool, int (64 bit), float (64 bit), vector (three 32 bit floats),
and string.
Now there is a new variable type: block pos
(three 32 bit ints).
This type is designated by the letter "p".
- As a modder, in some places you have to create variables before setting them.
You can still do this, but now there are
"
set_force" functions
such as
ga_set_force_i.
These functions will create the variable (if it does not already exist)
and then will set it even if it did not exist before.
To be consistent, all "set" functions (as opposed to "set_force" functions)
will require the variable to exist already.
There are also more "get_maybe"
functions which make it easy to get
the value of variables that may or may not exist.
- To implement Steam Workshop support, the game uses
the Steam "Manual Callback Dispatch" system.
This is because Steam expects developers to use the MSVC C++ compiler to
use "Steam callbacks", but Fractal Block World uses the MinGW C++ compiler
so the standard Steam callback system will not work at all.
This is all because C++ does not have a standard
Application Binary Interface (ABI).
- Now, as a modder, you can specify the
max width of text.
If the text is longer than this, the char with and height will be
scaled so that the text fits.
You set the max width by calling
ga_win_txt_set_max_width.
A negative max width means there is infinite max width.
To prevent having to worry about other people's mods,
the max width is set to -1 by the engine before it renders
each window.
There is an exception: Before rendering a side display,
the engine sets the max width to the side display width.
- Added Infinity Flower to Secret Tau Cave Moon Treasure Room
and added I3 WIG Flower to I2 Mountains treasure room.
- Recall that there are "mutators", which a player can set
in the new game menu.
Now there are also "package params" which are like mutators
except they can be set for any mod,
not just the standalone
playable mod being played.
The user can set them in
Options → Mods → Mod Params.
See the "Package Params" guide.
- Now when you use certain entities in the world,
we compute the line segment from the player to the center of the item and we see if that
line segment intersects any env rects.
We then trigger all these env rects
(assuming they have the function
__on_line_seg_hits).
This prevents cheesing (skipping past env rects),
although there are a couple of areas with
"cheesy" env rects that are not triggered by these line segments.
Technically we should not use the line segment described and
instead use the line segment that is part of the ray used to interact with the entity,
but it probably does not matter.
- You are no longer allowed to rebind the
escape or grave (tilde) key or the
PACKAGE_OPEN_MAIN_MENU or
PACKAGE_OPEN_CONSOLE actions
from inside the easy controls menu.
This is to prevent accidentally locking yourself out of the
game which requires deleting config files to fix.
You can still rebind these by directly modifying config files
if you wanted to do something really advanced.
- There is a new debugging tool.
If you add the line "
set debug.log_stack true"
to your program_startup.txt, then when the program exits
it will generate the files
Output/debug_stack_times.txt,
Output/debug_stack_histogram.txt, and
Output/debug_stack_average_times.txt.
The main body of the first file says how many seconds each
"debug push/pop" pair took.
For example, after playing a few seconds and closing,
it says that the push/pop pair with the name "Engine::Render"
took a total of 3.59 seconds.
On the other hand, "FractalBlockEngine::Render" took
1.92 seconds.
However the FractalBlockEngine::Render function is only called
from within the Engine::Render function,
so the 1.92 seconds of the FractalBlockEngine::Render function
are part of the 3.59 seconds of the Engine::Render function.
These debug push/pop pairs are hard coded into the engine,
so you have no control over them.
However, the game API functions ga_debug_push and
ga_debug_pop will generate a push/pop pair which you
can use to analyze your Lua code.
Next, the file Output/debug_stack_histogram.txt
says how many times each function was called.
The file Output/debug_stack_average_times.txt
shows the average amount of time spent in each debug push/pop pair
(it is the total time divided by the number of times the pair was called).
- Normally input events are bound to actions,
and every action has a name (like CUSTOM_03).
However, input events can also be bound to "anonymous" actions,
which are just console commands with no formal names.
Now these anonymous actions are displayed in the
Options → Controls → Bindings → Controls menu.
You can unbind them there, but you cannot rebind them there
(they can only be bound using the console command
bind).
- Recall that you are able to make your own actions
(which input events can bind to).
These are the
CUSTOM_XX actions.
Some of these were already set to things like "save" or "grow".
However, these preset example actions are
now called EXAMPLE_XX.
You should not modify those, although you could if you did
some hacking.
With this new method,
the example actions and your custom actions
will not overlap with one another.
The existing custom actions will remain
until you either reset them or delete the Input/params.txt file
(but deleting the Input/params.txt file will reset some of your
game customizations).
If someone installs a fresh copy of the game,
all custom actions will be blank
(but there will be several non-blank example actions).
- One of the
EXAMPLE_XX actions is
exit.
This will exit the game without saving.
While I don't want people to shoot themselves in the foot
and lose progress in the game, some modders might want to have this as a key binding.
Casual players should probably use the safer-for-gameplay action
saveexit instead.
Version 1.02.01:
- Date: September 2026
- It used to be that the depth buffer was cleared after
rendering each level.
Now the depth buffer is cleared after rendering 2 levels.
This implies that the viewer level L
as well as level L-1 are rendered together without clearing the depth buffer
in between them.
This decreases the number of depth buffer related render glitches.
You can specify how many levels are rendered before the depth buffer
is cleared by setting the variable
engine.render.depth_buffer.num_levels.
However, setting to anything higher than 2 causes Z-fighting
(because the game uses a 24 bit depth buffer).
You can try to change over to a 32 bit depth buffer
by setting the variable display.depth_buffer.num_bits to 32
(but on many computers, including the developer's computer,
a 32 bit depth buffer is not supported and the game will exit
on startup).
- It is now possible to create a mod that has a skybox.
See the skybox guide.
The key is that before every level is rendered, the function
p.__render_before_level is called.
It is passed the level that is being rendered (which is an integer)
as well as a bool which specifies whether the depth buffer was just cleared.
- In addition to the
p.__render_before_level function there is still the
p.__render_augmented function.
This function is now called at every level,
not just the viewer level (and the level is passed as an integer argument
to the function).
The p.__render_augmented function is called just before the alpha
component of the level is rendered.
- Env rects work differently now.
Before, we would intersect the bounding box of the player with
env rects once per frame.
Now, when we move the player during every discrete update
(which happens 25 times per second), we see if the path of the
player's camera crosses any env rect.
As a result, it should basically be impossible to move the player
0.1 block lengths or more through an env rect without triggering it.
For speed, we do not consider a block position (on any level) more than
once during a discrete update.
However, it is possible to trigger an env rect more than once per frame
if that involves multiple block positions inside the env rect.
- There is now a way to use blocks in place of env rects.
If a block script has the function
p.__on_viewer_lp_inside(level, bp),
then this will be called whenever the viewer's level position
(the camera position) is inside the block.
This will be called at most once per discrete update for any given block.
If the player tries to move through the block, the function will be called.
This function is recursive, in that it will be called even if the block
is on a coarser level than the viewer level.
- Now when the engine loads a game, it clears ALL keys (input events).
Before, it only cleared keys which the engine thought were down.
Note that saved games do not store which keys are down,
but a package might have a global bool var like "moving forward"
which is saved (which is why we need to clear ALL keys when loading a game).
- Here is something for advanced modding.
If a mod has a game Lua script with a function called
p.__get_should_interrupt_save,
this function will be called when a save is requested.
If that function returns false, then the save will be halted.
Nothing will be saved
(hence no chunk files will be written
to the chunk file database).
- Recall that every block is actually a "stack of frames".
When you change the type of a block, you are actually pushing a new frame
on top of the block stack for that block position.
Prior to this update, block variables generated from worldgen
were stored in frame zero.
This was wrong, because frame zero can "revert" after a certain amount of time.
In this update, we store block variables from worldgen
in an appropriate place.
We store them as part of the chunk but not part of block stacks.
We can say that they are stored in "negative one frames".
These negative one frames are read only and are NOT saved when
the game is saved.
- Made the update cycle of moving entities slightly faster
by adding
built-in moving entity variable callback functions.
Let us explain with an example.
There is a moving entity engine variable
called
__tex_typical, and each frame it needs
to be recomputed from the variables __mesh
and __tex_override.
Originally, this computation happened in the moving entity rendering code
(each frame).
In a later version of the engine, we instead recalculated __tex_typical
in the update phase
(which happens 25 times per second, even if the entity was not rendered).
Now, we use the optimal method: we recalculate
__tex_typical immediately when either
of the variables __mesh or __tex_override changes.
This new method is faster, but is more complicated.
- Now when a moving entity fully turns towards the player,
it will wait one second before turning again (to make the code faster).
- Now when a child chunk is added or removed from a chunk,
certain "dirty bits" are set.
This improves the speed of the particle system update code as well as the moving entity
movement code (in the trivial case).
- In general, the moving entity update code is faster
(but more complicated).
- There are now only half as many rats in the Mylantis caves
(but there is still a huge number of them).
Version 1.02.00:
- Date: July 2026
- Added a
truly amazing secret area
which required rewriting core parts of the engine!
The community can have fun searching for and exploring this area.
- Now certain "infertile" chunks are not created at all.
This is an example of "pruning the chunk tree".
For example, consider the block type
XAR_YING_FOREST_AIR.
This consists of a single gray block in the middle,
with sometimes a grower, a shrinker, or a goblin.
Now this block type has been set to
"prune when infertile" (PWI).
When a XAR_YING_FOREST_AIR chunk exists and is fertile,
it looks normal.
However, as soon as the chunk becomes infertile,
the chunk is removed.
If we had not set it to be PWI,
then when it is infertile it would just have its single
block in the middle but no growers, shrinkers, or goblins.
We would like to thank the user enzo for suggesting
a change like this.
One might even want to remove certain fertile chunks,
but there is a problem with doing this in general.
For example, we shouldn't be allowed to remove chunks on level L
if they intersect the "envelope" of level L+1.
The envelope of level L+1 is the bounding box of that
level but extended in all directions by 2 chunks.
This new pruning feature required changing parts of the engine.
For example, there was an old rule that when a chunk C is
created, if an adjacent chunk does not exist, then we
do not create any external squares on that side of C.
We have had to modify that rule.
Also, the visray algorithm has been modified.
It is true that you can have solid blocks
be PWI, but this might result in rendering artifacts because
the depth buffer is cleared after rendering each level.
To see how to create Lua block scripts that are PWI,
see the file Data/Packages/base/WorldNodes/Nodes/block_e.lua
which is the standard Lua block script for an empty block.
- The way moving entities (ments) are rendered has changed.
Before, when it was time to render an ment, we would compute
a bunch of variables.
Now, we compute them every discrete update (which happens 25
times per second).
The downside of this new approach is we are computing vars
for ments which might not intersect the view frustum
over the next few frames. Such ments will not be rendered
(but we cannot tell in advance because we don't know if the player will
suddenly move their head).
In a future update, the plan is to NOT update these vars 25 times
per second but instead only update them when a relevant change
is made to the ment.
- Now there is a menu to turn on/off the in game
memory counter.
It can be found at Options → Engine → Debug → Memory Counter.
Once set, the program must be restarted for it to take effect.
The total memory used by the program is displayed in the main menu
in the upper left.
There is also a side display to display total memory.
- In addition to the memory counter there is a new tool
for memory debugging: the memory mom.
Every smart pointer in this program has a string name
describing the object that is pointed to.
When the memory mom is enabled, if you run the command
debug_mom_dump, it will dump to
stdout.txt the names of
all these objects and how many there are of each type.
When this mode is enabled, it will slow down the game slightly.
The memory mom will also dump twice during program shutdown
(if it is enabled).
Once set to enabled, you must restart the program for it to work.
- The xar block overrides
system has been improved.
Now when you override a xar block with a Lua block,
the texture of the Lua block will replace
the texture of the xar block.
Also, the "is solid" parameters of the Lua block
will replace those of the xar block.
To make sure that this new system is not tedious,
there is a way to copy the basic parameters
of a xar block into a Lua block.
What you do in the Lua block script is define
a function
__get_bt_to_copy
and have it return something like
"XAR_SMALL_YELLOW_FLOWER".
Then, you do not need to define the
__get_is_solid
and __get_tex function of the Lua block
(but you can if you want).
If you have the __get_bt_to_copy
function, it is required to return a string that starts with
""XAR_".
- For advanced modding there is a new body mode called "custom".
You enable this by calling the function
ga_move_set_body_custom.
When this mode is enabled, the engine does not perform any collision
detection to the player whatsoever, but the world is still rendered.