Create .lvl and .fcm level exporter.

This commit is contained in:
UnknownShadow200 2016-05-08 15:39:49 +10:00
parent d74dc198e9
commit f5878f70a3
8 changed files with 157 additions and 8 deletions

View file

@ -174,6 +174,8 @@
<Compile Include="Generator\NotchyGenerator.cs" />
<Compile Include="Generator\NotchyGenerator.Utils.cs" />
<Compile Include="GraphicsAPI\OpenGLESApi.cs" />
<Compile Include="Map\Formats\MapFcm3.Exporter.cs" />
<Compile Include="Map\Formats\MapLvl.Exporter.cs" />
<Compile Include="Map\Formats\MapLvl.Importer.cs" />
<Compile Include="Map\Formats\NbtFile.cs" />
<Compile Include="Math\RayTracer.cs" />
@ -208,7 +210,7 @@
<Compile Include="Map\ChunkMeshBuilder.cs" />
<Compile Include="Map\ChunkMeshBuilder.FloodFill.cs" />
<Compile Include="Map\ChunkMeshBuilderTex2Col4.cs" />
<Compile Include="Map\Formats\IMapFileFormat.cs" />
<Compile Include="Map\Formats\IMapFormat.cs" />
<Compile Include="Map\Formats\MapCw.Exporter.cs" />
<Compile Include="Map\Formats\MapCw.Importer.cs" />
<Compile Include="Map\Formats\MapDat.Importer.cs" />

View file

@ -67,11 +67,11 @@ namespace ClassicalSharp.Map {
nbt.Write( NbtTagType.Int8 );
nbt.Write( "H" );
nbt.WriteUInt8( (byte)Utils.DegreesToPacked( p.SpawnYaw, 256 ) );
nbt.WriteUInt8( (byte)Utils.DegreesToPacked( p.SpawnYaw ) );
nbt.Write( NbtTagType.Int8 );
nbt.Write( "P" );
nbt.WriteUInt8( (byte)Utils.DegreesToPacked( p.SpawnPitch, 256 ) );
nbt.WriteUInt8( (byte)Utils.DegreesToPacked( p.SpawnPitch ) );
nbt.Write( NbtTagType.End );
}

View file

@ -0,0 +1,69 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
// Part of fCraft | Copyright (c) 2009-2014 Matvei Stefarov <me@matvei.org> | BSD-3 | See LICENSE.txt
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
using ClassicalSharp.Entities;
namespace ClassicalSharp.Map {
/// <summary> Exports a world to a FCMv3 map file (fCraft server map) </summary>
public sealed class MapFcm3Exporter : IMapFormatExporter {
const uint Identifier = 0x0FC2AF40;
const byte Revision = 13;
const int chunkSize = 128 * 128 * 128;
public void Save( Stream stream, Game game ) {
World map = game.World;
LocalPlayer p = game.LocalPlayer;
BinaryWriter writer = new BinaryWriter( stream );
writer.Write( Identifier );
writer.Write( Revision );
writer.Write( (short)map.Width );
writer.Write( (short)map.Height );
writer.Write( (short)map.Length );
writer.Write( (int)(p.Spawn.X * 32) );
writer.Write( (int)(p.Spawn.Y * 32) );
writer.Write( (int)(p.Spawn.Z * 32) );
writer.Write( Utils.DegreesToPacked( p.SpawnYaw ) );
writer.Write( Utils.DegreesToPacked( p.SpawnPitch ) );
writer.Write( 0 ); // Date modified
writer.Write( 0 ); // Date created
writer.Write( map.Uuid.ToByteArray() );
writer.Write( (byte)1 ); // layer count
// skip over index and metacount
long indexOffset = stream.Position;
writer.Seek( 29, SeekOrigin.Current );
long offset = stream.Position;
byte[] blocks = map.mapData;
using( DeflateStream ds = new DeflateStream( stream, CompressionMode.Compress, true ) ) {
for( int i = 0; i < blocks.Length; i += chunkSize ) {
int len = Math.Min( chunkSize, blocks.Length - i );
ds.Write( blocks, i, len );
}
}
int compressedLength = (int)(stream.Position - offset);
// come back to write the index
writer.BaseStream.Seek( indexOffset, SeekOrigin.Begin );
writer.Write( (byte)0 ); // data layer type (Blocks)
writer.Write( offset ); // offset, in bytes, from start of stream
writer.Write( compressedLength ); // compressed length, in bytes
writer.Write( 0 ); // general purpose field
writer.Write( 1 ); // element size
writer.Write( map.mapData.Length ); // element count
writer.Write( 0 ); // No metadata
}
}
}

View file

