diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj
index ba6961253..885adf3ea 100644
--- a/ClassicalSharp/ClassicalSharp.csproj
+++ b/ClassicalSharp/ClassicalSharp.csproj
@@ -146,12 +146,13 @@
+
-
+
diff --git a/ClassicalSharp/Network/Enums.cs b/ClassicalSharp/Network/Enums.cs
index d37813e95..ebd02165c 100644
--- a/ClassicalSharp/Network/Enums.cs
+++ b/ClassicalSharp/Network/Enums.cs
@@ -42,6 +42,8 @@ namespace ClassicalSharp {
CpeDefineBlock = 35,
CpeDefineLiquid = 36,
CpeRemoveBlockDefinition = 37,
+
+ Max,
}
public enum CpeMessage {
diff --git a/ClassicalSharp/Network/NetworkProcessor.CPE.cs b/ClassicalSharp/Network/NetworkProcessor.CPE.cs
new file mode 100644
index 000000000..362d36135
--- /dev/null
+++ b/ClassicalSharp/Network/NetworkProcessor.CPE.cs
@@ -0,0 +1,331 @@
+using System;
+using ClassicalSharp.TexturePack;
+using OpenTK.Input;
+
+namespace ClassicalSharp {
+
+ public partial class NetworkProcessor : INetworkProcessor {
+
+ #region Writing
+
+ public override void SendPlayerClick( MouseButton button, bool buttonDown, byte targetId, PickedPos pos ) {
+ Player p = game.LocalPlayer;
+ MakePlayerClick( (byte)button, buttonDown, p.YawDegrees, p.PitchDegrees, targetId,
+ pos.BlockPos, pos.BlockFace );
+ }
+
+ private static void MakeExtInfo( string appName, int extensionsCount ) {
+ WriteUInt8( (byte)PacketId.CpeExtInfo );
+ WriteString( appName );
+ WriteInt16( (short)extensionsCount );
+ }
+
+ private static void MakeExtEntry( string extensionName, int extensionVersion ) {
+ WriteUInt8( (byte)PacketId.CpeExtEntry );
+ WriteString( extensionName );
+ WriteInt32( extensionVersion );
+ }
+
+ private static void MakeCustomBlockSupportLevel( byte version ) {
+ WriteUInt8( (byte)PacketId.CpeCustomBlockSupportLevel );
+ WriteUInt8( version );
+ }
+
+ private static void MakePlayerClick( byte button, bool buttonDown, float yaw, float pitch, byte targetEntity,
+ Vector3I targetPos, CpeBlockFace targetFace ) {
+ WriteUInt8( (byte)PacketId.CpePlayerClick );
+ WriteUInt8( button );
+ WriteUInt8( buttonDown ? (byte)0 : (byte)1 );
+ WriteInt16( (short)Utils.DegreesToPacked( yaw, 65536 ) );
+ WriteInt16( (short)Utils.DegreesToPacked( pitch, 65536 ) );
+ WriteUInt8( targetEntity );
+ WriteInt16( (short)targetPos.X );
+ WriteInt16( (short)targetPos.Y );
+ WriteInt16( (short)targetPos.Z );
+ WriteUInt8( (byte)targetFace );
+ }
+
+ #endregion
+
+
+ #region Reading
+
+ int cpeServerExtensionsCount;
+ bool sendHeldBlock, useMessageTypes, usingTexturePack;
+ static string[] clientExtensions = {
+ "EmoteFix", "ClickDistance", "HeldBlock", "BlockPermissions",
+ "SelectionCuboid", "MessageTypes", "CustomBlocks", "EnvColors",
+ "HackControl", "EnvMapAppearance", "ExtPlayerList", "ChangeModel",
+ "EnvWeatherType", "PlayerClick", // NOTE: There are no plans to support TextHotKey.
+ };
+
+ void HandleCpeExtInfo() {
+ string appName = reader.ReadAsciiString();
+ Utils.LogDebug( "Server identified itself as: " + appName );
+ cpeServerExtensionsCount = reader.ReadInt16();
+ }
+
+ void HandleCpeExtEntry() {
+ string extName = reader.ReadAsciiString();
+ int extVersion = reader.ReadInt32();
+ Utils.LogDebug( "cpe ext: " + extName + " , " + extVersion );
+
+ if( extName == "HeldBlock" ) {
+ sendHeldBlock = true;
+ } else if( extName == "MessageTypes" ) {
+ useMessageTypes = true;
+ } else if( extName == "ExtPlayerList" ) {
+ UsingExtPlayerList = true;
+ } else if( extName == "PlayerClick" ) {
+ UsingPlayerClick = true;
+ } else if( extName == "EnvMapAppearance" && extVersion == 2 ) {
+ usingTexturePack = true;
+ }
+ cpeServerExtensionsCount--;
+
+ if( cpeServerExtensionsCount == 0 ) {
+ MakeExtInfo( Utils.AppName, clientExtensions.Length );
+ SendPacket();
+ for( int i = 0; i < clientExtensions.Length; i++ ) {
+ string name = clientExtensions[i];
+ int version = (name == "ExtPlayerList" || name == "EnvMapApperance") ? 2 : 1;
+ MakeExtEntry( name, version );
+ SendPacket();
+ }
+ }
+ }
+
+ void HandleCpeSetClickDistance() {
+ game.LocalPlayer.ReachDistance = reader.ReadInt16() / 32f;
+ }
+
+ void HandleCpeCustomBlockSupportLevel() {
+ byte supportLevel = reader.ReadUInt8();
+ MakeCustomBlockSupportLevel( 1 );
+ SendPacket();
+
+ if( supportLevel == 1 ) {
+ for( int i = (int)Block.CobblestoneSlab; i <= (int)Block.StoneBrick; i++ ) {
+ game.Inventory.CanPlace[i] = true;
+ game.Inventory.CanDelete[i] = true;
+ }
+ game.Events.RaiseBlockPermissionsChanged();
+ } else {
+ Utils.LogWarning( "Server's block support level is {0}, this client only supports level 1.", supportLevel );
+ Utils.LogWarning( "You won't be able to see or use blocks from levels above level 1" );
+ }
+ }
+
+ void HandleCpeHoldThis() {
+ byte blockType = reader.ReadUInt8();
+ bool canChange = reader.ReadUInt8() == 0;
+ game.Inventory.CanChangeHeldBlock = true;
+ game.Inventory.HeldBlock = (Block)blockType;
+ game.Inventory.CanChangeHeldBlock = canChange;
+ }
+
+ void HandleCpeExtAddPlayerName() {
+ short nameId = reader.ReadInt16();
+ string playerName = Utils.StripColours( reader.ReadAsciiString() );
+ string listName = reader.ReadAsciiString();
+ string groupName = reader.ReadAsciiString();
+ byte groupRank = reader.ReadUInt8();
+
+ if( nameId >= 0 && nameId <= 255 ) {
+ CpeListInfo oldInfo = game.CpePlayersList[nameId];
+ CpeListInfo info = new CpeListInfo( (byte)nameId, playerName, listName, groupName, groupRank );
+ game.CpePlayersList[nameId] = info;
+
+ if( oldInfo != null ) {
+ game.Events.RaiseCpeListInfoChanged( (byte)nameId );
+ } else {
+ game.Events.RaiseCpeListInfoAdded( (byte)nameId );
+ }
+ }
+ }
+
+ void HandleCpeExtAddEntity() {
+ byte entityId = reader.ReadUInt8();
+ string displayName = reader.ReadAsciiString();
+ string skinName = reader.ReadAsciiString();
+ AddEntity( entityId, displayName, skinName, false );
+ }
+
+ void HandleCpeExtRemovePlayerName() {
+ short nameId = reader.ReadInt16();
+ if( nameId >= 0 && nameId <= 255 ) {
+ game.Events.RaiseCpeListInfoRemoved( (byte)nameId );
+ game.CpePlayersList[nameId] = null;
+ }
+ }
+
+ void HandleCpeMakeSelection() {
+ byte selectionId = reader.ReadUInt8();
+ string label = reader.ReadAsciiString();
+ short startX = reader.ReadInt16();
+ short startY = reader.ReadInt16();
+ short startZ = reader.ReadInt16();
+ short endX = reader.ReadInt16();
+ short endY = reader.ReadInt16();
+ short endZ = reader.ReadInt16();
+
+ byte r = (byte)reader.ReadInt16();
+ byte g = (byte)reader.ReadInt16();
+ byte b = (byte)reader.ReadInt16();
+ byte a = (byte)reader.ReadInt16();
+
+ Vector3I p1 = Vector3I.Min( startX, startY, startZ, endX, endY, endZ );
+ Vector3I p2 = Vector3I.Max( startX, startY, startZ, endX, endY, endZ );
+ FastColour col = new FastColour( r, g, b, a );
+ game.SelectionManager.AddSelection( selectionId, p1, p2, col );
+ }
+
+ void HandleCpeRemoveSelection() {
+ byte selectionId = reader.ReadUInt8();
+ game.SelectionManager.RemoveSelection( selectionId );
+ }
+
+ void HandleCpeEnvColours() {
+ byte variable = reader.ReadUInt8();
+ short red = reader.ReadInt16();
+ short green = reader.ReadInt16();
+ short blue = reader.ReadInt16();
+ bool invalid = red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255;
+ FastColour col = new FastColour( red, green, blue );
+
+ if( variable == 0 ) { // sky colour
+ game.Map.SetSkyColour( invalid ? Map.DefaultSkyColour : col );
+ } else if( variable == 1 ) { // clouds colour
+ game.Map.SetCloudsColour( invalid ? Map.DefaultCloudsColour : col );
+ } else if( variable == 2 ) { // fog colour
+ game.Map.SetFogColour( invalid ? Map.DefaultFogColour : col );
+ } else if( variable == 3 ) { // ambient light (shadow light)
+ game.Map.SetShadowlight( invalid ? Map.DefaultShadowlight : col );
+ } else if( variable == 4 ) { // diffuse light (sun light)
+ game.Map.SetSunlight( invalid ? Map.DefaultSunlight : col );
+ }
+ }
+
+ void HandleCpeSetBlockPermission() {
+ byte blockId = reader.ReadUInt8();
+ bool canPlace = reader.ReadUInt8() != 0;
+ bool canDelete = reader.ReadUInt8() != 0;
+ Inventory inv = game.Inventory;
+
+ if( blockId == 0 ) {
+ for( int i = 1; i < BlockInfo.CpeBlocksCount; i++ ) {
+ inv.CanPlace.SetNotOverridable( canPlace, i );
+ inv.CanDelete.SetNotOverridable( canDelete, i );
+ }
+ } else {
+ inv.CanPlace.SetNotOverridable( canPlace, blockId );
+ inv.CanDelete.SetNotOverridable( canDelete, blockId );
+ }
+ game.Events.RaiseBlockPermissionsChanged();
+ }
+
+ void HandleCpeChangeModel() {
+ byte playerId = reader.ReadUInt8();
+ string modelName = reader.ReadAsciiString().ToLowerInvariant();
+ Player player = game.Players[playerId];
+ if( player != null ) {
+ player.SetModel( modelName );
+ }
+ }
+
+ void HandleCpeEnvSetMapApperance() {
+ string url = reader.ReadAsciiString();
+ byte sideBlock = reader.ReadUInt8();
+ byte edgeBlock = reader.ReadUInt8();
+ short waterLevel = reader.ReadInt16();
+ game.Map.SetWaterLevel( waterLevel );
+ game.Map.SetEdgeBlock( (Block)edgeBlock );
+ game.Map.SetSidesBlock( (Block)sideBlock );
+
+ if( url == String.Empty ) {
+ TexturePackExtractor extractor = new TexturePackExtractor();
+ extractor.Extract( game.defaultTexPack, game );
+ } else {
+ game.Animations.Dispose();
+ if( usingTexturePack )
+ game.AsyncDownloader.DownloadData( url, true, "texturePack" );
+ else
+ game.AsyncDownloader.DownloadImage( url, true, "terrain" );
+
+ }
+ Utils.LogDebug( "Image url: " + url );
+ }
+
+ void HandleCpeEnvWeatherType() {
+ game.Map.SetWeather( (Weather)reader.ReadUInt8() );
+ }
+
+ void HandleCpeHackControl() {
+ game.LocalPlayer.CanFly = reader.ReadUInt8() != 0;
+ game.LocalPlayer.CanNoclip = reader.ReadUInt8() != 0;
+ game.LocalPlayer.CanSpeed = reader.ReadUInt8() != 0;
+ game.LocalPlayer.CanRespawn = reader.ReadUInt8() != 0;
+ game.CanUseThirdPersonCamera = reader.ReadUInt8() != 0;
+
+ if( !game.CanUseThirdPersonCamera ) {
+ game.SetCamera( false );
+ }
+ float jumpHeight = reader.ReadInt16() / 32f;
+ if( jumpHeight < 0 ) jumpHeight = 1.4f;
+ game.LocalPlayer.CalculateJumpVelocity( jumpHeight );
+ }
+
+ void HandleCpeExtAddEntity2() {
+ byte entityId = reader.ReadUInt8();
+ string displayName = reader.ReadAsciiString();
+ string skinName = reader.ReadAsciiString();
+ AddEntity( entityId, displayName, skinName, true );
+ }
+
+ void HandleCpeDefineBlockOrLiquid() {
+ byte block = reader.ReadUInt8();
+ BlockInfo info = game.BlockInfo;
+ info.ResetBlockInfo( block );
+
+ info.Name[block] = reader.ReadAsciiString();
+ info.CollideType[block] = (BlockCollideType)reader.ReadUInt8();
+ // TODO: Liquid collide type not properly supported.
+ info.SpeedMultiplier[block] = (float)Math.Pow( 2, (reader.ReadUInt8() - 128) / 64f );
+ info.SetTop( reader.ReadUInt8(), (Block)block );
+ info.SetSide( reader.ReadUInt8(), (Block)block );
+ info.SetBottom( reader.ReadUInt8(), (Block)block );
+ reader.ReadUInt8(); // opacity hint, but we ignore this.
+ info.BlocksLight[block] = reader.ReadUInt8() == 0;
+ reader.ReadUInt8(); // walk sound, but we ignore this.
+ info.EmitsLight[block] = reader.ReadUInt8() != 0;
+
+ if( lastOpcode == PacketId.CpeDefineBlock ) {
+ byte shape = reader.ReadUInt8();
+ if( shape == 1 ) info.Height[block] = 1;
+ else if( shape == 2 ) info.Height[block] = 0.5f;
+ // TODO: upside down slab not properly supported
+ else if( shape == 3 ) info.Height[block] = 0.5f;
+ else if( shape == 4 ) info.IsSprite[block] = true;
+
+ byte blockDraw = reader.ReadUInt8();
+ if( blockDraw == 0 ) info.IsOpaque[block] = true;
+ else if( blockDraw == 1 ) info.IsTransparent[block] = true;
+ else if( blockDraw == 2 ) info.IsTranslucent[block] = true;
+ else if( blockDraw == 3 ) info.IsTranslucent[block] = true;
+
+ Console.WriteLine( block + " : " + shape + "," + blockDraw );
+ } else {
+ byte fogDensity = reader.ReadUInt8();
+ info.FogDensity[block] = fogDensity == 0 ? 0 : (fogDensity + 1) / 128f;
+ info.FogColour[block] = new FastColour(
+ reader.ReadUInt8(), reader.ReadUInt8(), reader.ReadUInt8() );
+ }
+ info.SetupCullingCache();
+ }
+
+ void HandleCpeRemoveBlockDefinition() {
+ game.BlockInfo.ResetBlockInfo( reader.ReadUInt8() );
+ }
+ }
+ #endregion
+}
\ No newline at end of file
diff --git a/ClassicalSharp/Network/WomConfigHandler.cs b/ClassicalSharp/Network/NetworkProcessor.WoM.cs
similarity index 98%
rename from ClassicalSharp/Network/WomConfigHandler.cs
rename to ClassicalSharp/Network/NetworkProcessor.WoM.cs
index 332666f3d..da0116a55 100644
--- a/ClassicalSharp/Network/WomConfigHandler.cs
+++ b/ClassicalSharp/Network/NetworkProcessor.WoM.cs
@@ -10,6 +10,7 @@ namespace ClassicalSharp {
string womEnvIdentifier = "womenv_0", womTerrainIdentifier = "womterrain_0";
int womCounter = 0;
+ bool sendWomId = false, sentWomId = false;
void CheckForWomEnvironment() {
DownloadedItem item;
diff --git a/ClassicalSharp/Network/NetworkProcessor.cs b/ClassicalSharp/Network/NetworkProcessor.cs
index d9757b807..832248157 100644
--- a/ClassicalSharp/Network/NetworkProcessor.cs
+++ b/ClassicalSharp/Network/NetworkProcessor.cs
@@ -1,17 +1,16 @@
-using System;
-using System.Drawing;
-using System.IO;
+using System;
+using System.Drawing;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using ClassicalSharp.Network;
+using ClassicalSharp.TexturePack;
+using OpenTK;
#if __MonoCS__
using Ionic.Zlib;
#else
using System.IO.Compression;
#endif
-using System.Net;
-using System.Net.Sockets;
-using ClassicalSharp.Network;
-using ClassicalSharp.TexturePack;
-using OpenTK;
-using OpenTK.Input;
namespace ClassicalSharp {
@@ -19,6 +18,7 @@ namespace ClassicalSharp {
public NetworkProcessor( Game window ) {
game = window;
+ SetupHandlers();
}
public override bool IsSinglePlayer {
@@ -28,9 +28,6 @@ namespace ClassicalSharp {
Socket socket;
NetworkStream stream;
Game game;
- bool sendHeldBlock;
- bool useMessageTypes;
- bool usingTexturePack;
bool receivedFirstPosition;
public override void Connect( IPAddress address, int port ) {
@@ -69,12 +66,6 @@ namespace ClassicalSharp {
SendPacket();
}
- public override void SendPlayerClick( MouseButton button, bool buttonDown, byte targetId, PickedPos pos ) {
- Player p = game.LocalPlayer;
- MakePlayerClick( (byte)button, buttonDown, p.YawDegrees, p.PitchDegrees, targetId,
- pos.BlockPos, pos.BlockFace );
- }
-
public override void Dispose() {
socket.Close();
Disconnected = true;
@@ -131,13 +122,6 @@ namespace ClassicalSharp {
8, 86, 2, 4, 66, 69, 2, 8, 138, 0, 77, 79, 2,
};
- static string[] clientExtensions = {
- "EmoteFix", "ClickDistance", "HeldBlock", "BlockPermissions",
- "SelectionCuboid", "MessageTypes", "CustomBlocks", "EnvColors",
- "HackControl", "EnvMapAppearance", "ExtPlayerList", "ChangeModel",
- "EnvWeatherType", "PlayerClick", // NOTE: There are no plans to support TextHotKey.
- };
-
#region Writing
@@ -176,37 +160,6 @@ namespace ClassicalSharp {
WriteString( text );
}
- private static void MakeExtInfo( string appName, int extensionsCount ) {
- WriteUInt8( (byte)PacketId.CpeExtInfo );
- WriteString( appName );
- WriteInt16( (short)extensionsCount );
- }
-
- private static void MakeExtEntry( string extensionName, int extensionVersion ) {
- WriteUInt8( (byte)PacketId.CpeExtEntry );
- WriteString( extensionName );
- WriteInt32( extensionVersion );
- }
-
- private static void MakeCustomBlockSupportLevel( byte version ) {
- WriteUInt8( (byte)PacketId.CpeCustomBlockSupportLevel );
- WriteUInt8( version );
- }
-
- private static void MakePlayerClick( byte button, bool buttonDown, float yaw, float pitch, byte targetEntity,
- Vector3I targetPos, CpeBlockFace targetFace ) {
- WriteUInt8( (byte)PacketId.CpePlayerClick );
- WriteUInt8( button );
- WriteUInt8( buttonDown ? (byte)0 : (byte)1 );
- WriteInt16( (short)Utils.DegreesToPacked( yaw, 65536 ) );
- WriteInt16( (short)Utils.DegreesToPacked( pitch, 65536 ) );
- WriteUInt8( targetEntity );
- WriteInt16( (short)targetPos.X );
- WriteInt16( (short)targetPos.Y );
- WriteInt16( (short)targetPos.Z );
- WriteUInt8( (byte)targetFace );
- }
-
static void WriteString( string value ) {
int count = Math.Min( value.Length, 64 );
for( int i = 0; i < count; i++ ) {
@@ -253,464 +206,193 @@ namespace ClassicalSharp {
#region Reading
+
FastNetReader reader;
- int cpeServerExtensionsCount;
DateTime receiveStart;
DeflateStream gzipStream;
GZipHeaderReader gzipHeader;
int mapSizeIndex, mapIndex;
byte[] mapSize = new byte[4], map;
FixedBufferStream gzippedMap;
- bool sendWomId = false, sentWomId = false;
PacketId lastOpcode;
void ReadPacket( byte opcode ) {
reader.Remove( 1 ); // remove opcode
lastOpcode = (PacketId)opcode;
+ Action handler;
- switch( (PacketId)opcode ) {
- case PacketId.Handshake:
- {
- byte protocolVer = reader.ReadUInt8();
- ServerName = reader.ReadAsciiString();
- ServerMotd = reader.ReadAsciiString();
- game.LocalPlayer.SetUserType( reader.ReadUInt8() );
- receivedFirstPosition = false;
- game.LocalPlayer.ParseHackFlags( ServerName, ServerMotd );
- } break;
-
- case PacketId.Ping:
- break;
-
- case PacketId.LevelInitialise:
- {
- game.Map.Reset();
- game.SetNewScreen( new LoadingMapScreen( game, ServerName, ServerMotd ) );
- if( ServerMotd.Contains( "cfg=" ) ) {
- ReadWomConfigurationAsync();
- }
- receivedFirstPosition = false;
- gzipHeader = new GZipHeaderReader();
- // Workaround because built in mono stream assumes that the end of stream
- // has been reached the first time a read call returns 0. (MS.NET doesn't)
- #if __MonoCS__
- gzipStream = new DeflateStream( gzippedMap, true );
- #else
- gzipStream = new DeflateStream( gzippedMap, CompressionMode.Decompress );
- if( OpenTK.Configuration.RunningOnMono ) {
- Utils.LogWarning( "You are running on Mono, but this build does not support the Mono workaround." );
- Utils.LogWarning( "You should either download the Mono compatible build or define '__MonoCS__' when targeting Mono. " +
- "(The Mono compiler already defines this by default)" );
- Utils.LogWarning( "You will likely experience an 'Internal error (no progress possible) ReadInternal' exception when decompressing the map." );
- }
- #endif
- mapSizeIndex = 0;
- mapIndex = 0;
- receiveStart = DateTime.UtcNow;
- } break;
-
- case PacketId.LevelDataChunk:
- {
- int usedLength = reader.ReadInt16();
- gzippedMap.Position = 0;
- gzippedMap.SetLength( usedLength );
-
- if( gzipHeader.done || gzipHeader.ReadHeader( gzippedMap ) ) {
- if( mapSizeIndex < 4 ) {
- mapSizeIndex += gzipStream.Read( mapSize, mapSizeIndex, 4 - mapSizeIndex );
- }
+ if( opcode >= maxHandledPacket || (handler = handlers[opcode]) == null)
+ throw new NotImplementedException( "Unsupported packet:" + (PacketId)opcode );
+ handler();
+ }
+
+ void HandleHandshake() {
+ byte protocolVer = reader.ReadUInt8();
+ ServerName = reader.ReadAsciiString();
+ ServerMotd = reader.ReadAsciiString();
+ game.LocalPlayer.SetUserType( reader.ReadUInt8() );
+ receivedFirstPosition = false;
+ game.LocalPlayer.ParseHackFlags( ServerName, ServerMotd );
+ }
+
+ void HandlePing() {
+ }
+
+ void HandleLevelInit() {
+ game.Map.Reset();
+ game.SetNewScreen( new LoadingMapScreen( game, ServerName, ServerMotd ) );
+ if( ServerMotd.Contains( "cfg=" ) ) {
+ ReadWomConfigurationAsync();
+ }
+ receivedFirstPosition = false;
+ gzipHeader = new GZipHeaderReader();
+ // Workaround because built in mono stream assumes that the end of stream
+ // has been reached the first time a read call returns 0. (MS.NET doesn't)
+ #if __MonoCS__
+ gzipStream = new DeflateStream( gzippedMap, true );
+ #else
+ gzipStream = new DeflateStream( gzippedMap, CompressionMode.Decompress );
+ if( OpenTK.Configuration.RunningOnMono ) {
+ Utils.LogWarning( "You are running on Mono, but this build does not support the Mono workaround." );
+ Utils.LogWarning( "You should either download the Mono compatible build or define '__MonoCS__' when targeting Mono. " +
+ "(The Mono compiler already defines this by default)" );
+ Utils.LogWarning( "You will likely experience an 'Internal error (no progress possible) ReadInternal' exception when decompressing the map." );
+ }
+ #endif
+
+ mapSizeIndex = 0;
+ mapIndex = 0;
+ receiveStart = DateTime.UtcNow;
+ }
+
+ void HandleLevelDataChunk() {
+ int usedLength = reader.ReadInt16();
+ gzippedMap.Position = 0;
+ gzippedMap.SetLength( usedLength );
+
+ if( gzipHeader.done || gzipHeader.ReadHeader( gzippedMap ) ) {
+ if( mapSizeIndex < 4 ) {
+ mapSizeIndex += gzipStream.Read( mapSize, mapSizeIndex, 4 - mapSizeIndex );
+ }
- if( mapSizeIndex == 4 ) {
- if( map == null ) {
- int size = mapSize[0] << 24 | mapSize[1] << 16 | mapSize[2] << 8 | mapSize[3];
- map = new byte[size];
- }
- mapIndex += gzipStream.Read( map, mapIndex, map.Length - mapIndex );
- }
- }
- reader.Remove( 1024 );
- byte progress = reader.ReadUInt8();
- game.Events.RaiseMapLoading( progress );
- } break;
-
- case PacketId.LevelFinalise:
- {
- game.SetNewScreen( new NormalScreen( game ) );
- int mapWidth = reader.ReadInt16();
- int mapHeight = reader.ReadInt16();
- int mapLength = reader.ReadInt16();
-
- double loadingMs = ( DateTime.UtcNow - receiveStart ).TotalMilliseconds;
- Utils.LogDebug( "map loading took:" + loadingMs );
- game.Map.UseRawMap( map, mapWidth, mapHeight, mapLength );
- game.Events.RaiseOnNewMapLoaded();
- map = null;
- gzipStream.Dispose();
- if( sendWomId && !sentWomId ) {
- SendChat( "/womid WoMClient-2.0.7" );
- sentWomId = true;
- }
- gzipStream = null;
- GC.Collect( 0 );
- } break;
-
- case PacketId.SetBlock:
- {
- int x = reader.ReadInt16();
- int y = reader.ReadInt16();
- int z = reader.ReadInt16();
- byte type = reader.ReadUInt8();
-
- if( game.Map.IsNotLoaded )
- Utils.LogWarning( "Server tried to update a block while still sending us the map!" );
- else if( !game.Map.IsValidPos( x, y, z ) )
- Utils.LogWarning( "Server tried to update a block at an invalid position!" );
- else
- game.UpdateBlock( x, y, z, type );
- } break;
-
- case PacketId.AddEntity:
- {
- byte entityId = reader.ReadUInt8();
- string name = reader.ReadAsciiString();
- AddEntity( entityId, name, name, true );
- } break;
-
- case PacketId.EntityTeleport:
- {
- byte entityId = reader.ReadUInt8();
- ReadAbsoluteLocation( entityId, true );
- } break;
-
- case PacketId.RelPosAndOrientationUpdate:
- ReadRelativeLocation();
- break;
-
- case PacketId.RelPosUpdate:
- ReadRelativePosition();
- break;
-
- case PacketId.OrientationUpdate:
- ReadOrientation();
- break;
-
- case PacketId.RemoveEntity:
- {
- byte entityId = reader.ReadUInt8();
- Player player = game.Players[entityId];
- if( entityId != 0xFF && player != null ) {
- game.Events.RaiseEntityRemoved( entityId );
- player.Despawn();
- game.Players[entityId] = null;
- }
- } break;
-
- case PacketId.Message:
- {
- byte messageType = reader.ReadUInt8();
- string text = reader.ReadChatString( ref messageType, useMessageTypes );
- game.Chat.Add( text, (CpeMessage)messageType );
- } break;
-
- case PacketId.Kick:
- {
- string reason = reader.ReadAsciiString();
- game.Disconnect( "&eLost connection to the server", reason );
- Dispose();
- } break;
-
- case PacketId.SetPermission:
- {
- game.LocalPlayer.SetUserType( reader.ReadUInt8() );
- } break;
-
- case PacketId.CpeExtInfo:
- {
- string appName = reader.ReadAsciiString();
- Utils.LogDebug( "Server identified itself as: " + appName );
- cpeServerExtensionsCount = reader.ReadInt16();
- } break;
-
- case PacketId.CpeExtEntry:
- {
- string extName = reader.ReadAsciiString();
- int extVersion = reader.ReadInt32();
- Utils.LogDebug( "cpe ext: " + extName + " , " + extVersion );
- if( extName == "HeldBlock" ) {
- sendHeldBlock = true;
- } else if( extName == "MessageTypes" ) {
- useMessageTypes = true;
- } else if( extName == "ExtPlayerList" ) {
- UsingExtPlayerList = true;
- } else if( extName == "PlayerClick" ) {
- UsingPlayerClick = true;
- } else if( extName == "EnvMapAppearance" && extVersion == 2 ) {
- usingTexturePack = true;
- }
- cpeServerExtensionsCount--;
-
- if( cpeServerExtensionsCount == 0 ) {
- MakeExtInfo( Utils.AppName, clientExtensions.Length );
- SendPacket();
- for( int i = 0; i < clientExtensions.Length; i++ ) {
- string name = clientExtensions[i];
- int version = (name == "ExtPlayerList" || name == "EnvMapApperance") ? 2 : 1;
- MakeExtEntry( name, version );
- SendPacket();
- }
- }
- } break;
-
- case PacketId.CpeSetClickDistance:
- {
- game.LocalPlayer.ReachDistance = reader.ReadInt16() / 32f;
- } break;
-
- case PacketId.CpeCustomBlockSupportLevel:
- {
- byte supportLevel = reader.ReadUInt8();
- MakeCustomBlockSupportLevel( 1 );
- SendPacket();
-
- if( supportLevel == 1 ) {
- for( int i = (int)Block.CobblestoneSlab; i <= (int)Block.StoneBrick; i++ ) {
- game.Inventory.CanPlace[i] = true;
- game.Inventory.CanDelete[i] = true;
- }
- game.Events.RaiseBlockPermissionsChanged();
- } else {
- Utils.LogWarning( "Server's block support level is {0}, this client only supports level 1.", supportLevel );
- Utils.LogWarning( "You won't be able to see or use blocks from levels above level 1" );
- }
- } break;
-
- case PacketId.CpeHoldThis:
- {
- byte blockType = reader.ReadUInt8();
- bool canChange = reader.ReadUInt8() == 0;
- game.Inventory.CanChangeHeldBlock = true;
- game.Inventory.HeldBlock = (Block)blockType;
- game.Inventory.CanChangeHeldBlock = canChange;
- } break;
-
- case PacketId.CpeExtAddPlayerName:
- {
- short nameId = reader.ReadInt16();
- string playerName = Utils.StripColours( reader.ReadAsciiString() );
- string listName = reader.ReadAsciiString();
- string groupName = reader.ReadAsciiString();
- byte groupRank = reader.ReadUInt8();
- if( nameId >= 0 && nameId <= 255 ) {
- CpeListInfo oldInfo = game.CpePlayersList[nameId];
- CpeListInfo info = new CpeListInfo( (byte)nameId, playerName, listName, groupName, groupRank );
- game.CpePlayersList[nameId] = info;
-
- if( oldInfo != null ) {
- game.Events.RaiseCpeListInfoChanged( (byte)nameId );
- } else {
- game.Events.RaiseCpeListInfoAdded( (byte)nameId );
- }
- }
- } break;
-
- case PacketId.CpeExtAddEntity:
- {
- byte entityId = reader.ReadUInt8();
- string displayName = reader.ReadAsciiString();
- string skinName = reader.ReadAsciiString();
- AddEntity( entityId, displayName, skinName, false );
- } break;
-
- case PacketId.CpeExtRemovePlayerName:
- {
- short nameId = reader.ReadInt16();
- if( nameId >= 0 && nameId <= 255 ) {
- game.Events.RaiseCpeListInfoRemoved( (byte)nameId );
- game.CpePlayersList[nameId] = null;
- }
- } break;
-
- case PacketId.CpeMakeSelection:
- {
- byte selectionId = reader.ReadUInt8();
- string label = reader.ReadAsciiString();
- short startX = reader.ReadInt16();
- short startY = reader.ReadInt16();
- short startZ = reader.ReadInt16();
- short endX = reader.ReadInt16();
- short endY = reader.ReadInt16();
- short endZ = reader.ReadInt16();
-
- byte r = (byte)reader.ReadInt16();
- byte g = (byte)reader.ReadInt16();
- byte b = (byte)reader.ReadInt16();
- byte a = (byte)reader.ReadInt16();
-
- Vector3I p1 = Vector3I.Min( startX, startY, startZ, endX, endY, endZ );
- Vector3I p2 = Vector3I.Max( startX, startY, startZ, endX, endY, endZ );
- FastColour col = new FastColour( r, g, b, a );
- game.SelectionManager.AddSelection( selectionId, p1, p2, col );
- } break;
-
- case PacketId.CpeRemoveSelection:
- {
- byte selectionId = reader.ReadUInt8();
- game.SelectionManager.RemoveSelection( selectionId );
- } break;
-
- case PacketId.CpeEnvColours:
- {
- byte variable = reader.ReadUInt8();
- short red = reader.ReadInt16();
- short green = reader.ReadInt16();
- short blue = reader.ReadInt16();
- bool invalid = red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255;
- FastColour col = new FastColour( red, green, blue );
-
- if( variable == 0 ) { // sky colour
- game.Map.SetSkyColour( invalid ? Map.DefaultSkyColour : col );
- } else if( variable == 1 ) { // clouds colour
- game.Map.SetCloudsColour( invalid ? Map.DefaultCloudsColour : col );
- } else if( variable == 2 ) { // fog colour
- game.Map.SetFogColour( invalid ? Map.DefaultFogColour : col );
- } else if( variable == 3 ) { // ambient light (shadow light)
- game.Map.SetShadowlight( invalid ? Map.DefaultShadowlight : col );
- } else if( variable == 4 ) { // diffuse light (sun light)
- game.Map.SetSunlight( invalid ? Map.DefaultSunlight : col );
- }
- } break;
-
- case PacketId.CpeSetBlockPermission:
- {
- byte blockId = reader.ReadUInt8();
- bool canPlace = reader.ReadUInt8() != 0;
- bool canDelete = reader.ReadUInt8() != 0;
- Inventory inv = game.Inventory;
-
- if( blockId == 0 ) {
- for( int i = 1; i < BlockInfo.CpeBlocksCount; i++ ) {
- inv.CanPlace.SetNotOverridable( canPlace, i );
- inv.CanDelete.SetNotOverridable( canDelete, i );
- }
- } else {
- inv.CanPlace.SetNotOverridable( canPlace, blockId );
- inv.CanDelete.SetNotOverridable( canDelete, blockId );
- }
- game.Events.RaiseBlockPermissionsChanged();
- } break;
-
- case PacketId.CpeChangeModel:
- {
- byte playerId = reader.ReadUInt8();
- string modelName = reader.ReadAsciiString().ToLowerInvariant();
- Player player = game.Players[playerId];
- if( player != null ) {
- player.SetModel( modelName );
- }
- } break;
-
- case PacketId.CpeEnvSetMapApperance:
- {
- string url = reader.ReadAsciiString();
- byte sideBlock = reader.ReadUInt8();
- byte edgeBlock = reader.ReadUInt8();
- short waterLevel = reader.ReadInt16();
- game.Map.SetWaterLevel( waterLevel );
- game.Map.SetEdgeBlock( (Block)edgeBlock );
- game.Map.SetSidesBlock( (Block)sideBlock );
- if( url == String.Empty ) {
- TexturePackExtractor extractor = new TexturePackExtractor();
- extractor.Extract( game.defaultTexPack, game );
- } else {
- game.Animations.Dispose();
- if( usingTexturePack )
- game.AsyncDownloader.DownloadData( url, true, "texturePack" );
- else
- game.AsyncDownloader.DownloadImage( url, true, "terrain" );
-
- }
- Utils.LogDebug( "Image url: " + url );
- } break;
-
- case PacketId.CpeEnvWeatherType:
- game.Map.SetWeather( (Weather)reader.ReadUInt8() );
- break;
-
- case PacketId.CpeHackControl:
- {
- game.LocalPlayer.CanFly = reader.ReadUInt8() != 0;
- game.LocalPlayer.CanNoclip = reader.ReadUInt8() != 0;
- game.LocalPlayer.CanSpeed = reader.ReadUInt8() != 0;
- game.LocalPlayer.CanRespawn = reader.ReadUInt8() != 0;
- game.CanUseThirdPersonCamera = reader.ReadUInt8() != 0;
- if( !game.CanUseThirdPersonCamera ) {
- game.SetCamera( false );
- }
- float jumpHeight = reader.ReadInt16() / 32f;
- if( jumpHeight < 0 ) jumpHeight = 1.4f;
- game.LocalPlayer.CalculateJumpVelocity( jumpHeight );
- } break;
-
- case PacketId.CpeExtAddEntity2:
- {
- byte entityId = reader.ReadUInt8();
- string displayName = reader.ReadAsciiString();
- string skinName = reader.ReadAsciiString();
- AddEntity( entityId, displayName, skinName, true );
- } break;
-
- case PacketId.CpeDefineBlock:
- case PacketId.CpeDefineLiquid:
- {
- byte block = reader.ReadUInt8();
- BlockInfo info = game.BlockInfo;
- info.ResetBlockInfo( block );
-
- info.Name[block] = reader.ReadAsciiString();
- info.CollideType[block] = (BlockCollideType)reader.ReadUInt8();
- // TODO: Liquid collide type not properly supported.
- info.SpeedMultiplier[block] = (float)Math.Pow( 2, (reader.ReadUInt8() - 128) / 64f );
- info.SetTop( reader.ReadUInt8(), (Block)block );
- info.SetSide( reader.ReadUInt8(), (Block)block );
- info.SetBottom( reader.ReadUInt8(), (Block)block );
- reader.ReadUInt8(); // opacity hint, but we ignore this.
- info.BlocksLight[block] = reader.ReadUInt8() == 0;
- reader.ReadUInt8(); // walk sound, but we ignore this.
- info.EmitsLight[block] = reader.ReadUInt8() != 0;
-
- if( opcode == (byte)PacketId.CpeDefineBlock ) {
- byte shape = reader.ReadUInt8();
- if( shape == 1 ) info.Height[block] = 1;
- else if( shape == 2 ) info.Height[block] = 0.5f;
- // TODO: upside down slab not properly supported
- else if( shape == 3 ) info.Height[block] = 0.5f;
- else if( shape == 4 ) info.IsSprite[block] = true;
-
- byte blockDraw = reader.ReadUInt8();
- if( blockDraw == 0 ) info.IsOpaque[block] = true;
- else if( blockDraw == 1 ) info.IsTransparent[block] = true;
- else if( blockDraw == 2 ) info.IsTranslucent[block] = true;
- else if( blockDraw == 3 ) info.IsTranslucent[block] = true;
-
- Console.WriteLine( block + " : " + shape + "," + blockDraw );
- } else {
- byte fogDensity = reader.ReadUInt8();
- info.FogDensity[block] = fogDensity == 0 ? 0 : (fogDensity + 1) / 128f;
- info.FogColour[block] = new FastColour(
- reader.ReadUInt8(), reader.ReadUInt8(), reader.ReadUInt8() );
- }
- info.SetupCullingCache();
- } break;
-
- case PacketId.CpeRemoveBlockDefinition:
- game.BlockInfo.ResetBlockInfo( reader.ReadUInt8() );
- break;
-
- default:
- throw new NotImplementedException( "Unsupported packet:" + (PacketId)opcode );
+ if( mapSizeIndex == 4 ) {
+ if( map == null ) {
+ int size = mapSize[0] << 24 | mapSize[1] << 16 | mapSize[2] << 8 | mapSize[3];
+ map = new byte[size];
+ }
+ mapIndex += gzipStream.Read( map, mapIndex, map.Length - mapIndex );
+ }
+ }
+
+ reader.Remove( 1024 );
+ byte progress = reader.ReadUInt8();
+ game.Events.RaiseMapLoading( progress );
+ }
+
+ void HandleLevelFinalise() {
+ game.SetNewScreen( new NormalScreen( game ) );
+ int mapWidth = reader.ReadInt16();
+ int mapHeight = reader.ReadInt16();
+ int mapLength = reader.ReadInt16();
+
+ double loadingMs = ( DateTime.UtcNow - receiveStart ).TotalMilliseconds;
+ Utils.LogDebug( "map loading took:" + loadingMs );
+ game.Map.UseRawMap( map, mapWidth, mapHeight, mapLength );
+ game.Events.RaiseOnNewMapLoaded();
+
+ map = null;
+ gzipStream.Dispose();
+ if( sendWomId && !sentWomId ) {
+ SendChat( "/womid WoMClient-2.0.7" );
+ sentWomId = true;
+ }
+ gzipStream = null;
+ GC.Collect();
+ }
+
+ void HandleSetBlock() {
+ int x = reader.ReadInt16();
+ int y = reader.ReadInt16();
+ int z = reader.ReadInt16();
+ byte type = reader.ReadUInt8();
+
+ if( game.Map.IsNotLoaded )
+ Utils.LogWarning( "Server tried to update a block while still sending us the map!" );
+ else if( !game.Map.IsValidPos( x, y, z ) )
+ Utils.LogWarning( "Server tried to update a block at an invalid position!" );
+ else
+ game.UpdateBlock( x, y, z, type );
+ }
+
+ void HandleAddEntity() {
+ byte entityId = reader.ReadUInt8();
+ string name = reader.ReadAsciiString();
+ AddEntity( entityId, name, name, true );
+ }
+
+ void HandleEntityTeleport() {
+ byte entityId = reader.ReadUInt8();
+ ReadAbsoluteLocation( entityId, true );
+ }
+
+ void HandleRelPosAndOrientationUpdate() {
+ byte playerId = reader.ReadUInt8();
+ float x = reader.ReadInt8() / 32f;
+ float y = reader.ReadInt8() / 32f;
+ float z = reader.ReadInt8() / 32f;
+
+ float yaw = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
+ float pitch = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
+ LocationUpdate update = LocationUpdate.MakePosAndOri( x, y, z, yaw, pitch, true );
+ UpdateLocation( playerId, update, true );
+ }
+
+ void HandleRelPositionUpdate() {
+ byte playerId = reader.ReadUInt8();
+ float x = reader.ReadInt8() / 32f;
+ float y = reader.ReadInt8() / 32f;
+ float z = reader.ReadInt8() / 32f;
+
+ LocationUpdate update = LocationUpdate.MakePos( x, y, z, true );
+ UpdateLocation( playerId, update, true );
+ }
+
+ void HandleOrientationUpdate() {
+ byte playerId = reader.ReadUInt8();
+ float yaw = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
+ float pitch = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
+
+ LocationUpdate update = LocationUpdate.MakeOri( yaw, pitch );
+ UpdateLocation( playerId, update, true );
+ }
+
+ void HandleRemoveEntity() {
+ byte entityId = reader.ReadUInt8();
+ Player player = game.Players[entityId];
+ if( entityId != 0xFF && player != null ) {
+ game.Events.RaiseEntityRemoved( entityId );
+ player.Despawn();
+ game.Players[entityId] = null;
}
}
+ void HandleMessage() {
+ byte messageType = reader.ReadUInt8();
+ string text = reader.ReadChatString( ref messageType, useMessageTypes );
+ game.Chat.Add( text, (CpeMessage)messageType );
+ }
+
+ void HandleKick() {
+ string reason = reader.ReadAsciiString();
+ game.Disconnect( "&eLost connection to the server", reason );
+ Dispose();
+ }
+
+ void HandleSetPermission() {
+ game.LocalPlayer.SetUserType( reader.ReadUInt8() );
+ }
+
+
void AddEntity( byte entityId, string displayName, string skinName, bool readPosition ) {
if( entityId != 0xFF ) {
Player oldPlayer = game.Players[entityId];
@@ -729,39 +411,12 @@ namespace ClassicalSharp {
}
}
}
-
- void ReadRelativeLocation() {
- byte playerId = reader.ReadUInt8();
- float x = reader.ReadInt8() / 32f;
- float y = reader.ReadInt8() / 32f;
- float z = reader.ReadInt8() / 32f;
- float yaw = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
- float pitch = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
- LocationUpdate update = LocationUpdate.MakePosAndOri( x, y, z, yaw, pitch, true );
- UpdateLocation( playerId, update, true );
- }
-
- void ReadOrientation() {
- byte playerId = reader.ReadUInt8();
- float yaw = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
- float pitch = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
- LocationUpdate update = LocationUpdate.MakeOri( yaw, pitch );
- UpdateLocation( playerId, update, true );
- }
-
- void ReadRelativePosition() {
- byte playerId = reader.ReadUInt8();
- float x = reader.ReadInt8() / 32f;
- float y = reader.ReadInt8() / 32f;
- float z = reader.ReadInt8() / 32f;
- LocationUpdate update = LocationUpdate.MakePos( x, y, z, true );
- UpdateLocation( playerId, update, true );
- }
-
+
void ReadAbsoluteLocation( byte playerId, bool interpolate ) {
float x = reader.ReadInt16() / 32f;
float y = ( reader.ReadInt16() - 51 ) / 32f; // We have to do this.
if( playerId == 255 ) y += 22/32f;
+
float z = reader.ReadInt16() / 32f;
float yaw = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
float pitch = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
@@ -778,6 +433,29 @@ namespace ClassicalSharp {
player.SetLocation( update, interpolate );
}
}
+
#endregion
+
+ Action[] handlers;
+ int maxHandledPacket;
+
+ void SetupHandlers() {
+ maxHandledPacket = (int)PacketId.Max;
+ handlers = new Action[] { HandleHandshake, HandlePing, HandleLevelInit,
+ HandleLevelDataChunk, HandleLevelFinalise, null, HandleSetBlock,
+ HandleAddEntity, HandleEntityTeleport, HandleRelPosAndOrientationUpdate,
+ HandleRelPositionUpdate, HandleOrientationUpdate, HandleRemoveEntity,
+ HandleMessage, HandleKick, HandleSetPermission,
+
+ HandleCpeExtInfo, HandleCpeExtEntry, HandleCpeSetClickDistance,
+ HandleCpeCustomBlockSupportLevel, HandleCpeHoldThis, null,
+ HandleCpeExtAddPlayerName, HandleCpeExtAddEntity, HandleCpeExtRemovePlayerName,
+ HandleCpeEnvColours, HandleCpeMakeSelection, HandleCpeRemoveSelection,
+ HandleCpeSetBlockPermission, HandleCpeChangeModel, HandleCpeEnvSetMapApperance,
+ HandleCpeEnvWeatherType, HandleCpeHackControl, HandleCpeExtAddEntity2,
+ null, HandleCpeDefineBlockOrLiquid, HandleCpeDefineBlockOrLiquid,
+ HandleCpeRemoveBlockDefinition
+ };
+ }
}
}
\ No newline at end of file
diff --git a/ClassicalSharp/Utils/FastColour.cs b/ClassicalSharp/Utils/FastColour.cs
index 6ce240f82..05821666a 100644
--- a/ClassicalSharp/Utils/FastColour.cs
+++ b/ClassicalSharp/Utils/FastColour.cs
@@ -56,6 +56,7 @@ namespace ClassicalSharp {
result.R = (byte)( value.R * t );
result.G = (byte)( value.G * t );
result.B = (byte)( value.B * t );
+ result.A = 50;
return result;
}