void IOLoop() { byte[] loginPacket = CreateLoginPacket(migratedUsername ?? _username, _ver); ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { if(EndPoint.Port > IPEndPoint.MaxPort || EndPoint.Port < IPEndPoint.MinPort) throw new SocketException(-1); //Do not continue if the user attempts to connect on a port larger than possible. ServerSocket.Connect(EndPoint); ServerSocket.Send(loginPacket); } catch( SocketException ex ) { Log( LogType.Error, HandleSocketError( ex.ErrorCode ), ex.ToString() ); return; } BinaryReader reader = new BinaryReader(new NetworkStream(ServerSocket)); while (true) { try { byte OpCode = reader.ReadByte(); PacketEventArgs opcodeEvent = new PacketEventArgs((ServerPackets)OpCode); Events.RaisePacketReceived(opcodeEvent); switch ((ServerPackets)OpCode) { case ServerPackets.ServerIdentification://0x00 { ProtocolVersion = reader.ReadByte(); // Protocol version should be seven. string Name = Encoding.ASCII.GetString(reader.ReadBytes(64)).Trim(); string MOTD = Encoding.ASCII.GetString(reader.ReadBytes(64)).Trim(); if(!serverLoaded) { ServerName = Name; ServerMOTD = MOTD; serverLoaded = true; } UserType = reader.ReadByte();//Get the type. 0x64 = Op, 0x00 = Normal user. } break; case ServerPackets.Ping://0x01 break; // Server only requires we read the packet ID. case ServerPackets.LevelInitialize://0x02 Players.Clear(); if(_savemap) mapStream = new MemoryStream(); CanReconnectAfterKick = true; break; case ServerPackets.LevelDataChunk://0x03 { int chunkLength = IPAddress.HostToNetworkOrder(reader.ReadInt16()); // Length of non padded data. byte[] chunkData = reader.ReadBytes(1024); // May be padded with extra data. byte progress = reader.ReadByte(); if(_savemap) { mapStream.Write( chunkData, 0, chunkLength ); } MapProgressEventArgs e = new MapProgressEventArgs(progress); Events.RaiseMapProgress(e); } break; case ServerPackets.LevelFinalize://0x04: { MapSizeX = IPAddress.HostToNetworkOrder(reader.ReadInt16()); MapSizeZ = IPAddress.HostToNetworkOrder(reader.ReadInt16()); MapSizeY = IPAddress.HostToNetworkOrder(reader.ReadInt16()); Connected = true; //At this state, we've loaded the map and we're ready to send chat etc. if(_savemap) { Log( LogType.BotActivity, "Beginning to save map.." ); mapStream.Seek(0, SeekOrigin.Begin); Map map = new Map(MapSizeX, MapSizeY, MapSizeZ); using (GZipStream decompressed = new GZipStream( mapStream,CompressionMode.Decompress ) ) { decompressed.Read( new byte[4], 0, 4 ); //Ignore size of stream. int sizeX = MapSizeX, sizeY = MapSizeY, sizeZ = MapSizeZ; for( int z = 0; z < sizeZ; z++ ) { for( int y = 0; y < sizeY; y++ ) { for( int x = 0; x < sizeX; x++ ) { byte next = (byte)decompressed.ReadByte(); map.SetBlock( x, y, z, next ); } } } } mapStream.Dispose(); map.Save("map_" + DateTime.Now.ToString("ddHHmmssfffffff")); //Formatting for DateTime. map.Dispose(); Log( LogType.BotActivity, "Saved map" ); } MapLoadedEventArgs e = new MapLoadedEventArgs(); Events.RaiseMapLoaded(e); } break; case ServerPackets.SetBlock://0x06: { int blockX = IPAddress.HostToNetworkOrder(reader.ReadInt16()); int blockZ = IPAddress.HostToNetworkOrder(reader.ReadInt16()); int blockY = IPAddress.HostToNetworkOrder(reader.ReadInt16()); byte blockType = reader.ReadByte(); BlockPlacedEventArgs e = new BlockPlacedEventArgs(blockX, blockY, blockZ, blockType); Events.RaiseBlockPlaced(e); if(marksLeft > 0 && blockType == 39 && CubID != null) { byte id = CubID.Value; if(Players[id].X < 0 || Players[id].Y < 0 || Players[id].Z < 0) { SendMessagePacket("Error: You are too far away from the bot."); break; } if(new Vector3I(blockX, blockY, blockZ).DistanceTo(new Vector3I( (int)Players[id].X, (int)Players[id].Y, (int)Players[id].Z)) > 11) { break; //Another player probably tried placing a block somewhere else. } marks[marks.Length - marksLeft] = new Vector3I(blockX, blockY, blockZ); marksLeft--; //Go from smallest to largest. if(marksLeft == 0 && QueuedDrawer != null) { Draw(QueuedDrawer, marks, cuboidType); } } } break; case ServerPackets.SpawnPlayer://0x07: { byte pID = reader.ReadByte(); string playername = Encoding.ASCII.GetString(reader.ReadBytes(64)).Trim(); Players[pID] = new Player(); Players[pID].Name = playername; Players[pID].X = IPAddress.HostToNetworkOrder(reader.ReadInt16()) / 32f; Players[pID].Z = IPAddress.HostToNetworkOrder(reader.ReadInt16()) / 32f; Players[pID].Y = IPAddress.HostToNetworkOrder(reader.ReadInt16()) / 32f; Players[pID].Yaw = reader.ReadByte(); Players[pID].Pitch = reader.ReadByte(); PositionEventArgs e = new PositionEventArgs(pID, Players[pID]); Events.RaisePlayerMoved(e); } break; case ServerPackets.PlayerTeleport://0x08 { byte pID = reader.ReadByte(); Players[pID].X = IPAddress.HostToNetworkOrder(reader.ReadInt16()) / 32f; Players[pID].Z = IPAddress.HostToNetworkOrder(reader.ReadInt16()) / 32f; Players[pID].Y = IPAddress.HostToNetworkOrder(reader.ReadInt16()) / 32f; Players[pID].Yaw = reader.ReadByte(); Players[pID].Pitch = reader.ReadByte(); PositionEventArgs e = new PositionEventArgs(pID, Players[pID]); Events.RaisePlayerMoved(e); } break; case ServerPackets.PositionandOrientationUpdate://0x09 { byte pID = reader.ReadByte(); Players[pID].X += reader.ReadSByte() / 32f; Players[pID].Z += reader.ReadSByte() / 32f; Players[pID].Y += reader.ReadSByte() / 32f; Players[pID].Yaw = reader.ReadByte(); Players[pID].Pitch = reader.ReadByte(); PositionEventArgs e = new PositionEventArgs(pID, Players[pID]); Events.RaisePlayerMoved(e); } break; case ServerPackets.PositionUpdate://0x0a { byte pID = reader.ReadByte(); Players[pID].X += (reader.ReadSByte() / 32f); Players[pID].Z += (reader.ReadSByte() / 32f); Players[pID].Y += (reader.ReadSByte() / 32f); PositionEventArgs e = new PositionEventArgs(pID, Players[pID]); Events.RaisePlayerMoved(e); } break; case ServerPackets.OrientationUpdate://0x0b { byte pID = reader.ReadByte(); Players[pID].Yaw = reader.ReadByte(); Players[pID].Pitch = reader.ReadByte(); PositionEventArgs e = new PositionEventArgs(pID, Players[pID]); Events.RaisePlayerMoved(e); } break; case ServerPackets.DespawnPlayer://0x0c { byte playerID = reader.ReadByte(); Players.Remove(playerID); // Also sent when the player joins another map. } break; case ServerPackets.Message://0x0d { reader.ReadByte(); // Most servers don't send the ID. string Line = Encoding.ASCII.GetString(reader.ReadBytes(64)).Trim(); MessageEventArgs e = new MessageEventArgs(Line); //Raise the event, let the event handler take care of splitting. Events.RaiseChatMessage(e); Log( LogType.Chat, Line ); if (Line.Contains(_delimiter.ToString())) { //Most servers use : as the delimiter. string[] lineSplit = Line.Split(new char[] { _delimiter }, 2); string Message = Extensions.StripColors(lineSplit[1]).TrimStart(' '); string User = Extensions.StripColors(lineSplit[0]); if (!IgnoredUserList.Contains(User)) { string strippedcommandheader = Message.Split(' ')[0].ToLower(); foreach(KeyValuePair<string,CommandDelegate> command in RegisteredCommands) { if(strippedcommandheader == "." + command.Key.ToLower()) { if(_requiresop) { if(Users.Contains(User)) { EnqueueCommand(command.Value,Line); break; } else { SendMessagePacket(User +", you are not allowed to use the bot."); } } else { EnqueueCommand(command.Value,Line); break; } } } } } ProcessCommandQueue(); } break; case ServerPackets.DisconnectSelf://0x0e { string reason = Encoding.ASCII.GetString(reader.ReadBytes(64)).Trim(); KickedEventArgs e = new KickedEventArgs(reason, CanReconnectAfterKick); Events.RaiseGotKicked(e); if( _reconnectonkick == true && CanReconnectAfterKick == true ) { Log( LogType.BotActivity, "Kicked from the server. Reconnecting.", reason ); CanReconnectAfterKick = false; Thread thread = new Thread(IOLoop); thread.Name = "LibClassicBotIO"; thread.IsBackground = true; thread.Start(); } else if(_reconnectonkick == false) { Log( LogType.Warning, "Kicked from the server. Not attempting to reconnect.", reason ); } else { ServerSocket.Close(); Log( LogType.Error, "The bot was prevented from reconnecting.", "(Kick packet received before a LevelBegin packet.)" ); } return; } case ServerPackets.SetPermission://0x0f UserType = reader.ReadByte();//Issued in Vanilla when someone calls /op, used in fCraft for bedrock permission checking. break; default: throw new IOException("Unrecognised packet. Opcode: " + OpCode.ToString()); } } catch ( IOException ex ) { if( _reconnectonkick == true && CanReconnectAfterKick == true ) { CancelDrawer(); CanReconnectAfterKick = false; Thread thread = new Thread(IOLoop); thread.Name = "LibClassicBotIO"; thread.IsBackground = true; thread.Start(); } else { Log( LogType.Error, "An I/O error has occured. The connection have been closed uncleanly. Exiting the bot.", ex.ToString() ); } return; } } }
/// <summary>Raises a new PacketReceived Event.</summary> internal void RaisePacketReceived(PacketEventArgs e) { if (PacketReceived != null) PacketReceived(null, e); }