Pfft, you bring pithy diamonds as a gift? Everyone knows cocoa beans are much more rare, and thus worth a lot more than mere diamonds. I reward Jaquadro with a dozen chocolate chip cookies.
There is new data in RC2's save files called TileTicks. You might be interested, especially since you are simulating water and may want to simulate other active blocks. I made a post showing what I found out so far.
(forum links aren't working right, so here's a quote.)
Minecraft RC2 adds a new kind of saved data, TileTicks, for keeping track of the state of Redstone machines and for other purposes. When a world is first created, a TileTick is added for each "active" block including Sand, Gravel, Water and Lava (active), Leaves (decaying) and others. This can result in up to a thousand TileTicks being saved in each chunk, which is just a little bit more than MCEdit can handle while still being responsive. (A drawback of using pure Python for reading NBT data. I managed to make NBT load twice as fast by cleaning it up, but a C or Pyrex-based NBT loader would be at least ten times faster.)
A picture might give you a better idea.
In this picture, I've opened a world that was just created by the Minecraft Server. This world loads very slowly because of the TileTicks. TileTicks are the white cubes; on the right is a chunk file I extracted from this world (using Chunk Control, Extract Chunks) and then opened with NBTEdit.
Because I stopped the server as soon as it reported "Done!", it didn't get a chance to process any of the TileTicks. If I wait up to 20 seconds after "Done!" to stop the server, most of the ticks are processed.
Could those tile ticks also make redstone function correctly after being unloaded and reloaded into memory? Currently, if the redstone is running a circuit, and it gets unloaded, things will get all ;lakdsjf;laksjd'd up.
I haven't quite made it to updating with the rest of the 1.0 changes yet. If you want to give someone a modified copy of the library, that is okay, but I would encourage you to fork the project on Github and issue a pull request for changes you think are useful to all users of the library.
Hey, I'm sorting through blocks and replacing/destroying certain ones for a tool I'm working on, called Hellifier. When I try to perform these operations on a large world, (60-70MB) I get an out of memory exception. Would it be best for performance to call world.Save(); whenever a chunk is done being sorted? Would that also stop the out of memory exception?
Yes, you need to periodically call world.Save(). Once you modify a chunk, Substrate will pin it in memory forever, until it is either saved or released. This is to avoid unexpectedly losing your changes from cache contention, but you will run the risk of running out of memory if you do not manage it. Every uncompressed chunk is at least 100K in memory, and a 70MB world probably has at least 16000 chunks. The math says "that sucks".
Yes, you need to periodically call world.Save(). Once you modify a chunk, Substrate will pin it in memory forever, until it is either saved or released. This is to avoid unexpectedly losing your changes from cache contention, but you will run the risk of running out of memory if you do not manage it. Every uncompressed chunk is at least 100K in memory, and a 70MB world probably has at least 16000 chunks. The math says "that sucks".
Thanks a bunch. Now it calls a save after every chunk.
I'm a newb with C#, would you mind looking through my code and pointing out obvious faults/ways I could make it faster? If it's not a problem.
private void backgroundWorker3_DoWork(object sender, DoWorkEventArgs e)
{
BetaWorld world = BetaWorld.Open(textBox10.Text);
BetaChunkManager cm = world.GetChunkManager();
Random rng = new Random();
foreach (ChunkRef chunk in cm)
{
//Scans through blocks
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
for (int y = 0; y < 128; y++)
{
//changes blocks into other blocks based on data values
//leaves > netherrack
if (chunk.Blocks.GetID(x, y, z) == 18)
{
chunk.Blocks.SetID(x, y, z, 87);
chunk.Blocks.SetData(x, y, z, 0);
int seed1 = rng.Next(0, 3571);
if (seed1 > 3099)
{
int f = y + 1;
try
{
if (chunk.Blocks.GetID(x, f, z) == 0)
{
chunk.Blocks.SetID(x, f, z, 51);
}
}
catch
{
}
}
}
//logs > obsidian
if (chunk.Blocks.GetID(x, y, z) == 17)
{
chunk.Blocks.SetID(x, y, z, 49);
chunk.Blocks.SetData(z, y, z, 0);
}
//grass > mycelium
if (chunk.Blocks.GetID(x, y, z) == 2)
{
chunk.Blocks.SetID(x, y, z, 110);
chunk.Blocks.SetData(x, y, z, 0);
}
//stone > netherrack
//Then it uses a psuedo RNG to randomly place fire on netherrack.
if (chunk.Blocks.GetID(x, y, z) == 1)
{
chunk.Blocks.SetID(x, y, z, 87);
int seed = rng.Next(0, 3571);
int seed1 = rng.Next(0, 3571);
if (seed1 > 3243)
{
chunk.Blocks.SetID(x, y, z, 0);
}
if (seed > 2911)
{
int f = y + 1;
try
{
if (chunk.Blocks.GetID(x, f, z) == 0)
{
chunk.Blocks.SetID(x, f, z, 51);
}
}
catch
{
}
}
}
//changes grass to dead bushes.
if (chunk.Blocks.GetID(x, y, z) == 31)
{
chunk.Blocks.SetData(x, y, z, 0);
}
//sand > souldsand
if (chunk.Blocks.GetID(x, y, z) == 12)
{
chunk.Blocks.SetID(x, y, z, 88);
}
//water > lava
if (chunk.Blocks.GetID(x, y, z) == 8)
{
chunk.Blocks.SetID(x, y, z, 10);
}
//moving water > lava
if (chunk.Blocks.GetID(x, y, z) == 9)
{
chunk.Blocks.SetID(x, y, z, 10);
}
//stone bricks > nether brick
if (chunk.Blocks.GetID(x, y, z) == 98)
{
chunk.Blocks.SetID(x, y, z, 112);
//randomly destroy some stuff.
int dest = rng.Next(0, 3571);
if (dest > 2907)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//glass panes > iron bars
if (chunk.Blocks.GetID(x, y, z) == 102)
{
chunk.Blocks.SetID(x, y, z, 101);
int dest1 = rng.Next(0, 3571);
if (dest1 > 2809)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//fence > nether fence
if (chunk.Blocks.GetID(x, y, z) == 85)
{
chunk.Blocks.SetID(x, y, z, 113);
}
//Random Structure Destruction begins here:
//Wooden planks
if (chunk.Blocks.GetID(x, y, z) == 5)
{
int dest2 = rng.Next(0, 3571);
if (dest2 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Bookshelves
if (chunk.Blocks.GetID(x, y, z) == 47)
{
int dest3 = rng.Next(0, 3571);
if (dest3 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Wooden Stairs
if (chunk.Blocks.GetID(x, y, z) == 53)
{
int dest4 = rng.Next(0, 3571);
if (dest4 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Gold Blocks
if (chunk.Blocks.GetID(x, y, z) == 41)
{
int dest5 = rng.Next(0, 3571);
if (dest5 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//iron Blocks
if (chunk.Blocks.GetID(x, y, z) == 42)
{
int dest6 = rng.Next(0, 3571);
if (dest6 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Wool has a random chance of being set on fire.
if (chunk.Blocks.GetID(x, y, z) == 35)
{
int dest7 = rng.Next(0, 3571);
int yb = y++;
if (dest7 > 2959)
{
if (chunk.Blocks.GetID(x, yb, z) == 0)
{
chunk.Blocks.SetID(x, yb, z, 51);
}
}
}
//Cobblestone
if (chunk.Blocks.GetID(x, y, z) == 4)
{
int dest8 = rng.Next(0, 3571);
if (dest8 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
if (dest8 < 958)
{
chunk.Blocks.SetID(x, y, z, 48);
}
}
//Clay Brick
if (chunk.Blocks.GetID(x, y, z) == 45)
{
int dest9 = rng.Next(0, 3571);
if (dest9 > 2592)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Clay Brick Stairs
if (chunk.Blocks.GetID(x, y, z) == 108)
{
int dest10 = rng.Next(0, 3571);
if (dest10 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Stone Brick Stairs:
if (chunk.Blocks.GetID(x, y, z) == 109)
{
int dest11 = rng.Next(0, 3571);
if (dest11 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Cobble Stairs:
if (chunk.Blocks.GetID(x, y, z) == 67)
{
int dest12 = rng.Next(0, 3571);
if (dest12 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Lapis Lazuli Block
if (chunk.Blocks.GetID(x, y, z) == 22)
{
int dest13 = rng.Next(0, 3571);
if (dest13 > 2919)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Ladders
if (chunk.Blocks.GetID(x, y, z) == 65)
{
int dest14 = rng.Next(0, 3571);
if (dest14 > 2200)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Minecart Tracks
if (chunk.Blocks.GetID(x, y, z) == 66)
{
int dest15 = rng.Next(0, 3571);
if (dest15 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Endstone
if (chunk.Blocks.GetID(x, y, z) == 24)
{
int dest16 = rng.Next(0, 3571);
if (dest16 > 2001)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
//Deletes all vines
if (chunk.Blocks.GetID(x, y, z) == 106)
{
chunk.Blocks.SetID(x, y, z, 0);
}
}
}
}
world.Save();
}
MessageBox.Show("Done!");
}
It's basically meant to turn a nice world into a hellish world xD
Well, one obvious issue is that each pass is going to try executing every if block. That means if a block gets replaced early on, it may get replaced again with something else further down. You should also avoid all those repeated calls to chunk.Blocks.GetID. Instead get the ID once and put it into a variable, then use that in your comparisons. You'd probably be better served by a switch statement.
switch (chunk.Blocks.GetID(x, y, z)) {
case BlockType.LEAVES:
// replace leaf code...
break;
case BlockType.GRASS:
// replace grass code...
break;
}
One gotcha is that if you define new variables in one of the case statements (int seed = ....), the block of code needs to be surrounded by curly braces like your if statements.
Well, one obvious issue is that each pass is going to try executing every if block. That means if a block gets replaced early on, it may get replaced again with something else further down. You should also avoid all those repeated calls to chunk.Blocks.GetID. Instead get the ID once and put it into a variable, then use that in your comparisons. You'd probably be better served by a switch statement.
switch (chunk.Blocks.GetID(x, y, z)) {
case BlockType.LEAVES:
// replace leaf code...
break;
case BlockType.GRASS:
// replace grass code...
break;
}
One gotcha is that if you define new variables in one of the case statements (int seed = ....), the block of code needs to be surrounded by curly braces like your if statements.
Ah, thanks. I knew all those if-statements were not meant to be :dry.gif:
private void backgroundWorker3_DoWork(object sender, DoWorkEventArgs e)
{
BetaWorld world = BetaWorld.Open(textBox10.Text);
BetaChunkManager cm = world.GetChunkManager();
Random rng = new Random();
foreach (ChunkRef chunk in cm)
{
//Scans through blocks
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
for (int y = 0; y < 128; y++)
{
//changes blocks into other blocks based on data values
//leaves > netherrack
switch (chunk.Blocks.GetID(x, y, z))
{
case BlockType.LEAVES:
{
chunk.Blocks.SetID(x, y, z, BlockType.NETHERRACK);
chunk.Blocks.SetData(x, y, z, 0);
int seed1 = rng.Next(0, 3571);
if (seed1 > 3099)
{
int f = y + 1;
try
{
if (chunk.Blocks.GetID(x, f, z) == BlockType.AIR)
{
chunk.Blocks.SetID(x, f, z, BlockType.FIRE);
}
}
catch
{
}
}
break;
}
//logs > obsidian
case BlockType.WOOD:
{
chunk.Blocks.SetID(x, y, z, BlockType.OBSIDIAN);
chunk.Blocks.SetData(z, y, z, 0);
break;
}
//grass > mycelium
case BlockType.GRASS:
{
chunk.Blocks.SetID(x, y, z, BlockType.MYCELIUM);
chunk.Blocks.SetData(x, y, z, 0);
break;
}
//stone > netherrack
//Then it uses a psuedo RNG to randomly place fire on netherrack.
case BlockType.STONE:
{
chunk.Blocks.SetID(x, y, z, BlockType.NETHERRACK);
int seed = rng.Next(0, 3571);
int seed1 = rng.Next(0, 3571);
if (seed1 > 3243)
{
chunk.Blocks.SetID(x, y, z, BlockType.AIR);
}
if (seed > 2911)
{
int f = y + 1;
try
{
if (chunk.Blocks.GetID(x, f, z) == BlockType.AIR)
{
chunk.Blocks.SetID(x, f, z, 51);
}
}
catch
{
}
}
break;
}
//Tall grass > dead bushes
case BlockType.TALL_GRASS:
chunk.Blocks.SetData(x, y, z, 0);
break;
//sand > souldsand
case BlockType.SAND:
chunk.Blocks.SetID(x, y, z, BlockType.SOUL_SAND);
break;
//water > lava
case BlockType.STATIONARY_WATER:
chunk.Blocks.SetID(x, y, z, BlockType.STATIONARY_LAVA);
break;
//Moving water > stationary lava (creates a cool affect with the lava)
case BlockType.WATER:
chunk.Blocks.SetID(x, y, z, BlockType.STATIONARY_LAVA);
break;
//stone bricks > nether brick
case BlockType.STONE_BRICK:
{
chunk.Blocks.SetID(x, y, z, 112);
//randomly destroy some stuff.
int dest = rng.Next(0, 3571);
if (dest > 2907)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//glass panes > iron bars
case BlockType.GLASS_PANE:
{
chunk.Blocks.SetID(x, y, z, 101);
int dest1 = rng.Next(0, 3571);
if (dest1 > 2809)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//fence > nether fence
case BlockType.FENCE:
{
chunk.Blocks.SetID(x, y, z, 113);
break;
}
//Random Structure Destruction begins here:
//Wooden planks
case BlockType.WOOD_PLANK:
{
int dest2 = rng.Next(0, 3571);
if (dest2 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
int fire = rng.Next(0, 3571);
int yf = y++;
if (fire > 3402)
{
chunk.Blocks.SetID(x, yf, z, BlockType.FIRE);
}
break;
}
case BlockType.BOOKSHELF:
{
//Bookshelves
int dest3 = rng.Next(0, 3571);
if (dest3 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Wooden Stairs
case BlockType.WOOD_STAIRS:
{
int dest4 = rng.Next(0, 3571);
if (dest4 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Gold Blocks
case BlockType.GOLD_BLOCK:
{
int dest5 = rng.Next(0, 3571);
if (dest5 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//iron Blocks
case BlockType.IRON_BLOCK:
{
int dest6 = rng.Next(0, 3571);
if (dest6 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Wool has a random chance of being set on fire.
case BlockType.WOOL:
{
int dest7 = rng.Next(0, 3571);
int yb = y++;
if (dest7 > 2959)
{
if (chunk.Blocks.GetID(x, yb, z) == 0)
{
chunk.Blocks.SetID(x, yb, z, 51);
}
}
break;
}
//Cobblestone
case BlockType.COBBLESTONE:
{
chunk.Blocks.SetID(x, y, z, BlockType.NETHER_BRICK);
int dest8 = rng.Next(0, 3571);
if (dest8 > 2023)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Clay Brick
case BlockType.BRICK_BLOCK:
{
int dest9 = rng.Next(0, 3571);
if (dest9 > 2592)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Clay Brick Stairs
case BlockType.BRICK_STAIRS:
{
int dest10 = rng.Next(0, 3571);
if (dest10 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Stone Brick Stairs:
case BlockType.STONE_BRICK_STAIRS:
{
int dest11 = rng.Next(0, 3571);
if (dest11 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Cobble Stairs:
case BlockType.COBBLESTONE_STAIRS:
{
int dest12 = rng.Next(0, 3571);
if (dest12 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Lapis Lazuli Block
case BlockType.LAPIS_BLOCK:
{
int dest13 = rng.Next(0, 3571);
if (dest13 > 2919)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Ladders
case BlockType.LADDER:
{
int dest14 = rng.Next(0, 3571);
if (dest14 > 2200)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Minecart Tracks
case BlockType.RAILS:
{
int dest15 = rng.Next(0, 3571);
if (dest15 > 2513)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Sandstone
case BlockType.SANDSTONE:
{
int dest16 = rng.Next(0, 3571);
if (dest16 > 2001)
{
chunk.Blocks.SetID(x, y, z, 0);
}
break;
}
//Deletes all vines
case BlockType.VINES:
{
chunk.Blocks.SetID(x, y, z, 0);
break;
}
case BlockType.SNOW:
{
chunk.Blocks.SetID(x, y, z, BlockType.AIR);
break;
}
}
//Gravity begins here
switch (chunk.Blocks.GetID(x, y, z))
{
case BlockType.NETHER_BRICK:
{
int x1 = x;
int y1 = y;
int y2 = y;
int z1 = z;
y1--;
while (chunk.Blocks.GetID(x1, y1, z1) == 0)
{
chunk.Blocks.SetID(x, y1, z, BlockType.NETHER_BRICK);
chunk.Blocks.SetID(x, y2, z, BlockType.AIR);
y1--;
y2--;
}
world.Save();
break;
}
}
}
}
}
world.Save();
}
MessageBox.Show("Done!");
}
Any idea on how I could simulate gravity on buildings and whatnot?
EDIT: After a few hours of fumbling around, I figured out how to simulate gravity. This should create some interesting ruins
Another edit:
Here are some before and after pics:
For those who need the updated level and player NBT entries, most of them have been added and pushed the repository now. The remainder of 1.0 changes may get in this weekend if I have the time.
Version 1.0.0 is released. I will most likely continue to synchronize versions with Minecraft from this point forward (excluding Minecraft maintenance releases).
There's been some structural changes in the project. Ionic.Zlib is now integrated into the Substrate assemblies instead of packing separately. I'm also now shipping two separate Substrate assemblies, one each for .NET-2.0 and .NET-4.0, respectively. Pick the assembly version that most closely matches the target framework of your own application. This has the advantage of not requiring your users to have both .NET-2.0 and .NET-4.0 frameworks installed if your application is built against .NET-4.0, for example. (It's weird but apparently there are people with .NET-4.0 but not .NET-2.0). For reference, Substrate has always been built against .NET-2.0 up to this point.
Feature-wise, the major addition is an Enchantments API. Semi-complete changeset:
- Ionic.Zlib integrated into the Substrate assembly
- Project split into .NET2/.NET4 output
- Added full Enchantment API for items
- Player and Mob classes updated with additional data
- Entity/TileEntity registires are enumerable
- ChangeValueType added to TagNodeList class
Also, I'd like to build a list of interesting projects using Substrate in the main post - especially of projects that have their source code available to the public. If you've released a project and would like it listed, say so.
The nub here again, once again asking for help with optimization. :dry.gif:
I'm working on some code to apply gravity. It works, it's just that it's very slow when applying it across all blocks. Would anyone mind helping me optimize it? :biggrin.gif:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Substrate;
namespace mctb
{
public class physics
{
public static void gravify(BetaWorld world, BetaChunkManager cm, ChunkRef chunk, int x, int y, int z, int affectedblock, int newblock = 0)
{
int x1 = x;
int y1 = y;
int y2 = y;
int z1 = z;
int[] nonsolid = { 0, 6, 18, 30, 31, 37, 38, 39, 40, 50, 75, 76, 69, 77, 115, 10, 8, 9, 10, 11, 51, 106, 83, 111 };
int[] breakblocks = { 6, 18, 31, 30, 37, 38, 39, 40, 69, 75, 76 };
if (newblock == 0)
{
newblock = affectedblock;
}
y1--;
//scans down until it finds a block that does not equal air.
try
{
while (nonsolid.Contains(chunk.Blocks.GetID(x1, y1, z1)))
{
y1--;
y2--;
if (breakblocks.Contains(chunk.Blocks.GetID(x, y1, z)))
{
chunk.Blocks.SetID(x, y1, z, BlockType.AIR);
}
if (nonsolid.Contains(chunk.Blocks.GetID(x1, y1, z1)) == false)
{
chunk.Blocks.SetID(x, y2, z, newblock);
chunk.Blocks.SetID(x, y, z, BlockType.AIR);
break;
}
}
//saves the world after each block to avoid everything hanging around in memory.
world.Save();
}
catch
{
}
}
}
}
I don't really understand what you're trying to do, so maybe you should explain the current flow of your code. Especially, how often is that function called -- once per X/Z coordinate, or once for every block in a chunk?
Potentially, calling world.Save() in this method is your biggest killer, because I'm assuming you're calling that method 16*16 or 16*16*128 times. You really don't want to call Save() more than once per chunk processed, if you can help it. You may also be calling that method more often than you need to. There are other data structures in the chunk, like Sunlight, which you could use to help prune the space you even need to consider looking at.
Aggreed diamonds to Jaquadro
(forum links aren't working right, so here's a quote.)
"We will absolutely not keep in mind what external mapeditors will have to do to read data from the disk, that makes no sense whatsoever." - Grum
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
https://github.com/jaquadro/Substrate
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
Thanks a bunch. Now it calls a save after every chunk.
I'm a newb with C#, would you mind looking through my code and pointing out obvious faults/ways I could make it faster? If it's not a problem.
One gotcha is that if you define new variables in one of the case statements (int seed = ....), the block of code needs to be surrounded by curly braces like your if statements.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
Ah, thanks. I knew all those if-statements were not meant to be :dry.gif:
Yeah, I didn't realize that was there, so I was just hard-coding the numbers Dx
EDIT: After a few hours of fumbling around, I figured out how to simulate gravity. This should create some interesting ruins
Another edit:
Here are some before and after pics:
after:
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
There's been some structural changes in the project. Ionic.Zlib is now integrated into the Substrate assemblies instead of packing separately. I'm also now shipping two separate Substrate assemblies, one each for .NET-2.0 and .NET-4.0, respectively. Pick the assembly version that most closely matches the target framework of your own application. This has the advantage of not requiring your users to have both .NET-2.0 and .NET-4.0 frameworks installed if your application is built against .NET-4.0, for example. (It's weird but apparently there are people with .NET-4.0 but not .NET-2.0). For reference, Substrate has always been built against .NET-2.0 up to this point.
Feature-wise, the major addition is an Enchantments API. Semi-complete changeset:
- Ionic.Zlib integrated into the Substrate assembly
- Project split into .NET2/.NET4 output
- Added full Enchantment API for items
- Player and Mob classes updated with additional data
- Entity/TileEntity registires are enumerable
- ChangeValueType added to TagNodeList class
Also, I'd like to build a list of interesting projects using Substrate in the main post - especially of projects that have their source code available to the public. If you've released a project and would like it listed, say so.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
One more internet to you, good sir.
I'm working on some code to apply gravity. It works, it's just that it's very slow when applying it across all blocks. Would anyone mind helping me optimize it? :biggrin.gif:
Any help/suggestions would be great.
Potentially, calling world.Save() in this method is your biggest killer, because I'm assuming you're calling that method 16*16 or 16*16*128 times. You really don't want to call Save() more than once per chunk processed, if you can help it. You may also be calling that method more often than you need to. There are other data structures in the chunk, like Sunlight, which you could use to help prune the space you even need to consider looking at.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate