Use dispatch table for reading packets, split off CPE reading/writing into a separate file.

This commit is contained in:
UnknownShadow200 2015-10-03 15:04:59 +10:00
parent 9f4d015709
commit bf314359cd
6 changed files with 541 additions and 527 deletions

View file

@ -146,12 +146,13 @@
<Compile Include="Model\ZombieModel.cs" />
<Compile Include="Network\Enums.cs" />
<Compile Include="Network\INetworkProcessor.cs" />
<Compile Include="Network\NetworkProcessor.CPE.cs" />
<Compile Include="Network\NetworkProcessor.cs" />
<Compile Include="Network\Utils\AsyncDownloader.cs" />
<Compile Include="Network\Utils\FastNetReader.cs" />
<Compile Include="Network\Utils\FixedBufferStream.cs" />
<Compile Include="Network\Utils\GZipHeaderReader.cs" />
<Compile Include="Network\WomConfigHandler.cs" />
<Compile Include="Network\NetworkProcessor.WoM.cs" />
<Compile Include="Commands\CommandManager.cs" />
<Compile Include="Commands\CommandReader.cs" />
<Compile Include="Physics\BoundingBox.cs" />

View file

@ -42,6 +42,8 @@ namespace ClassicalSharp {
CpeDefineBlock = 35,
CpeDefineLiquid = 36,
CpeRemoveBlockDefinition = 37,
Max,
}
public enum CpeMessage {

View file

@ -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
}

View file

@ -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;

View file

@ -1,17 +1,16 @@
using System;
using System.Drawing;
using System.IO;
#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;
#if __MonoCS__
using Ionic.Zlib;
#else
using System.IO.Compression;
#endif
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;
if( opcode >= maxHandledPacket || (handler = handlers[opcode]) == null)
throw new NotImplementedException( "Unsupported packet:" + (PacketId)opcode );
handler();
}
case PacketId.Ping:
break;
void HandleHandshake() {
byte protocolVer = reader.ReadUInt8();
ServerName = reader.ReadAsciiString();
ServerMotd = reader.ReadAsciiString();
game.LocalPlayer.SetUserType( reader.ReadUInt8() );
receivedFirstPosition = false;
game.LocalPlayer.ParseHackFlags( ServerName, ServerMotd );
}
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;
void HandlePing() {
}
case PacketId.LevelDataChunk:
{
int usedLength = reader.ReadInt16();
gzippedMap.Position = 0;
gzippedMap.SetLength( usedLength );
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
if( gzipHeader.done || gzipHeader.ReadHeader( gzippedMap ) ) {
if( mapSizeIndex < 4 ) {
mapSizeIndex += gzipStream.Read( mapSize, mapSizeIndex, 4 - mapSizeIndex );
}
mapSizeIndex = 0;
mapIndex = 0;
receiveStart = DateTime.UtcNow;
}
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;
void HandleLevelDataChunk() {
int usedLength = reader.ReadInt16();
gzippedMap.Position = 0;
gzippedMap.SetLength( usedLength );
case PacketId.LevelFinalise:
{
game.SetNewScreen( new NormalScreen( game ) );
int mapWidth = reader.ReadInt16();
int mapHeight = reader.ReadInt16();
int mapLength = reader.ReadInt16();
if( gzipHeader.done || gzipHeader.ReadHeader( gzippedMap ) ) {
if( mapSizeIndex < 4 ) {
mapSizeIndex += gzipStream.Read( mapSize, mapSizeIndex, 4 - mapSizeIndex );
}
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;
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 );
}
}
case PacketId.SetBlock:
{
int x = reader.ReadInt16();
int y = reader.ReadInt16();
int z = reader.ReadInt16();
byte type = reader.ReadUInt8();
reader.Remove( 1024 );
byte progress = reader.ReadUInt8();
game.Events.RaiseMapLoading( progress );
}
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;
void HandleLevelFinalise() {
game.SetNewScreen( new NormalScreen( game ) );
int mapWidth = reader.ReadInt16();
int mapHeight = reader.ReadInt16();
int mapLength = reader.ReadInt16();
case PacketId.AddEntity:
{
byte entityId = reader.ReadUInt8();
string name = reader.ReadAsciiString();
AddEntity( entityId, name, name, true );
} break;
double loadingMs = ( DateTime.UtcNow - receiveStart ).TotalMilliseconds;
Utils.LogDebug( "map loading took:" + loadingMs );
game.Map.UseRawMap( map, mapWidth, mapHeight, mapLength );
game.Events.RaiseOnNewMapLoaded();
case PacketId.EntityTeleport:
{
byte entityId = reader.ReadUInt8();
ReadAbsoluteLocation( entityId, true );
} break;
map = null;
gzipStream.Dispose();
if( sendWomId && !sentWomId ) {
SendChat( "/womid WoMClient-2.0.7" );
sentWomId = true;
}
gzipStream = null;
GC.Collect();
}
case PacketId.RelPosAndOrientationUpdate:
ReadRelativeLocation();
break;
void HandleSetBlock() {
int x = reader.ReadInt16();
int y = reader.ReadInt16();
int z = reader.ReadInt16();
byte type = reader.ReadUInt8();
case PacketId.RelPosUpdate:
ReadRelativePosition();
break;
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 );
}
case PacketId.OrientationUpdate:
ReadOrientation();
break;
void HandleAddEntity() {
byte entityId = reader.ReadUInt8();
string name = reader.ReadAsciiString();
AddEntity( entityId, name, name, true );
}
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;
void HandleEntityTeleport() {
byte entityId = reader.ReadUInt8();
ReadAbsoluteLocation( entityId, true );
}
case PacketId.Message:
{
byte messageType = reader.ReadUInt8();
string text = reader.ReadChatString( ref messageType, useMessageTypes );
game.Chat.Add( text, (CpeMessage)messageType );
} break;
void HandleRelPosAndOrientationUpdate() {
byte playerId = reader.ReadUInt8();
float x = reader.ReadInt8() / 32f;
float y = reader.ReadInt8() / 32f;
float z = reader.ReadInt8() / 32f;
case PacketId.Kick:
{
string reason = reader.ReadAsciiString();
game.Disconnect( "&eLost connection to the server", reason );
Dispose();
} break;
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 );
}
case PacketId.SetPermission:
{
game.LocalPlayer.SetUserType( reader.ReadUInt8() );
} break;
void HandleRelPositionUpdate() {
byte playerId = reader.ReadUInt8();
float x = reader.ReadInt8() / 32f;
float y = reader.ReadInt8() / 32f;
float z = reader.ReadInt8() / 32f;
case PacketId.CpeExtInfo:
{
string appName = reader.ReadAsciiString();
Utils.LogDebug( "Server identified itself as: " + appName );
cpeServerExtensionsCount = reader.ReadInt16();
} break;
LocationUpdate update = LocationUpdate.MakePos( x, y, z, true );
UpdateLocation( playerId, update, true );
}
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--;
void HandleOrientationUpdate() {
byte playerId = reader.ReadUInt8();
float yaw = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
float pitch = (float)Utils.PackedToDegrees( reader.ReadUInt8() );
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;
LocationUpdate update = LocationUpdate.MakeOri( yaw, pitch );
UpdateLocation( playerId, update, true );
}
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 );
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];
@ -730,38 +412,11 @@ 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
};
}
}
}

View file

@ -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;
}