From bf314359cd8e89dfdb62742c61b792a72a0faa55 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 3 Oct 2015 15:04:59 +1000 Subject: [PATCH] Use dispatch table for reading packets, split off CPE reading/writing into a separate file. --- ClassicalSharp/ClassicalSharp.csproj | 3 +- ClassicalSharp/Network/Enums.cs | 2 + .../Network/NetworkProcessor.CPE.cs | 331 ++++++++ ...nfigHandler.cs => NetworkProcessor.WoM.cs} | 1 + ClassicalSharp/Network/NetworkProcessor.cs | 730 +++++------------- ClassicalSharp/Utils/FastColour.cs | 1 + 6 files changed, 541 insertions(+), 527 deletions(-) create mode 100644 ClassicalSharp/Network/NetworkProcessor.CPE.cs rename ClassicalSharp/Network/{WomConfigHandler.cs => NetworkProcessor.WoM.cs} (98%) 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; }