@ -0,0 +1,72 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
using ClassicalSharp.Entities;
using ClassicalSharp.Net;
namespace ClassicalSharp.Map {
/// <summary> Exports a world to a LVL map file (MCLawl server map) </summary>
public sealed class MapLvlExporter : IMapFormatExporter {
const int Version = 1874;
const byte customTile = 163;
public void Save( Stream stream, Game game ) {
World map = game.World;
LocalPlayer p = game.LocalPlayer;
const int chunkSize = 128 * 128 * 128;
using( DeflateStream gs = new DeflateStream( stream, CompressionMode.Compress ) ) {
BinaryWriter writer = new BinaryWriter( gs );
writer.Write( (ushort)Version );
writer.Write( (ushort)map.Width );
writer.Write( (ushort)map.Length );
writer.Write( (ushort)map.Height );
writer.Write( (ushort)p.Spawn.X );
writer.Write( (ushort)p.Spawn.Z );
writer.Write( (ushort)p.Spawn.Y );
writer.Write( Utils.DegreesToPacked( p.SpawnYaw ) );
writer.Write( Utils.DegreesToPacked( p.SpawnPitch ) );
writer.Write( (ushort)0 ); // pervisit and perbuild perms
WriteBlocks( map.mapData, writer );
writer.Write( (byte)0xBD );
WriteCustomBlocks( map, writer );
}
}
void WriteBlocks( byte[] blocks, BinaryWriter writer ) {
const int bufferSize = 64 * 1024;
byte[] buffer = new byte[bufferSize];
int bIndex = 0;
for( int i = 0; i < blocks.Length; i++ ) {
byte block = blocks[i];
buffer[bIndex] = block >= BlockInfo.CpeBlocksCount ? customTile : block;
bIndex++;
if( bIndex == bufferSize ) {
writer.Write( buffer, 0, bufferSize ); bIndex = 0;
}
}
if( bIndex > 0 ) writer.Write( buffer, 0, bIndex );
}
void WriteCustomBlocks( World map, BinaryWriter writer ) {
byte[] chunk = new byte[16 * 16 * 16];
for( int y = 0; y < map.Height; y += 16 )
for( int z = 0; z < map.Length; z += 16 )
for( int x = 0; x < map.Width; x += 16 )
{
writer.Write( (byte)0 );
}
}
}
}

View file

@ -13,6 +13,8 @@ namespace ClassicalSharp.Map {
public sealed class MapLvlImporter : IMapFormatImporter {
const int Version = 1874;
const byte customTile = 163;
public byte[] Load( Stream stream, Game game, out int width, out int height, out int length ) {
GZipHeaderReader gsHeader = new GZipHeaderReader();
while( !gsHeader.ReadHeader( stream ) ) { }
@ -59,7 +61,7 @@ namespace ClassicalSharp.Map {
int bx = i & 0xF, by = (i >> 8) & 0xF, bz = (i >> 4) & 0xF;
int index = baseIndex + (by * length + bz) * width + bx;
if( blocks[index] == 163 ) // custom block id
if( blocks[index] == customTile )
blocks[index] = chunk[i];
}
}
@ -82,8 +84,8 @@ namespace ClassicalSharp.Map {
34, 35, 36, 22, 20, 49, 45, 1, 4, 0, 9, 11, 4, 19, 5, 17, 10, 49, 20, 1,
18, 12, 5, 25, 46, 44, 17, 49, 20, 1, 18, 12, 5, 25, 36, 34, 0, 9, 11, 46,
44, 0, 9, 11, 8, 10, 22, 27, 22, 8, 10, 28, 17, 49, 20, 1, 18, 12, 5, 25, 46,
44, 11, 9, 0, 9, 11, 163, 0, 0, 9, 11, 0, 0, 0, 0, 0, 0, 0, 28, 22, 21, 11,
0, 0, 0, 46, 46, 10, 10, 46, 20, 41, 42, 11, 9, 0, 8, 10, 10, 8, 0, 22, 22,
44, 11, 9, 0, 9, 11, customTile, 0, 0, 9, 11, 0, 0, 0, 0, 0, 0, 0, 28, 22, 21,
11, 0, 0, 0, 46, 46, 10, 10, 46, 20, 41, 42, 11, 9, 0, 8, 10, 10, 8, 0, 22, 22,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 10, 0, 0, 0, 0, 0, 22, 22, 42, 3, 2, 29,
47, 0, 0, 0, 0, 0, 27, 46, 48, 24, 22, 36, 34, 8, 10, 21, 29, 22, 10, 22, 22,
41, 19, 35, 21, 29, 49, 34, 16, 41, 0, 22 };

View file

@ -57,8 +57,8 @@ namespace ClassicalSharp.Net {
writer.WriteInt16( (short)(pos.X * 32) );
writer.WriteInt16( (short)((int)(pos.Y * 32) + 51) );
writer.WriteInt16( (short)(pos.Z * 32) );
writer.WriteUInt8( (byte)Utils.DegreesToPacked( yaw, 256 ) );
writer.WriteUInt8( (byte)Utils.DegreesToPacked( pitch, 256 ) );
writer.WriteUInt8( (byte)Utils.DegreesToPacked( yaw ) );
writer.WriteUInt8( (byte)Utils.DegreesToPacked( pitch ) );
}
#endregion

View file

@ -150,6 +150,10 @@ namespace ClassicalSharp {
return (int)(degrees * period / 360.0) % period;
}
public static int DegreesToPacked( double degrees ) {
return (int)(degrees * 256 / 360.0) & 0xFF;
}
public static double PackedToDegrees( byte packed ) {
return packed * 360.0 / 256.0;
}