Skip to content

Commit b616f0a

Browse files
Implement FastMap CPE extension.
1 parent 24ce8e4 commit b616f0a

File tree

7 files changed

+70
-39
lines changed

7 files changed

+70
-39
lines changed

fCraft/Network/CpeConstants.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ public enum CpeExt
107107

108108
/// <summary> Allows sending motd/handshake packets, without also needing to resend world. </summary>
109109
InstantMOTD,
110+
111+
/// <summary> More optimised sending of map. </summary>
112+
FastMap,
110113
}
111114

112115

fCraft/Network/LevelChunkStream.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public override void Flush() { }
1919
public override long Seek(long offset, SeekOrigin origin) { throw ex; }
2020
public override void SetLength(long length) { throw ex; }
2121

22-
internal int index, position, length;
22+
int index;
23+
internal byte chunkValue;
2324
Player player;
2425
byte[] data = new byte[chunkSize + 4];
2526
const int chunkSize = 1024;
@@ -61,7 +62,7 @@ public override void WriteByte(byte value) {
6162

6263
void WritePacket() {
6364
Packet.WriteI16((short)index, data, 1);
64-
data[1027] = (byte)(100 * (float)position / length);
65+
data[1027] = chunkValue;
6566
player.SendNow(new Packet(data));
6667
index = 0;
6768
}

fCraft/Network/PacketWriter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public override void Write( ushort data ) {
2626
}
2727

2828

29+
public override void Write( int data ) {
30+
base.Write( IPAddress.HostToNetworkOrder( (int)data ) );
31+
}
32+
33+
2934
public override void Write( string str ) {
3035
if( str == null ) throw new ArgumentNullException( "str" );
3136
if( str.Length > Packet.StringSize ) throw new ArgumentException( "String is too long (>64).", "str" );

fCraft/Network/Player.Handshake.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ static void WriteVarInt(int value, byte[] buffer, int offset) {
217217

218218
bool NegotiateProtocolExtension() {
219219
// write our ExtInfo and ExtEntry packets
220-
writer.Write(Packet.MakeExtInfo("ProCraft", 29).Bytes);
220+
writer.Write(Packet.MakeExtInfo("ProCraft", 30).Bytes);
221221

222222
writer.Write(Packet.MakeExtEntry(ClickDistanceExtName, 1).Bytes);
223223
writer.Write(Packet.MakeExtEntry(CustomBlocksExtName, 1).Bytes);
@@ -256,6 +256,7 @@ bool NegotiateProtocolExtension() {
256256
writer.Write(Packet.MakeExtEntry(InventoryOrderExtName, 1).Bytes);
257257

258258
writer.Write(Packet.MakeExtEntry(InstantMOTDExtName, 1).Bytes);
259+
writer.Write(Packet.MakeExtEntry(FastMapExtName, 1).Bytes);
259260

260261
// Fix for ClassiCube Client which violates the spec -
261262
// If server supports version > 1 but client version 1, client should reply with version 1.
@@ -381,6 +382,9 @@ bool NegotiateProtocolExtension() {
381382
case InstantMOTDExtName:
382383
if (version == 1) ext = CpeExt.InstantMOTD;
383384
break;
385+
case FastMapExtName:
386+
if (version == 1) ext = CpeExt.FastMap;
387+
break;
384388
}
385389
if (ext != CpeExt.None)
386390
supportedExts.Add(ext);

fCraft/Network/Player.Networking.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Globalization;
66
using System.IO;
7+
using System.IO.Compression;
78
using System.Linq;
89
using System.Net;
910
using System.Net.Sockets;
@@ -1115,6 +1116,10 @@ internal bool JoinWorldNow([NotNull] World newWorld, bool doUseWorldSpawn, World
11151116

11161117
writer.Write(OpCode.MapBegin);
11171118
BytesSent++;
1119+
if (Supports(CpeExt.FastMap)) {
1120+
writer.Write(map.Volume);
1121+
BytesSent += 4;
1122+
}
11181123

11191124
if (Supports(CpeExt.BlockDefinitions)) {
11201125
if (oldWorld != null) {
@@ -1166,10 +1171,20 @@ void WriteWorldData(Map map) {
11661171
Block maxLegal = supportsCustomBlocks ? Map.MaxCustomBlockType : Map.MaxLegalBlockType;
11671172
Logger.Log(LogType.Debug, "Player.JoinWorldNow: Sending compressed map to {0}.", Name);
11681173

1169-
if (supportsCustomBlocks && supportsBlockDefs)
1170-
map.CompressMap(this);
1171-
else
1172-
map.CompressAndConvertMap((byte)maxLegal, this);
1174+
using (LevelChunkStream dst = new LevelChunkStream(this)) {
1175+
Stream compressor = null;
1176+
try {
1177+
compressor = map.CompressMapHeader(this, dst);
1178+
1179+
if (supportsCustomBlocks && supportsBlockDefs) {
1180+
map.CompressMap(dst, compressor);
1181+
} else {
1182+
map.CompressAndConvertMap((byte)maxLegal, dst, compressor);
1183+
}
1184+
} finally {
1185+
if (compressor != null) compressor.Close();
1186+
}
1187+
}
11731188
}
11741189

11751190
void SendJoinMessage(World oldWorld, World newWorld) {
@@ -1286,10 +1301,10 @@ internal void SendEnvSettings() {
12861301
Send(Packet.MakeEnvSetMapProperty(EnvProp.WeatherSpeed, World.WeatherSpeed));
12871302
Send(Packet.MakeEnvSetMapProperty(EnvProp.WeatherFade, World.WeatherFade));
12881303

1289-
Send(Packet.MakeEnvSetMapProperty(EnvProp.SkyboxHorSpeed, World.SkyboxHorSpeed));
1290-
Send(Packet.MakeEnvSetMapProperty(EnvProp.SkyboxVerSpeed, World.SkyboxVerSpeed));
1304+
Send(Packet.MakeEnvSetMapProperty(EnvProp.SkyboxHorSpeed, World.SkyboxHorSpeed));
1305+
Send(Packet.MakeEnvSetMapProperty(EnvProp.SkyboxVerSpeed, World.SkyboxVerSpeed));
12911306

1292-
} else if (Supports(CpeExt.EnvMapAppearance2)) {
1307+
} else if (Supports(CpeExt.EnvMapAppearance2)) {
12931308
Send(Packet.MakeEnvSetMapAppearance2(World.GetTexture(), side, edge, World.GetEdgeLevel(),
12941309
World.GetCloudsHeight(), World.MaxFogDistance, HasCP437));
12951310
} else if (Supports(CpeExt.EnvMapAppearance)) {

fCraft/Player/Player.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,7 @@ public void ResetIdleTimer()
21562156
const string TwoWayPingExtName = "TwoWayPing";
21572157
const string InventoryOrderExtName = "InventoryOrder";
21582158
const string InstantMOTDExtName = "InstantMOTD";
2159+
const string FastMapExtName = "FastMap";
21592160

21602161
bool supportsBlockDefs, supportsCustomBlocks;
21612162
internal bool supportsExtPositions;

fCraft/World/Map.cs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -874,43 +874,45 @@ public static bool GetBlockByName([CanBeNull] World world, [NotNull] string bloc
874874
}
875875

876876
const int bufferSize = 64 * 1024;
877-
internal void CompressMap(Player dst) {
878-
byte[] array = Blocks;
879-
using (LevelChunkStream ms = new LevelChunkStream(dst))
880-
using (GZipStream compressor = new GZipStream(ms, CompressionMode.Compress, true))
881-
{
882-
int count = IPAddress.HostToNetworkOrder(array.Length); // convert to big endian
877+
internal Stream CompressMapHeader(Player player, LevelChunkStream dst) {
878+
Stream compressor = null;
879+
if (player.Supports(CpeExt.FastMap)) {
880+
compressor = new DeflateStream(dst, CompressionMode.Compress, true);
881+
} else {
882+
compressor = new GZipStream(dst, CompressionMode.Compress, true);
883+
int count = IPAddress.HostToNetworkOrder(Volume); // convert to big endian
883884
compressor.Write(BitConverter.GetBytes(count), 0, 4);
884-
ms.length = array.Length;
885-
886-
for (int i = 0; i < array.Length; i += bufferSize) {
887-
int len = Math.Min(bufferSize, array.Length - i);
888-
ms.position = i;
889-
compressor.Write(array, i, len);
890-
}
885+
}
886+
return compressor;
887+
}
888+
889+
internal void CompressMap(LevelChunkStream dst, Stream compressor) {
890+
byte[] array = Blocks;
891+
float progScale = 100.0f / array.Length;
892+
893+
for (int i = 0; i < array.Length; i += bufferSize) {
894+
int len = Math.Min(bufferSize, array.Length - i);
895+
dst.chunkValue = (byte)(i * progScale);
896+
compressor.Write(array, i, len);
891897
}
892898
}
893899

894-
internal void CompressAndConvertMap(byte maxLegal, Player dst) {
900+
internal void CompressAndConvertMap(byte maxLegal, LevelChunkStream dst, Stream compressor) {
895901
byte[] array = Blocks;
902+
float progScale = 100.0f / array.Length;
903+
896904
byte* fallback = stackalloc byte[256];
897905
MakeFallbacks(fallback, maxLegal, World);
898-
using (LevelChunkStream ms = new LevelChunkStream(dst))
899-
using (GZipStream compressor = new GZipStream(ms, CompressionMode.Compress, true))
900-
{
901-
int count = IPAddress.HostToNetworkOrder(array.Length); // convert to big endian
902-
compressor.Write(BitConverter.GetBytes(count), 0, 4);
903-
ms.length = array.Length;
904-
905-
byte[] buffer = new byte[bufferSize];
906-
for (int i = 0; i < array.Length; i += bufferSize) {
907-
int len = Math.Min(bufferSize, array.Length - i);
908-
for (int j = 0; j < len; j++)
909-
buffer[j] = fallback[array[i + j]];
910-
911-
ms.position = i;
912-
compressor.Write(buffer, 0, len);
906+
byte[] buffer = new byte[bufferSize];
907+
908+
for (int i = 0; i < array.Length; i += bufferSize) {
909+
int len = Math.Min(bufferSize, array.Length - i);
910+
for (int j = 0; j < len; j++) {
911+
buffer[j] = fallback[array[i + j]];
913912
}
913+
914+
dst.chunkValue = (byte)(i * progScale);
915+
compressor.Write(buffer, 0, len);
914916
}
915917
}
916918

0 commit comments

Comments
 (0)