This includes the Substrate.Data namespace for editing maps (and more generally data resources, if new resource types are added in the future). The MapManager functions similarly to the PlayerManager, and there is a separate MapConverter utility class for color and conversion operations. The MapConverter will handle converting an arbitrary System.Drawing.Bitmap object, so you can pretty much use the entire System.Drawing assembly for all of your drawing needs for maps without having to worry so much about the conversion aspects. Maps can also be converted into Bitmaps for editing or export.
In the next release, I'll include a static enumerator on EntityFactory and TileEntityFactory so you can procedurally generate the list of all registered types.
In the next release, I'll include a static enumerator on EntityFactory and TileEntityFactory so you can procedurally generate the list of all registered types.
EDIT: Ignore this post, I noticed I was retrieving a Chunk and not ChunkRef in my helper functions. Changed to ChunkRef and it's fine now. Post left to avoid "wtf, I could have swore I saw..." moments.
I've got an odd issue and I'm not sure if it's because I am failing to call a .Save method or if there's a bug in Substrate. I'm using Substrate in a server, and I'm attempting to persist metadata for a block, specifically a fence gate. The meta data is comprised of bit 2 being whether the gate is open or closed, and bits 0 and 1 holding the facing direction of the gate.
With the code here, it simply looks to toggle the gate's open/closed state and then persist to the current chunk the gate is in; however, once the execution context passes the case statement and the user toggles the gate again, analysis of the block's metadata shows that the value has reverted.
Position pos = PlaceItem.Position;
AlphaBlockRef b = GetBlockAtPosition(pos);
if (b.ID == 107) // fence gate
{
// If I check b.Data here, it's always 1 (the gate direction) even after I have set the metadata previously by clicking the gate in-game.
byte IsOpen = (b.Data >> 2) & 1; // stored at bit 2
// here, IsOpen == 0 since the gate is closed
byte Direction = (b.Data & 3); // stored in bits 0-1
// Direction == 1, the gate faces west
IsOpen = (IsOpen == 0 ? 1 : 0); // simple toggle, not using bool due to << usage
// IsOpen is now 1, gate is open
b.Data = (IsOpen << 2) | Direction;
// b.Data == 5, which shows correct bit setting
SetBlockMetaData(pos, b.Data); // Persist the new metadata value
c.Send(BlockChange.Create(pos.X, pos.Y, pos.Z, b.ID, b.Data));
// If I retrieve the block metadata here, it does show that it is indeed 5
break;
}
Elsewhere in the project:
public AlphaBlockRef GetBlockAtPosition(Position p)
{
// here, p.World is { 278, 64, 117 }, p.Block is { 6, 64, 5 } and p.Chunk is { 17, 7 }
Chunk ch = m_World.GetChunkManager.GetChunk(p.ChunkX, p.ChunkZ);
return ch.Blocks.GetBlockRef(p.BlockX, p.BlockY, p.BlockZ);
}
private void SetBlockMetadata(Position p, byte
{
// verified position values, p.World is { 278, 64, 117 }, p.Block is { 6, 64, 5 } and p.Chunk is { 17, 7 }
// so the correct chunk is being retrieved and the correct block data is being set as far as I can tell
Chunk ch = m_World.GetChunkManager.GetChunk(p.ChunkX, p.ChunkZ);
ch.Blocks.SetData(p.BlockX, p.BlockY, p.BlockZ, B);
}
Soooo.... Do I need to call Chunk.Save each and every time I change a metadata value?
It's surprising that there is no comprehensive Java SDK, considering MineCraft is itself written in Java, but maybe current practice is to directly use de-obfuscated source files from MineCraft for basic stuff? I have no idea. The region code was also released directly which is the biggest obstacle to doing anything useful.
At this time I don't have plans for porting to Java. Porting Java code to C# is mostly a trivial process as Java code can almost be dropped-in and directly compiled as C# code (ignoring the obviously different standard libraries). The same is not true for the reverse process. C# contains additional language features that have no direct equivalent in Java and are thus tedious to convert.
Okay, I think I have a valid, non-user error issue here. I keep randomly, yes randomly, getting a exception that is based in the zip library that Substrate uses. I forgot to get the stack trace, and there is no common point or way to trigger the exception, but I've had it happen three times now.
This is with a 1.7.3 created map that I have not loaded into a 1.8.1 server yet. The first exception was with 0x00 and happened when I broke a dirt block on the roof of a temp shelter I had. The second I can't recall, the third was when accessing a ChunkRef.Blocks.XDim variable. The ChunkRef was not null and it was in a definitely existant chunk. I reloaded the server and went to the same place and did not encounter the exception. i also was not able to reproduce the first exception by breaking the same dirt block. Since with the compiled Substrate dll a programmer has no access to the underlying FileStream, the fix is going to have to be done by the Substrate author :smile.gif:
Exception: Bad state (unknown compression method (0x17))
I will try to remember to get the stacktrace next time, but in the meantime, has anyone else encountered this issue? I'm guessing it's rooted in the Iconic.Zip dll, but at the moment I'm a bit too busy to remove the Iconic.Zip reference and replace it with ICSharpCode.SharpZipLib as a way to test it out.
Edit: I removed the reference to Substrate.dll and instead added the 0.9.0 project to my solution and referenced the project so that I'd have source code to view when an exception occured. Here's what I think the exception is from. The stacktrace does not show the execution context, but when I looked at the call stack, it jogged my memory. My first exception popped up from the same function. I have a second thread running that manages crop/plant growth, and it appears that since FileStreams are not thread safe, the thread interactions are causing the exception. Just a suspicion, though. Try using a temporary threadsafe stream obtained from the static FileStream.Synchronized method which takes a stream as a parameter.
System.IndexOutOfRangeException occurred
Message=Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.
Source=mscorlib
StackTrace:
at System.Buffer.InternalBlockCopy(Array src, Int32 srcOffsetBytes, Array dst, Int32 dstOffsetBytes, Int32 byteCount)
at System.IO.FileStream.Read(Byte[] array, Int32 offset, Int32 count)
at Substrate.Core.RegionFile.GetChunkDataInputStream(Int32 x, Int32 z) in C:\Users\Lycaon\Desktop\Substrate\SubstrateCS\Source\Core\RegionFile.cs:line 251
InnerException:
Here is another exception, different than the previous one I posted.
This is when attempting to retrieve a ChunkRef for a chunk at { 15, 4 } and this chunk definitely exists, since I had just previously logged in at the same chunk about 1m30s earlier. Currently not reproducible.
KeyNotFoundException occured {"The given key was not present in the dictionary."}
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Substrate.Core.LRUCache`2.set_Item(TKey key, TValue value)
at Substrate.Core.ChunkCache.Insert(ChunkRef chunk)
at Substrate.Region.GetChunkRef(Int32 lcx, Int32 lcz)
at Substrate.BetaChunkManager.GetChunkRef(Int32 cx, Int32 cz)
at AnotherMinecraftServer.Client.SendChunksAsync(Object args)
Nothing in Substrate is thread-safe. If you have more than one thread simultaneously doing anything with chunks, including reading them, it could corrupt the underlying LRUCache, because the cache itself is built on plain old lists and dictionaries which are not thread-safe, and reading chunks causes writes to the cache. I can't think of any other reason for random, unreproducable key errors coming from the chunk cache.
Nothing in Substrate is thread-safe. If you have more than one thread simultaneously doing anything with chunks, including reading them, it could corrupt the underlying LRUCache, because the cache itself is built on plain old lists and dictionaries which are not thread-safe, and reading chunks causes writes to the cache. I can't think of any other reason for random, unreproducable key errors coming from the chunk cache.
I'll take that into consideration. I might have already found a workaround but I'm not sure yet until I actually test it. I know Substrate probably wasn't intended to be used in a server environment but it's bound to happen eventually (if it already hasn't). I'm still not convinced that explains the Zip exceptions, but I think my theoretical explanation is on the right track.
I would not anticipate zip errors unless you got your own version of the Ionic.Zlib source code, which last I checked, still had some bugs which are patched in the DLL that I distribute. If you have something reproduceable, then send me the world that is causing problems, otherwise I have a difficult time believing the Zlib library is at fault here.
I would not anticipate zip errors unless you got your own version of the Ionic.Zlib source code, which last I checked, still had some bugs which are patched in the DLL that I distribute. If you have something reproduceable, then send me the world that is causing problems, otherwise I have a difficult time believing the Zlib library is at fault here.
I'd have thought that the exception Exception: Bad state (unknown compression method (0x17)) would point to the zip library, being a compression library and all. My app certainly doesn't make direct reference to Iconic's library, nor any other compression code for that matter.
This includes the Substrate.Data namespace for editing maps (and more generally data resources, if new resource types are added in the future). The MapManager functions similarly to the PlayerManager, and there is a separate MapConverter utility class for color and conversion operations. The MapConverter will handle converting an arbitrary System.Drawing.Bitmap object, so you can pretty much use the entire System.Drawing assembly for all of your drawing needs for maps without having to worry so much about the conversion aspects. Maps can also be converted into Bitmaps for editing or export.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
Thanks =)
In the next release, I'll include a static enumerator on EntityFactory and TileEntityFactory so you can procedurally generate the list of all registered types.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
Thanks a lot!
I've got an odd issue and I'm not sure if it's because I am failing to call a .Save method or if there's a bug in Substrate. I'm using Substrate in a server, and I'm attempting to persist metadata for a block, specifically a fence gate. The meta data is comprised of bit 2 being whether the gate is open or closed, and bits 0 and 1 holding the facing direction of the gate.
With the code here, it simply looks to toggle the gate's open/closed state and then persist to the current chunk the gate is in; however, once the execution context passes the case statement and the user toggles the gate again, analysis of the block's metadata shows that the value has reverted.
Position pos = PlaceItem.Position;
AlphaBlockRef b = GetBlockAtPosition(pos);
if (b.ID == 107) // fence gate
{
// If I check b.Data here, it's always 1 (the gate direction) even after I have set the metadata previously by clicking the gate in-game.
byte IsOpen = (b.Data >> 2) & 1; // stored at bit 2
// here, IsOpen == 0 since the gate is closed
byte Direction = (b.Data & 3); // stored in bits 0-1
// Direction == 1, the gate faces west
IsOpen = (IsOpen == 0 ? 1 : 0); // simple toggle, not using bool due to << usage
// IsOpen is now 1, gate is open
b.Data = (IsOpen << 2) | Direction;
// b.Data == 5, which shows correct bit setting
SetBlockMetaData(pos, b.Data); // Persist the new metadata value
c.Send(BlockChange.Create(pos.X, pos.Y, pos.Z, b.ID, b.Data));
// If I retrieve the block metadata here, it does show that it is indeed 5
break;
}
Elsewhere in the project:
public AlphaBlockRef GetBlockAtPosition(Position p)
{
// here, p.World is { 278, 64, 117 }, p.Block is { 6, 64, 5 } and p.Chunk is { 17, 7 }
Chunk ch = m_World.GetChunkManager.GetChunk(p.ChunkX, p.ChunkZ);
return ch.Blocks.GetBlockRef(p.BlockX, p.BlockY, p.BlockZ);
}
private void SetBlockMetadata(Position p, byte
{
// verified position values, p.World is { 278, 64, 117 }, p.Block is { 6, 64, 5 } and p.Chunk is { 17, 7 }
// so the correct chunk is being retrieved and the correct block data is being set as far as I can tell
Chunk ch = m_World.GetChunkManager.GetChunk(p.ChunkX, p.ChunkZ);
ch.Blocks.SetData(p.BlockX, p.BlockY, p.BlockZ, B);
}
Soooo.... Do I need to call Chunk.Save each and every time I change a metadata value?
Thank you!
That would take a while. I'm surprised someone hasn't made an equivalent for Java.
At this time I don't have plans for porting to Java. Porting Java code to C# is mostly a trivial process as Java code can almost be dropped-in and directly compiled as C# code (ignoring the obviously different standard libraries). The same is not true for the reverse process. C# contains additional language features that have no direct equivalent in Java and are thus tedious to convert.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
This is with a 1.7.3 created map that I have not loaded into a 1.8.1 server yet. The first exception was with 0x00 and happened when I broke a dirt block on the roof of a temp shelter I had. The second I can't recall, the third was when accessing a ChunkRef.Blocks.XDim variable. The ChunkRef was not null and it was in a definitely existant chunk. I reloaded the server and went to the same place and did not encounter the exception. i also was not able to reproduce the first exception by breaking the same dirt block. Since with the compiled Substrate dll a programmer has no access to the underlying FileStream, the fix is going to have to be done by the Substrate author :smile.gif:
Exception: Bad state (unknown compression method (0x17))
I will try to remember to get the stacktrace next time, but in the meantime, has anyone else encountered this issue? I'm guessing it's rooted in the Iconic.Zip dll, but at the moment I'm a bit too busy to remove the Iconic.Zip reference and replace it with ICSharpCode.SharpZipLib as a way to test it out.
Edit: I removed the reference to Substrate.dll and instead added the 0.9.0 project to my solution and referenced the project so that I'd have source code to view when an exception occured. Here's what I think the exception is from. The stacktrace does not show the execution context, but when I looked at the call stack, it jogged my memory. My first exception popped up from the same function. I have a second thread running that manages crop/plant growth, and it appears that since FileStreams are not thread safe, the thread interactions are causing the exception. Just a suspicion, though. Try using a temporary threadsafe stream obtained from the static FileStream.Synchronized method which takes a stream as a parameter.
System.IndexOutOfRangeException occurred
Message=Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.
Source=mscorlib
StackTrace:
at System.Buffer.InternalBlockCopy(Array src, Int32 srcOffsetBytes, Array dst, Int32 dstOffsetBytes, Int32 byteCount)
at System.IO.FileStream.Read(Byte[] array, Int32 offset, Int32 count)
at Substrate.Core.RegionFile.GetChunkDataInputStream(Int32 x, Int32 z) in C:\Users\Lycaon\Desktop\Substrate\SubstrateCS\Source\Core\RegionFile.cs:line 251
InnerException:
This is when attempting to retrieve a ChunkRef for a chunk at { 15, 4 } and this chunk definitely exists, since I had just previously logged in at the same chunk about 1m30s earlier. Currently not reproducible.
KeyNotFoundException occured {"The given key was not present in the dictionary."}
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Substrate.Core.LRUCache`2.set_Item(TKey key, TValue value)
at Substrate.Core.ChunkCache.Insert(ChunkRef chunk)
at Substrate.Region.GetChunkRef(Int32 lcx, Int32 lcz)
at Substrate.BetaChunkManager.GetChunkRef(Int32 cx, Int32 cz)
at AnotherMinecraftServer.Client.SendChunksAsync(Object args)
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
I'll take that into consideration. I might have already found a workaround but I'm not sure yet until I actually test it. I know Substrate probably wasn't intended to be used in a server environment but it's bound to happen eventually (if it already hasn't). I'm still not convinced that explains the Zip exceptions, but I think my theoretical explanation is on the right track.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate
I'd have thought that the exception Exception: Bad state (unknown compression method (0x17)) would point to the zip library, being a compression library and all. My app certainly doesn't make direct reference to Iconic's library, nor any other compression code for that matter.
Mods I Develop: Garden Stuff -- Storage Drawers -- Hunger Strike
Tools I Develop: NBTExplorer -- Substrate