bool NegotiateProtocolExtension()
{
// write our ExtInfo and ExtEntry packets
writer.Write(Packet.MakeExtInfo("ProCraft", 30).Bytes);
writer.Write(Packet.MakeExtEntry(ClickDistanceExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(CustomBlocksExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(HeldBlockExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(TextHotKeyExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(ExtPlayerListExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(EnvColorsExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(SelectionCuboidExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(BlockPermissionsExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(ChangeModelExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(EnvMapAppearanceExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(EnvWeatherTypeExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(HackControlExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(ExtPlayerListExtName, 2).Bytes);
writer.Write(Packet.MakeExtEntry(PlayerClickExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(MessageTypesExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(EmoteFixExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(LongerMessagesExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(FullCP437ExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(BlockDefinitionsExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(BlockDefinitionsExtExtName, 2).Bytes);
writer.Write(Packet.MakeExtEntry(BulkBlockUpdateExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(TextColorsExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(EnvMapAspectExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(ExtPlayerPositionsExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(EntityPropertyExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(TwoWayPingExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(InventoryOrderExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(InstantMOTDExtName, 1).Bytes);
writer.Write(Packet.MakeExtEntry(FastMapExtName, 1).Bytes);
// Fix for ClassiCube Client which violates the spec -
// If server supports version > 1 but client version 1, client should reply with version 1.
// ClassiCube just doesn't reply at all in that case.
writer.Write(Packet.MakeExtEntry(EnvMapAppearanceExtName, 2).Bytes);
// Expect ExtInfo reply from the client
OpCode extInfoReply = reader.ReadOpCode();
//Logger.Log(LogType.Debug, "Expected: {0} / Received: {1}", OpCode.ExtInfo, extInfoReply );
if (extInfoReply != OpCode.ExtInfo)
{
Logger.Log(LogType.Debug, "Player {0}: Unexpected ExtInfo reply ({1})", Info.Name, extInfoReply);
return(false);
}
ClientName = reader.ReadString();
int expectedEntries = reader.ReadInt16();
// wait for client to send its ExtEntries
for (int i = 0; i < expectedEntries; i++)
{
// Expect ExtEntry replies (0 or more)
OpCode extEntryReply = reader.ReadOpCode();
if (extEntryReply != OpCode.ExtEntry)
{
Logger.Log(LogType.Warning, "Player {0} from {1}: Unexpected ExtEntry reply ({2})", Name, IP,
extInfoReply);
return(false);
}
string extName = reader.ReadString();
int version = reader.ReadInt32();
CpeExt ext = CpeExt.None;
switch (extName)
{
case CustomBlocksExtName:
if (version == 1)
{
ext = CpeExt.CustomBlocks;
}
break;
case BlockPermissionsExtName:
if (version == 1)
{
ext = CpeExt.BlockPermissions;
}
break;
case ClickDistanceExtName:
if (version == 1)
{
ext = CpeExt.ClickDistance;
}
break;
case EnvColorsExtName:
if (version == 1)
{
ext = CpeExt.EnvColors;
}
break;
case ChangeModelExtName:
if (version == 1)
{
ext = CpeExt.ChangeModel;
}
break;
case EnvMapAppearanceExtName:
if (version == 1)
{
ext = CpeExt.EnvMapAppearance;
}
if (version == 2)
{
ext = CpeExt.EnvMapAppearance2;
}
break;
case EnvWeatherTypeExtName:
if (version == 1)
{
ext = CpeExt.EnvWeatherType;
}
break;
case HeldBlockExtName:
if (version == 1)
{
ext = CpeExt.HeldBlock;
}
break;
case ExtPlayerListExtName:
if (version == 1)
{
ext = CpeExt.ExtPlayerList;
if (Supports(CpeExt.ExtPlayerList2))
{
ext = CpeExt.ExtPlayerList2;
}
}
else if (version == 2)
{
ext = CpeExt.ExtPlayerList2;
if (Supports(CpeExt.ExtPlayerList))
{
supportedExts.Remove(CpeExt.ExtPlayerList);
}
}
break;
case SelectionCuboidExtName:
if (version == 1)
{
ext = CpeExt.SelectionCuboid;
}
break;
case MessageTypesExtName:
if (version == 1)
{
ext = CpeExt.MessageType;
}
break;
case HackControlExtName:
if (version == 1)
{
ext = CpeExt.HackControl;
}
break;
case EmoteFixExtName:
if (version == 1)
{
ext = CpeExt.EmoteFix;
}
break;
case TextHotKeyExtName:
if (version == 1)
{
ext = CpeExt.TextHotKey;
}
break;
case PlayerClickExtName:
if (version == 1)
{
ext = CpeExt.PlayerClick;
}
break;
case LongerMessagesExtName:
if (version == 1)
{
ext = CpeExt.LongerMessages;
}
break;
case FullCP437ExtName:
if (version == 1)
{
ext = CpeExt.FullCP437;
}
break;
case BlockDefinitionsExtName:
if (version == 1)
{
ext = CpeExt.BlockDefinitions;
}
break;
case BlockDefinitionsExtExtName:
if (version == 1)
{
ext = CpeExt.BlockDefinitionsExt;
}
if (version == 2)
{
ext = CpeExt.BlockDefinitionsExt2;
}
break;
case BulkBlockUpdateExtName:
if (version == 1)
{
ext = CpeExt.BulkBlockUpdate;
}
break;
case TextColorsExtName:
if (version == 1)
{
ext = CpeExt.TextColors;
}
break;
case EnvMapAspectExtName:
if (version == 1)
{
ext = CpeExt.EnvMapAspect;
}
break;
case ExtPlayerPositionsExtName:
if (version == 1)
{
ext = CpeExt.ExtPlayerPositions;
}
supportsExtPositions = true;
break;
case EntityPropertyExtName:
if (version == 1)
{
ext = CpeExt.EntityProperty;
}
break;
case TwoWayPingExtName:
if (version == 1)
{
ext = CpeExt.TwoWayPing;
}
break;
case InventoryOrderExtName:
if (version == 1)
{
ext = CpeExt.InventoryOrder;
}
break;
case InstantMOTDExtName:
if (version == 1)
{
ext = CpeExt.InstantMOTD;
}
break;
case FastMapExtName:
if (version == 1)
{
ext = CpeExt.FastMap;
}
break;
}
if (ext != CpeExt.None)
{
supportedExts.Add(ext);
}
}
supportsCustomBlocks = Supports(CpeExt.CustomBlocks);
supportsBlockDefs = Supports(CpeExt.BlockDefinitions);
// log client's capabilities
if (supportedExts.Count > 0)
{
Logger.Log(LogType.Debug, "Player {0} is using \"{1}\", supporting: {2}",
Info.Name, ClientName, supportedExts.JoinToString(", "));
}
else if (supportedExts.Count == 0)
{
Kick("Please use the ClassicalSharp client", LeaveReason.InvalidOpcodeKick);
}
// if client also supports CustomBlockSupportLevel, figure out what level to use
// Send CustomBlockSupportLevel
writer.Write(Packet.MakeCustomBlockSupportLevel(CustomBlocksLevel).Bytes);
if (Supports(CpeExt.TextColors))
{
for (int i = 0; i < Color.ExtColors.Length; i++)
{
if (Color.ExtColors[i].Undefined)
{
continue;
}
writer.Write(Packet.MakeSetTextColor(Color.ExtColors[i]).Bytes);
}
}
return(true);
}