public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			if (client.Player == null)
				return;

			int code = packet.ReadByte();
			if (code != 0)
			{
				if (log.IsWarnEnabled)
					log.WarnFormat("bonuses button: code is other than zero ({0})", code);
			}

			new RegionTimerAction<GamePlayer>(client.Player,
			                                  p => p.Out.SendCustomTextWindow(LanguageMgr.GetTranslation(client.Account.Language, "PlayerBonusesListRequestHandler.HandlePacket.Bonuses")
			                                                                  , new List<string>(client.Player.GetBonuses()))).Start(1);
		}
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			if (client.Player == null)
				return;

			int code = packet.ReadByte();
			if (code != 0)
			{
				if (log.IsWarnEnabled)
					log.Warn("bonuses button: code is other than zero (" + code + ")");
			}

			var info = new List<string>();

			client.Player.GetBonuses(info);

			client.Out.SendCustomTextWindow(LanguageMgr.GetTranslation(client.Account.Language, "PlayerBonusesListRequestHandler.HandlePacket.Bonuses"), info);
		}
Пример #3
0
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			if (client.Player == null)
				return;
			uint X = packet.ReadInt();
			uint Y = packet.ReadInt();
			ushort id =(ushort) packet.ReadShort();
			ushort obj=(ushort) packet.ReadShort();

			GameObject target = client.Player.TargetObject;
			if (target == null)
			{
				client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerPickUpRequestHandler.HandlePacket.Target"), eChatType.CT_System, eChatLoc.CL_SystemWindow);
				return;
			}
			if (target.ObjectState != GameObject.eObjectState.Active)
			{
				client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerPickUpRequestHandler.HandlePacket.InvalidTarget"), eChatType.CT_System, eChatLoc.CL_SystemWindow);
				return;
			}
			
			client.Player.PickupObject(target, false);
		}
 public void HandlePacket(GameClient client, GSPacketIn packet)
 {
     // todo
 }
Пример #5
0
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			if (client == null || client.Player == null)
				return;

			if (client.Player.ObjectState != GameObject.eObjectState.Active) return;

			ushort sessionId = packet.ReadShort(); // session ID
			if (client.SessionID != sessionId)
			{
//				GameServer.BanAccount(client, 120, "Hack sessionId", string.Format("Wrong sessionId:0x{0} in 0xBA packet (SessionID:{1})", sessionId, client.SessionID));
				return; // client hack
			}

			ushort head = packet.ReadShort();
			client.Player.Heading = (ushort)(head & 0xFFF);
			packet.Skip(1); // unknown
			int flags = packet.ReadByte();
//			client.Player.PetInView = ((flags & 0x04) != 0); // TODO
			client.Player.GroundTargetInView = ((flags & 0x08) != 0);
			client.Player.TargetInView = ((flags & 0x10) != 0);

			byte[] con = packet.ToArray();
			if (!client.Player.IsAlive)
			{
				con[9] = 5; // set dead state
			}
			else if (client.Player.Steed != null && client.Player.Steed.ObjectState == GameObject.eObjectState.Active)
			{
				client.Player.Heading = client.Player.Steed.Heading;
				con[9] = 6; // Set ride state
				con[7] = (byte)(client.Player.Steed.RiderSlot(client.Player)); // there rider slot this player
				con[2] = (byte)(client.Player.Steed.ObjectID >> 8); //heading = steed ID
				con[3] = (byte)(client.Player.Steed.ObjectID & 0xFF);
			}
			con[5] &= 0xC0; //11 00 00 00 = 0x80(Torch) + 0x40(Unknown), all other in view check's not need send anyone
			if (client.Player.IsWireframe)
			{
				con[5] |= 0x01;
			}
			//stealth is set here
			if (client.Player.IsStealthed)
			{
				con[5] |= 0x02;
			}
			con[8] = (byte)((con[8] & 0x80) | client.Player.HealthPercent);

			GSUDPPacketOut outpak = new GSUDPPacketOut(client.Out.GetPacketCode(eServerPackets.PlayerHeading));
			//Now copy the whole content of the packet
			outpak.Write(con, 0, /*con.Length*/10);
			outpak.WritePacketLength();

			GSUDPPacketOut outpak190 = null;

//			byte[] outp = outpak.GetBuffer();
//			outpak = null;

			foreach(GamePlayer player in client.Player.GetPlayersInRadius(WorldMgr.VISIBILITY_DISTANCE))
			{
				if(player != null && player != client.Player)
				{
					if (player.Client.Version >= GameClient.eClientVersion.Version190)
					{
						if (outpak190 == null)
						{
	 						outpak190 = new GSUDPPacketOut(client.Out.GetPacketCode(eServerPackets.PlayerHeading));
	 						byte[] con190 = (byte[]) con.Clone();
							//Now copy the whole content of the packet
							outpak190.Write(con190, 0, /*con190.Lenght*/10);
							outpak190.WriteByte(client.Player.ManaPercent);
							outpak190.WriteByte(client.Player.EndurancePercent);
							outpak190.WritePacketLength();
//							byte[] outp190 = outpak190.GetBuffer();
//							outpak190 = null;// ?
						}
						player.Out.SendUDPRaw(outpak190);
					}
					else
						player.Out.SendUDPRaw(outpak);
				}
			}
		}
Пример #6
0
		/// <summary>
		/// UDP event handler. Called when a UDP packet is waiting to be read
		/// </summary>
		/// <param name="ar"></param>
		protected void RecvFromCallback(IAsyncResult ar)
		{
			if (m_status != eGameServerStatus.GSS_Open)
				return;

			if (ar == null)
				return;

			var server = (GameServer) ar.AsyncState;
			Socket s = server.UDPSocket;
			GameClient client = null;

			if (s != null)
			{
				//Creates a temporary EndPoint to pass to EndReceiveFrom.
				EndPoint tempRemoteEP = new IPEndPoint(IPAddress.Any, 0);
				bool receiving = false;
				try
				{
					// Handle the packet
					int read = s.EndReceiveFrom(ar, ref tempRemoteEP);
					if (read == 0)
					{
						log.Debug("UDP received bytes = 0");
					}
					else
					{
						int pakCheck = (server.UDPBuffer[read - 2] << 8) | server.UDPBuffer[read - 1];
						int calcCheck = PacketProcessor.CalculateChecksum(server.UDPBuffer, 0, read - 2);

						if (calcCheck != pakCheck)
						{
							if (log.IsWarnEnabled)
								log.WarnFormat("Bad UDP packet checksum (packet:0x{0:X4} calculated:0x{1:X4}) -> ignored", pakCheck, calcCheck);
							if (log.IsDebugEnabled)
								log.Debug(Marshal.ToHexDump("UDP buffer dump, received " + read + "bytes", server.UDPBuffer));
						}
						else
						{
							var sender = (IPEndPoint) (tempRemoteEP);

							var pakin = new GSPacketIn(read - GSPacketIn.HDR_SIZE);
							pakin.Load(server.UDPBuffer, 0, read);

							//Get the next message
							BeginReceiveUDP(s, server);
							receiving = true;

							client = WorldMgr.GetClientFromID(pakin.SessionID);

							if (client != null)
							{
								//If this is the first message from the client, we
								//save the endpoint!
								if (client.UdpEndPoint == null)
								{
									client.UdpEndPoint = sender;
									client.UdpConfirm = false;
								}
								//Only handle the packet if it comes from a valid client
								if (client.UdpEndPoint.Equals(sender))
								{
									client.PacketProcessor.HandlePacket(pakin);
								}
							}
							else if (log.IsErrorEnabled)
							{
								log.Error(
									string.Format("Got an UDP packet from invalid client id or ip: client id = {0}, ip = {1},  code = {2:x2}",
									              pakin.SessionID, sender, pakin.ID));
							}
						}
					}
				}
				catch (SocketException)
				{
				}
				catch (ObjectDisposedException)
				{
				}
				catch (Exception e)
				{
					if (log.IsErrorEnabled)
						log.Error("RecvFromCallback", e);
				}
				finally
				{
					if (!receiving)
					{
						//Get the next message
						//Even if we have errors, we need to continue with UDP
						BeginReceiveUDP(s, server);
					}
				}
			}
		}
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			string accountName = packet.ReadString(24);

			if (log.IsDebugEnabled)
				log.DebugFormat("CharacterCreateRequestHandler for account {0} using version {1}", accountName, client.Version);

			if (!accountName.StartsWith(client.Account.Name))// TODO more correctly check, client send accountName as account-S, -N, -H (if it not fit in 20, then only account)
			{
				if (ServerProperties.Properties.BAN_HACKERS)
					client.BanAccount(string.Format("Autoban wrong Account '{0}'", accountName));

				client.Disconnect();
				return;
			}
			
			// Realm
			eRealm currentRealm = eRealm.None;
			if (accountName.EndsWith("-S")) currentRealm = eRealm.Albion;
			else if (accountName.EndsWith("-N")) currentRealm = eRealm.Midgard;
			else if (accountName.EndsWith("-H")) currentRealm = eRealm.Hibernia;

			
			// Client character count support
			int charsCount = client.Version < GameClient.eClientVersion.Version173 ? 8 : 10;
			bool needRefresh = false;
			
			for (int i = 0; i < charsCount; i++)
			{
				var pakdata = new CreationCharacterData(packet, client);
				
				// Graveen: changed the following to allow GMs to have special chars in their names (_,-, etc..)
				var nameCheck = new Regex("^[A-Z][a-zA-Z]");
				if (!string.IsNullOrEmpty(pakdata.CharName) && (pakdata.CharName.Length < 3 || !nameCheck.IsMatch(pakdata.CharName)))
				{
					if ((ePrivLevel)client.Account.PrivLevel == ePrivLevel.Player)
					{
						if (ServerProperties.Properties.BAN_HACKERS)
							client.BanAccount(string.Format("Autoban bad CharName '{0}'", pakdata.CharName));

						client.Disconnect();
						return;
					}
				}
					
				switch ((eOperation)pakdata.Operation)
				{
					case eOperation.Delete:
						if (string.IsNullOrEmpty(pakdata.CharName))
						{
							// Deletion in 1.104+ check for removed character.
							needRefresh |= CheckForDeletedCharacter(accountName, client, i);
						}
						break;
					case eOperation.Customize:
						if (!string.IsNullOrEmpty(pakdata.CharName))
						{
							// Candidate for Customizing ?
							var character = client.Account.Characters != null ? client.Account.Characters.FirstOrDefault(ch => ch.Name.Equals(pakdata.CharName, StringComparison.OrdinalIgnoreCase)) : null;
							if (character != null)
								needRefresh |= CheckCharacterForUpdates(pakdata, client, character);
						}
						break;
					case eOperation.Create:
						if (!string.IsNullOrEmpty(pakdata.CharName))
						{
							// Candidate for Creation ?
							var character = client.Account.Characters != null ? client.Account.Characters.FirstOrDefault(ch => ch.Name.Equals(pakdata.CharName, StringComparison.OrdinalIgnoreCase)) : null;
							if (character == null)
								needRefresh |= CreateCharacter(pakdata, client, i);
						}
						break;
					default:
						break;
				}
			}
			
			if(needRefresh)
			{
				client.Out.SendCharacterOverview(currentRealm);
			}
		}
			/// <summary>
			/// Reads up ONE character iteration on the packet stream
			/// </summary>
			/// <param name="packet"></param>
			/// <param name="client"></param>
			public CreationCharacterData(GSPacketIn packet, GameClient client)
			{
				//unk - probably indicates customize or create (these are moved from 1.99 4 added bytes)
				if (client.Version >= GameClient.eClientVersion.Version1104)
					packet.ReadIntLowEndian();

				CharName = packet.ReadString(24);
				CustomMode = packet.ReadByte();
				EyeSize = packet.ReadByte();
				LipSize = packet.ReadByte();
				EyeColor = packet.ReadByte();
				HairColor = packet.ReadByte();
				FaceType = packet.ReadByte();
				HairStyle = packet.ReadByte();
				packet.Skip(3);
				MoodType = packet.ReadByte();
				packet.Skip(8);
				
				Operation = packet.ReadInt();
				var unk = packet.ReadByte();
				
				packet.Skip(24); //Location String
				packet.Skip(24); //Skip class name
				packet.Skip(24); //Skip race name
				
				var level = packet.ReadByte(); //not safe!
				Class = packet.ReadByte();
				Realm = packet.ReadByte();
				
				//The following byte contains
				//1bit=start location ... in ShroudedIsles you can choose ...
				//1bit=first race bit
				//1bit=unknown
				//1bit=gender (0=male, 1=female)
				//4bit=race
				byte startRaceGender = (byte)packet.ReadByte();
				Race = (startRaceGender & 0x0F) + ((startRaceGender & 0x40) >> 2);
				Gender = ((startRaceGender >> 4) & 0x01);
				SIStartLocation = ((startRaceGender >> 7) != 0);

				CreationModel = packet.ReadShortLowEndian();
				Region = packet.ReadByte();
				packet.Skip(1); //TODO second byte of region unused currently
				packet.Skip(4); //TODO Unknown Int / last used?

				Strength = packet.ReadByte();
				Dexterity = packet.ReadByte();
				Constitution = packet.ReadByte();
				Quickness = packet.ReadByte();
				Intelligence = packet.ReadByte();
				Piety = packet.ReadByte();
				Empathy = packet.ReadByte();
				Charisma = packet.ReadByte();

				packet.Skip(40); //TODO equipment
				
				var activeRightSlot = packet.ReadByte(); // 0x9C
				var activeLeftSlot = packet.ReadByte(); // 0x9D
				var siZone = packet.ReadByte(); // 0x9E
				
				// skip 4 bytes added in 1.99
				if (client.Version >= GameClient.eClientVersion.Version199 && client.Version < GameClient.eClientVersion.Version1104)
					packet.Skip(4);
				
				// New constitution must be read before skipping 4 bytes
				NewConstitution = packet.ReadByte(); // 0x9F


			}
		//static int lastZ=int.MinValue;
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			//Tiv: in very rare cases client send 0xA9 packet before sending S<=C 0xE8 player world initialize
			if ((client.Player.ObjectState != GameObject.eObjectState.Active) ||
			    (client.ClientState != GameClient.eClientState.Playing))
				return;

			int environmentTick = Environment.TickCount;
			int packetVersion;
			if (client.Version > GameClient.eClientVersion.Version171)
			{
				packetVersion = 172;
			}
			else
			{
				packetVersion = 168;
			}

			int oldSpeed = client.Player.CurrentSpeed;

			//read the state of the player
			packet.Skip(2); //PID
			ushort data = packet.ReadShort();
			int speed = (data & 0x1FF);

			//			if(!GameServer.ServerRules.IsAllowedDebugMode(client)
			//				&& (speed > client.Player.MaxSpeed + SPEED_TOL))


			if ((data & 0x200) != 0)
				speed = -speed;

			if (client.Player.IsMezzed || client.Player.IsStunned)
			{
				// Nidel: updating client.Player.CurrentSpeed instead of speed
				client.Player.CurrentSpeed = 0;
			}
			else
			{
				client.Player.CurrentSpeed = (short)speed;
			}

			client.Player.IsStrafing = ((data & 0xe000) != 0);

			int realZ = packet.ReadShort();
			ushort xOffsetInZone = packet.ReadShort();
			ushort yOffsetInZone = packet.ReadShort();
			ushort currentZoneID;
			if (packetVersion == 168)
			{
				currentZoneID = (ushort)packet.ReadByte();
				packet.Skip(1); //0x00 padding for zoneID
			}
			else
			{
				currentZoneID = packet.ReadShort();
			}


			//Dinberg - Instance considerations.
			//Now this gets complicated, so listen up! We have told the client a lie when it comes to the zoneID.
			//As a result, every movement update, they are sending a lie back to us. Two liars could get confusing!

			//BUT, the lie we sent has a truth to it - the geometry and layout of the zone. As a result, the zones
			//x and y offsets will still actually be relevant to our current zone. And for the clones to have been
			//created, there must have been a real zone to begin with, of id == instanceZone.SkinID.

			//So, although our client is lying to us, and thinks its in another zone, that zone happens to coincide
			//exactly with the zone we are instancing - and so all the positions still ring true.

			//Philosophically speaking, its like looking in a mirror and saying 'Am I a reflected, or reflector?'
			//What it boils down to has no bearing whatsoever on the result of anything, so long as someone sitting
			//outside of the unvierse knows not to listen to whether you say which you are, and knows the truth to the
			//answer. Then, he need only know what you are doing ;)

			Zone newZone = WorldMgr.GetZone(currentZoneID);
			if (newZone == null)
			{
				if(client.Player==null) return;
				if(!client.Player.TempProperties.getProperty("isbeingbanned",false))
				{
					if (log.IsErrorEnabled)
						log.Error(client.Player.Name + "'s position in unknown zone! => " + currentZoneID);
					GamePlayer player=client.Player;
					player.TempProperties.setProperty("isbeingbanned", true);
					player.MoveToBind();
				}

				return; // TODO: what should we do? player lost in space
			}

			// move to bind if player fell through the floor
			if (realZ == 0)
			{
				client.Player.MoveTo(
					(ushort)client.Player.BindRegion,
					client.Player.BindXpos,
					client.Player.BindYpos,
					(ushort)client.Player.BindZpos,
					(ushort)client.Player.BindHeading
				);
				return;
			}

			int realX = newZone.XOffset + xOffsetInZone;
			int realY = newZone.YOffset + yOffsetInZone;

			bool zoneChange = newZone != client.Player.LastPositionUpdateZone;
			if (zoneChange)
			{
				//If the region changes -> make sure we don't take any falling damage
				if (client.Player.LastPositionUpdateZone != null && newZone.ZoneRegion.ID != client.Player.LastPositionUpdateZone.ZoneRegion.ID)
					client.Player.MaxLastZ = int.MinValue;

				// Update water level and diving flag for the new zone
				client.Out.SendPlayerPositionAndObjectID();
				zoneChange = true;

				/*
				 * "You have entered Burial Tomb."
				 * "Burial Tomb"
				 * "Current area is adjusted for one level 1 player."
				 * "Current area has a 50% instance bonus."
				 */

                string description = newZone.Description;
                string screenDescription = description;

                var translation = client.GetTranslation(newZone) as DBLanguageZone;
                if (translation != null)
                {
                    if (!Util.IsEmpty(translation.Description))
                        description = translation.Description;

                    if (!Util.IsEmpty(translation.ScreenDescription))
                        screenDescription = translation.ScreenDescription;
                }

                client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerPositionUpdateHandler.Entered", description),
				                       eChatType.CT_System, eChatLoc.CL_SystemWindow);
                client.Out.SendMessage(screenDescription, eChatType.CT_ScreenCenterSmaller, eChatLoc.CL_SystemWindow);

				client.Player.LastPositionUpdateZone = newZone;
			}

			int coordsPerSec = 0;
			int jumpDetect = 0;
			int timediff = Environment.TickCount - client.Player.LastPositionUpdateTick;
			int distance = 0;

			if (timediff > 0)
			{
				distance = client.Player.LastPositionUpdatePoint.GetDistanceTo(new Point3D(realX, realY, realZ));
				coordsPerSec = distance * 1000 / timediff;

				if (distance < 100 && client.Player.LastPositionUpdatePoint.Z > 0)
				{
					jumpDetect = realZ - client.Player.LastPositionUpdatePoint.Z;
				}
			}

			#region DEBUG
			#if OUTPUT_DEBUG_INFO
			if (client.Player.LastPositionUpdatePoint.X != 0 && client.Player.LastPositionUpdatePoint.Y != 0)
			{
				log.Debug(client.Player.Name + ": distance = " + distance + ", speed = " + oldSpeed + ",  coords/sec=" + coordsPerSec);
			}
			if (jumpDetect > 0)
			{
				log.Debug(client.Player.Name + ": jumpdetect = " + jumpDetect);
			}
			#endif
			#endregion DEBUG

			client.Player.LastPositionUpdateTick = Environment.TickCount;
			client.Player.LastPositionUpdatePoint.X = realX;
			client.Player.LastPositionUpdatePoint.Y = realY;
			client.Player.LastPositionUpdatePoint.Z = realZ;

			int tolerance = ServerProperties.Properties.CPS_TOLERANCE;

			if (client.Player.Steed != null && client.Player.Steed.MaxSpeed > 0)
			{
				tolerance += client.Player.Steed.MaxSpeed;
			}
			else if (client.Player.MaxSpeed > 0)
			{
				tolerance += client.Player.MaxSpeed;
			}

			if (client.Player.IsJumping)
			{
				coordsPerSec = 0;
				jumpDetect = 0;
				client.Player.IsJumping = false;
			}

			if (client.Player.IsAllowedToFly == false && (coordsPerSec > tolerance || jumpDetect > ServerProperties.Properties.JUMP_TOLERANCE))
			{
				bool isHackDetected = true;

				if (coordsPerSec > tolerance)
				{
					// check to see if CPS time tolerance is exceeded
					int lastCPSTick = client.Player.TempProperties.getProperty<int>(LASTCPSTICK, 0);

					if (environmentTick - lastCPSTick > ServerProperties.Properties.CPS_TIME_TOLERANCE)
					{
						isHackDetected = false;
					}
				}

				if (isHackDetected)
				{
					StringBuilder builder = new StringBuilder();
					builder.Append("MOVEHACK_DETECT");
					builder.Append(": CharName=");
					builder.Append(client.Player.Name);
					builder.Append(" Account=");
					builder.Append(client.Account.Name);
					builder.Append(" IP=");
					builder.Append(client.TcpEndpointAddress);
					builder.Append(" CPS:=");
					builder.Append(coordsPerSec);
					builder.Append(" JT=");
					builder.Append(jumpDetect);
					ChatUtil.SendDebugMessage(client, builder.ToString());

					if (client.Account.PrivLevel == 1)
					{
						GameServer.Instance.LogCheatAction(builder.ToString());

						if (ServerProperties.Properties.ENABLE_MOVEDETECT)
						{
							if (ServerProperties.Properties.BAN_HACKERS && false) // banning disabled until this technique is proven accurate
							{
								DBBannedAccount b = new DBBannedAccount();
								b.Author = "SERVER";
								b.Ip = client.TcpEndpointAddress;
								b.Account = client.Account.Name;
								b.DateBan = DateTime.Now;
								b.Type = "B";
								b.Reason = string.Format("Autoban MOVEHACK:(CPS:{0}, JT:{1}) on player:{2}", coordsPerSec, jumpDetect, client.Player.Name);
								GameServer.Database.AddObject(b);
								GameServer.Database.SaveObject(b);

								string message = "";
								
								message = "You have been auto kicked and banned due to movement hack detection!";
								for (int i = 0; i < 8; i++)
								{
									client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_SystemWindow);
									client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_ChatWindow);
								}

								client.Out.SendPlayerQuit(true);
								client.Player.SaveIntoDatabase();
								client.Player.Quit(true);
							}
							else
							{
								string message = "";
								
								message = "You have been auto kicked due to movement hack detection!";
								for (int i = 0; i < 8; i++)
								{
									client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_SystemWindow);
									client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_ChatWindow);
								}

								client.Out.SendPlayerQuit(true);
								client.Player.SaveIntoDatabase();
								client.Player.Quit(true);
							}
							client.Disconnect();
							return;
						}
					}
				}

				client.Player.TempProperties.setProperty(LASTCPSTICK, environmentTick);
			}

			ushort headingflag = packet.ReadShort();
			client.Player.Heading = (ushort)(headingflag & 0xFFF);
			ushort flyingflag = packet.ReadShort();
			byte flags = (byte)packet.ReadByte();

			if (client.Player.X != realX || client.Player.Y != realY)
			{
				client.Player.TempProperties.setProperty(LASTMOVEMENTTICK, client.Player.CurrentRegion.Time);
			}
			client.Player.X = realX;
			client.Player.Y = realY;
			client.Player.Z = realZ;

			if (zoneChange)
			{
				// update client zone information for waterlevel and diving
				client.Out.SendPlayerPositionAndObjectID();
			}

			// used to predict current position, should be before
			// any calculation (like fall damage)
			client.Player.MovementStartTick = Environment.TickCount;

			// Begin ---------- New Area System -----------
			if (client.Player.CurrentRegion.Time > client.Player.AreaUpdateTick) // check if update is needed
			{
				var oldAreas = client.Player.CurrentAreas;

				// Because we may be in an instance we need to do the area check from the current region
				// rather than relying on the zone which is in the skinned region.  - Tolakram

				var newAreas = client.Player.CurrentRegion.GetAreasOfZone(newZone, client.Player);

				// Check for left areas
				if (oldAreas != null)
				{
					foreach (IArea area in oldAreas)
					{
						if (!newAreas.Contains(area))
						{
							area.OnPlayerLeave(client.Player);
						}
					}
				}
				// Check for entered areas
				foreach (IArea area in newAreas)
				{
					if (oldAreas == null || !oldAreas.Contains(area))
					{
						area.OnPlayerEnter(client.Player);
					}
				}
				// set current areas to new one...
				client.Player.CurrentAreas = newAreas;
				client.Player.AreaUpdateTick = client.Player.CurrentRegion.Time + 2000; // update every 2 seconds
			}
			// End ---------- New Area System -----------


			client.Player.TargetInView = ((flags & 0x10) != 0);
			client.Player.GroundTargetInView = ((flags & 0x08) != 0);
			client.Player.IsTorchLighted = ((flags & 0x80) != 0);
			//7  6  5  4  3  2  1 0
			//15 14 13 12 11 10 9 8
			//                1 1

			const string SHLASTUPDATETICK = "SHPLAYERPOSITION_LASTUPDATETICK";
			const string SHLASTFLY = "SHLASTFLY_STRING";
			const string SHLASTSTATUS = "SHLASTSTATUS_STRING";
			int SHlastTick = client.Player.TempProperties.getProperty<int>(SHLASTUPDATETICK);
			int SHlastFly = client.Player.TempProperties.getProperty<int>(SHLASTFLY);
			int SHlastStatus = client.Player.TempProperties.getProperty<int>(SHLASTSTATUS);
			int SHcount = client.Player.TempProperties.getProperty<int>(SHSPEEDCOUNTER);
			int status = (data & 0x1FF ^ data) >> 8;
			int fly = (flyingflag & 0x1FF ^ flyingflag) >> 8;

			if (client.Player.IsJumping)
			{
				SHcount = 0;
			}

			if (SHlastTick != 0 && SHlastTick != environmentTick)
			{
				if (((SHlastStatus == status || (status & 0x8) == 0)) && ((fly & 0x80) != 0x80) && (SHlastFly == fly || (SHlastFly & 0x10) == (fly & 0x10) || !((((SHlastFly & 0x10) == 0x10) && ((fly & 0x10) == 0x0) && (flyingflag & 0x7FF) > 0))))
				{
					if ((environmentTick - SHlastTick) < 400)
					{
						SHcount++;

						if (SHcount > 1 && client.Account.PrivLevel > 1)
						{
							//Apo: ?? no idea how to name the first parameter for language translation: 1: ??, 2: {detected} ?, 3: {count} ?
							client.Out.SendMessage(string.Format("SH: ({0}) detected: {1}, count {2}", 500 / (environmentTick - SHlastTick), environmentTick - SHlastTick, SHcount), eChatType.CT_Staff, eChatLoc.CL_SystemWindow);
						}

						if (SHcount % 5 == 0)
						{
							StringBuilder builder = new StringBuilder();
							builder.Append("TEST_SH_DETECT[");
							builder.Append(SHcount);
							builder.Append("] (");
							builder.Append(environmentTick - SHlastTick);
							builder.Append("): CharName=");
							builder.Append(client.Player.Name);
							builder.Append(" Account=");
							builder.Append(client.Account.Name);
							builder.Append(" IP=");
							builder.Append(client.TcpEndpointAddress);
							GameServer.Instance.LogCheatAction(builder.ToString());

							if (client.Account.PrivLevel > 1)
							{
								client.Out.SendMessage("SH: Logging SH cheat.", eChatType.CT_Damaged, eChatLoc.CL_SystemWindow);

								if (SHcount >= ServerProperties.Properties.SPEEDHACK_TOLERANCE)
									client.Out.SendMessage("SH: Player would have been banned!", eChatType.CT_Damaged, eChatLoc.CL_SystemWindow);
							}

							if ((client.Account.PrivLevel == 1) && SHcount >= ServerProperties.Properties.SPEEDHACK_TOLERANCE)
							{
								if (ServerProperties.Properties.BAN_HACKERS)
								{
									DBBannedAccount b = new DBBannedAccount();
									b.Author = "SERVER";
									b.Ip = client.TcpEndpointAddress;
									b.Account = client.Account.Name;
									b.DateBan = DateTime.Now;
									b.Type = "B";
									b.Reason = string.Format("Autoban SH:({0},{1}) on player:{2}", SHcount, environmentTick - SHlastTick, client.Player.Name);
									GameServer.Database.AddObject(b);
									GameServer.Database.SaveObject(b);

									string message = "";
									
									message = "You have been auto kicked and banned for speed hacking!";
									for (int i = 0; i < 8; i++)
									{
										client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_SystemWindow);
										client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_ChatWindow);
									}

									client.Out.SendPlayerQuit(true);
									client.Player.SaveIntoDatabase();
									client.Player.Quit(true);
								}
								else
								{
									string message = "";
									
									message = "You have been auto kicked for speed hacking!";
									for (int i = 0; i < 8; i++)
									{
										client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_SystemWindow);
										client.Out.SendMessage(message, eChatType.CT_Help, eChatLoc.CL_ChatWindow);
									}

									client.Out.SendPlayerQuit(true);
									client.Player.SaveIntoDatabase();
									client.Player.Quit(true);
								}
								client.Disconnect();
								return;
							}
						}
					}
					else
					{
						SHcount = 0;
					}

					SHlastTick = environmentTick;
				}
			}
			else
			{
				SHlastTick = environmentTick;
			}

			int state = ((data >> 10) & 7);
			client.Player.IsClimbing = (state == 7);
			client.Player.IsSwimming = (state == 1);
			if (state == 3 && client.Player.TempProperties.getProperty<bool>(GamePlayer.DEBUG_MODE_PROPERTY, false) == false && client.Player.IsAllowedToFly == false) //debugFly on, but player not do /debug on (hack)
			{
				StringBuilder builder = new StringBuilder();
				builder.Append("HACK_FLY");
				builder.Append(": CharName=");
				builder.Append(client.Player.Name);
				builder.Append(" Account=");
				builder.Append(client.Account.Name);
				builder.Append(" IP=");
				builder.Append(client.TcpEndpointAddress);
				GameServer.Instance.LogCheatAction(builder.ToString());
				{
					if (ServerProperties.Properties.BAN_HACKERS)
					{
						DBBannedAccount b = new DBBannedAccount();
						b.Author = "SERVER";
						b.Ip = client.TcpEndpointAddress;
						b.Account = client.Account.Name;
						b.DateBan = DateTime.Now;
						b.Type = "B";
						b.Reason = string.Format("Autoban flying hack: on player:{0}", client.Player.Name);
						GameServer.Database.AddObject(b);
						GameServer.Database.SaveObject(b);
					}
					string message = "";
					
					message = "Client Hack Detected!";
					for (int i = 0; i < 6; i++)
					{
						client.Out.SendMessage(message, eChatType.CT_System, eChatLoc.CL_SystemWindow);
						client.Out.SendMessage(message, eChatType.CT_System, eChatLoc.CL_ChatWindow);
					}
					client.Out.SendPlayerQuit(true);
					client.Disconnect();
					return;
				}
			}

			SHlastFly = fly;
			SHlastStatus = status;
			client.Player.TempProperties.setProperty(SHLASTUPDATETICK, SHlastTick);
			client.Player.TempProperties.setProperty(SHLASTFLY, SHlastFly);
			client.Player.TempProperties.setProperty(SHLASTSTATUS, SHlastStatus);
			client.Player.TempProperties.setProperty(SHSPEEDCOUNTER, SHcount);
			lock (client.Player.LastUniqueLocations)
			{
				GameLocation[] locations = client.Player.LastUniqueLocations;
				GameLocation loc = locations[0];
				if (loc.X != realX || loc.Y != realY || loc.Z != realZ || loc.RegionID != client.Player.CurrentRegionID)
				{
					loc = locations[locations.Length - 1];
					Array.Copy(locations, 0, locations, 1, locations.Length - 1);
					locations[0] = loc;
					loc.X = realX;
					loc.Y = realY;
					loc.Z = realZ;
					loc.Heading = client.Player.Heading;
					loc.RegionID = client.Player.CurrentRegionID;
				}
			}

			//**************//
			//FALLING DAMAGE//
			//**************//
			if (GameServer.ServerRules.CanTakeFallDamage(client.Player) && client.Player.IsSwimming == false)
			{
				int maxLastZ = client.Player.MaxLastZ;
				/* Are we on the ground? */
				if ((flyingflag >> 15) != 0)
				{
					int safeFallLevel = client.Player.GetAbilityLevel(Abilities.SafeFall);
					int fallSpeed = (flyingflag & 0xFFF) - 100 * safeFallLevel; // 0x7FF fall speed and 0x800 bit = fall speed overcaped
					int fallMinSpeed = 400;
					int fallDivide = 6;
					if (client.Version >= GameClient.eClientVersion.Version188)
					{
						fallMinSpeed = 500;
						fallDivide = 15;
					}

                    int fallPercent = Math.Min(99, (fallSpeed - (fallMinSpeed + 1)) / fallDivide);

                    if (fallSpeed > fallMinSpeed)
                    {
                        client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerPositionUpdateHandler.FallingDamage"),
                        eChatType.CT_Damaged, eChatLoc.CL_SystemWindow);
                        client.Player.CalcFallDamage(fallPercent);
                    }

					client.Player.MaxLastZ = client.Player.Z;
				}

				else
				{
					// always set Z if on the ground
					if (flyingflag == 0)
						client.Player.MaxLastZ = client.Player.Z;
					// set Z if in air and higher than old Z
					else if (maxLastZ < client.Player.Z)
						client.Player.MaxLastZ = client.Player.Z;
				}
			}
			//**************//

			byte[] con168 = packet.ToArray();
            con168[0] = (byte)(client.SessionID >> 8);
            con168[1] = (byte)(client.SessionID & 0xff);

            //Riding is set here!
            if (client.Player.Steed != null && client.Player.Steed.ObjectState == GameObject.eObjectState.Active)
			{
				client.Player.Heading = client.Player.Steed.Heading;

				con168[2] = 0x18; // Set ride flag 00011000
				con168[3] = 0; // player speed = 0 while ride
				con168[12] = (byte)(client.Player.Steed.ObjectID >> 8); //heading = steed ID
				con168[13] = (byte)(client.Player.Steed.ObjectID & 0xFF);
				con168[14] = (byte)0;
				con168[15] = (byte)(client.Player.Steed.RiderSlot(client.Player)); // there rider slot this player
			}
			else if (!client.Player.IsAlive)
			{
				con168[2] &= 0xE3; //11100011
				con168[2] |= 0x14; //Set dead flag 00010100
			}
			//diving is set here
			con168[16] &= 0xFB; //11 11 10 11
			if ((con168[16] & 0x02) != 0x00)
			{
				client.Player.IsDiving = true;
				con168[16] |= 0x04;
			}
			else
				client.Player.IsDiving = false;

			con168[16] &= 0xFC; //11 11 11 00 cleared Wireframe & Stealth bits
			if (client.Player.IsWireframe)
			{
				con168[16] |= 0x01;
			}
			//stealth is set here
			if (client.Player.IsStealthed)
			{
				con168[16] |= 0x02;
			}

			con168[17] = (byte)((con168[17] & 0x80) | client.Player.HealthPercent);
			// zone ID has changed in 1.72, fix bytes 11 and 12
			byte[] con172 = (byte[])con168.Clone();
			if (packetVersion == 168)
			{
				// client sent v168 pos update packet, fix 172 version
				con172[10] = 0;
				con172[11] = con168[10];
			}
			else
			{
				// client sent v172 pos update packet, fix 168 version
				con168[10] = con172[11];
				con168[11] = 0;
			}

			GSUDPPacketOut outpak168 = new GSUDPPacketOut(client.Out.GetPacketCode(eServerPackets.PlayerPosition));
			//Now copy the whole content of the packet
			outpak168.Write(con168, 0, 18/*con168.Length*/);
			outpak168.WritePacketLength();

			GSUDPPacketOut outpak172 = new GSUDPPacketOut(client.Out.GetPacketCode(eServerPackets.PlayerPosition));
			//Now copy the whole content of the packet
			outpak172.Write(con172, 0, 18/*con172.Length*/);
			outpak172.WritePacketLength();

			//			byte[] pak168 = outpak168.GetBuffer();
			//			byte[] pak172 = outpak172.GetBuffer();
			//			outpak168 = null;
			//			outpak172 = null;
			GSUDPPacketOut outpak190 = null;


			GSUDPPacketOut outpak1112 = new GSUDPPacketOut(client.Out.GetPacketCode(eServerPackets.PlayerPosition));
			outpak1112.Write(con172, 0, 18/*con172.Length*/);
			outpak1112.WriteByte(client.Player.ManaPercent);
			outpak1112.WriteByte(client.Player.EndurancePercent);
			outpak1112.WriteByte((byte)(client.Player.RPFlag ? 1 : 0));
			outpak1112.WriteByte(0); //outpak1112.WriteByte((con168.Length == 22) ? con168[21] : (byte)0);
			outpak1112.WritePacketLength();


			foreach (GamePlayer player in client.Player.GetPlayersInRadius(WorldMgr.VISIBILITY_DISTANCE))
			{
				if (player == null)
					continue;
				//No position updates for ourselves
				if (player == client.Player)
				{
					// Update Player Cache (Client sending Packet is admitting he's already having it)
					player.Client.GameObjectUpdateArray[new Tuple<ushort, ushort>(client.Player.CurrentRegionID, (ushort)client.Player.ObjectID)] = GameTimer.GetTickCount();
					continue;
				}
				//no position updates in different houses
				if ((client.Player.InHouse || player.InHouse) && player.CurrentHouse != client.Player.CurrentHouse)
					continue;

				if (client.Player.MinotaurRelic != null)
				{
					MinotaurRelic relic = client.Player.MinotaurRelic;
					if (!relic.Playerlist.Contains(player) && player != client.Player)
					{
						relic.Playerlist.Add(player);
						player.Out.SendMinotaurRelicWindow(client.Player, client.Player.MinotaurRelic.Effect, true);
					}
				}

				if (!client.Player.IsStealthed || player.CanDetect(client.Player))
				{
					// Update Player Cache
					player.Client.GameObjectUpdateArray[new Tuple<ushort, ushort>(client.Player.CurrentRegionID, (ushort)client.Player.ObjectID)] = GameTimer.GetTickCount();
					
					//forward the position packet like normal!
					if (player.Client.Version >= GameClient.eClientVersion.Version1112)
					{
						player.Out.SendUDPRaw(outpak1112);
					}
					else if (player.Client.Version >= GameClient.eClientVersion.Version190)
					{
						if (outpak190 == null)
						{
							outpak190 = new GSUDPPacketOut(client.Out.GetPacketCode(eServerPackets.PlayerPosition));
							outpak190.Write(con172, 0, 18/*con172.Length*/);
							outpak190.WriteByte(client.Player.ManaPercent);
							outpak190.WriteByte(client.Player.EndurancePercent);
							outpak190.FillString(client.Player.CharacterClass.Name, 32);
							// roleplay flag, if == 1, show name (RP) with gray color
							if (client.Player.RPFlag)
								outpak190.WriteByte(1);
							else outpak190.WriteByte(0);
							outpak190.WriteByte((con168.Length == 54) ? con168[53] : (byte) 0); // send last byte for 190+ packets
							outpak190.WritePacketLength();
						}
						player.Out.SendUDPRaw(outpak190);
					}
					else if (player.Client.Version >= GameClient.eClientVersion.Version172)
						player.Out.SendUDPRaw(outpak172);
					else
						player.Out.SendUDPRaw(outpak168);
				}
				else
					player.Out.SendObjectDelete(client.Player); //remove the stealthed player from view
			}

			if (client.Player.CharacterClass.ID == (int)eCharacterClass.Warlock)
			{
				//Send Chamber effect
				client.Player.Out.SendWarlockChamberEffect(client.Player);
			}

			//handle closing of windows
			//trade window
			if (client.Player.TradeWindow != null)
			{
				if (client.Player.TradeWindow.Partner != null)
				{
					if (!client.Player.IsWithinRadius(client.Player.TradeWindow.Partner, WorldMgr.GIVE_ITEM_DISTANCE))
						client.Player.TradeWindow.CloseTrade();
				}
			}
		}
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			ushort id		= packet.ReadShort();
			ushort toSlot	= packet.ReadShort();
			ushort fromSlot = packet.ReadShort();
			ushort itemCount= packet.ReadShort();

			//log.Debug("MoveItem, id=" + id + " toSlot=" + toSlot + " fromSlot=" + fromSlot + " itemCount=" + itemCount);
			//ChatUtil.SendDebugMessage(client, "MoveItem, id=" + id.ToString() + " toSlot=" + toSlot.ToString() + " fromSlot=" + fromSlot.ToString() + " itemCount=" + itemCount.ToString());

			//If our slot is > 1000 it is (objectID+1000) of target
			if(toSlot>1000)
			{
				ushort objectID = (ushort)(toSlot-1000);
				GameObject obj = WorldMgr.GetObjectByIDFromRegion(client.Player.CurrentRegionID,objectID);
				if(obj==null || obj.ObjectState!=GameObject.eObjectState.Active)
				{
					client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
					client.Out.SendMessage("Invalid trade target. ("+objectID+")", eChatType.CT_System, eChatLoc.CL_SystemWindow);
					return;
				}

				GamePlayer tradeTarget = obj as GamePlayer;
				// If our target is another player we set the tradetarget
				// trade permissions are done in GamePlayer
				if (tradeTarget != null)
				{
					if(tradeTarget.Client.ClientState != GameClient.eClientState.Playing)
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						client.Out.SendMessage("Can't trade with inactive players.", eChatType.CT_System,  eChatLoc.CL_SystemWindow);
						return;
					}
					if(tradeTarget == client.Player)
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						client.Out.SendMessage("You can't trade with yourself, silly!",eChatType.CT_System,eChatLoc.CL_SystemWindow);
						return;
					}
					if(!GameServer.ServerRules.IsAllowedToTrade(client.Player, tradeTarget, false))
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						return;
					}
				}
				
				// Is the item we want to move in our backpack?
				// we also allow drag'n drop from equipped to blacksmith
				if ((fromSlot >= (ushort)eInventorySlot.FirstBackpack && fromSlot <= (ushort)eInventorySlot.LastBackpack)
				    || (obj is Blacksmith && fromSlot>=(ushort) eInventorySlot.MinEquipable && fromSlot<=(ushort) eInventorySlot.MaxEquipable))
				{
					if (!obj.IsWithinRadius(client.Player, WorldMgr.GIVE_ITEM_DISTANCE))
					{
						if (obj is GamePlayer)
							client.Out.SendMessage(LanguageMgr.GetTranslation(client, "PlayerMoveItemRequestHandler.TooFarAway", client.Player.GetName((GamePlayer)obj)), eChatType.CT_System, eChatLoc.CL_SystemWindow);
						else
							client.Out.SendMessage(LanguageMgr.GetTranslation(client, "PlayerMoveItemRequestHandler.TooFarAway", obj.GetName(0, false)), eChatType.CT_System, eChatLoc.CL_SystemWindow);
						//client.Out.SendMessage("You are too far away to give anything to " + obj.GetName(0, false) + ".", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						client.Out.SendInventorySlotsUpdate(new int[] { fromSlot });
						return;
					}

					InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromSlot);
					if (item == null)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromSlot });
						client.Out.SendMessage("Invalid item (slot# " + fromSlot + ").", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}

					if (obj is GameNPC == false || item.Count == 1)
						client.Player.Notify(GamePlayerEvent.GiveItem, client.Player, new GiveItemEventArgs(client.Player, obj, item));

					//If the item has been removed by the event handlers, return;
					//item = client.Player.Inventory.GetItem((eInventorySlot)fromSlot);
					if (item == null || item.OwnerID == null)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromSlot });
						return;
					}


					if(client.Account.PrivLevel == (uint)ePrivLevel.Player && tradeTarget != null && tradeTarget.Client.Account.PrivLevel != (uint)ePrivLevel.Player)
					{
						if(!item.IsDropable && !(obj is GameNPC && (obj is Blacksmith || obj is Recharger || (obj as GameNPC).CanTradeAnyItem)))
						{
							client.Out.SendInventorySlotsUpdate(new int[] { fromSlot });
							client.Out.SendMessage("You can not remove this item!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
							return;
						}
					}

					if(tradeTarget != null)
					{
						tradeTarget.ReceiveTradeItem(client.Player, item);
						client.Out.SendInventorySlotsUpdate(new int[] { fromSlot });
						return;
					}

					if (obj.ReceiveItem(client.Player, item))
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromSlot });
						return;
					}

					client.Out.SendInventorySlotsUpdate(new int[] { fromSlot });
					return;
				}

				//Is the "item" we want to move money? For Version 1.78+
				if(client.Version >= GameClient.eClientVersion.Version178
				   && fromSlot >= (int)eInventorySlot.Mithril178 && fromSlot <= (int)eInventorySlot.Copper178)
				{
					fromSlot -= eInventorySlot.Mithril178 - eInventorySlot.Mithril;
				}

				//Is the "item" we want to move money?
				if(fromSlot>=(ushort)eInventorySlot.Mithril && fromSlot<=(ushort)eInventorySlot.Copper)
				{
					int[] money=new int[5];
					money[fromSlot-(ushort)eInventorySlot.Mithril]=itemCount;
					long flatmoney = Money.GetMoney(money[0],money[1],money[2],money[3],money[4]);

					if(client.Version >= GameClient.eClientVersion.Version178) // add it back for proper slot update...
						fromSlot += eInventorySlot.Mithril178 - eInventorySlot.Mithril;

					if (!obj.IsWithinRadius(client.Player, WorldMgr.GIVE_ITEM_DISTANCE))
					{
						if (obj is GamePlayer)
							client.Out.SendMessage(LanguageMgr.GetTranslation(client, "PlayerMoveItemRequestHandler.TooFarAway", client.Player.GetName((GamePlayer)obj)), eChatType.CT_System, eChatLoc.CL_SystemWindow);
						else
							client.Out.SendMessage(LanguageMgr.GetTranslation(client, "PlayerMoveItemRequestHandler.TooFarAway", obj.GetName(0, false)), eChatType.CT_System, eChatLoc.CL_SystemWindow);

						//client.Out.SendMessage("You are too far away to give anything to " + obj.GetName(0, false) + ".", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						return;
					}

					if(flatmoney > client.Player.GetCurrentMoney())
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						return;
					}

					client.Player.Notify(GamePlayerEvent.GiveMoney, client.Player, new GiveMoneyEventArgs(client.Player, obj, flatmoney));

					if(tradeTarget!=null)
					{
						tradeTarget.ReceiveTradeMoney(client.Player, flatmoney);
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						return;
					}

					if(obj.ReceiveMoney(client.Player, flatmoney))
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						return;
					}

					client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
					return;
				}

				client.Out.SendInventoryItemsUpdate(null);
				return;
			}

			// House Vaults and GameConsignmentMerchant Merchants deliver the same slot numbers
			if (fromSlot >= (ushort)eInventorySlot.HousingInventory_First &&
			    fromSlot <= (ushort)eInventorySlot.HousingInventory_Last)
			{
				GameVault ghv = client.Player.ActiveVault;
				GameConsignmentMerchant cm = client.Player.ActiveConMerchant;
				

				if (cm != null) // we have an active GameConsignmentMerchant Merchant
				{
					if (ghv != null) //this should never happen
					{
						client.Out.SendInventoryItemsUpdate(null);
						return;
					}
					fromSlot += 1350;
				}
			}
			if (toSlot >= (ushort)eInventorySlot.HousingInventory_First &&
			    toSlot <= (ushort)eInventorySlot.HousingInventory_Last)
			{
				GameVault ghv = client.Player.ActiveVault;
				GameConsignmentMerchant cm = client.Player.ActiveConMerchant;
				if (cm != null) // we have an active GameConsignmentMerchant Merchant
				{
					if (ghv != null) //this should never happen
					{
						client.Out.SendInventoryItemsUpdate(null);
						return;
					}
					toSlot += 1350;
				}
			}

			bool fromHousing = (fromSlot >= (ushort)eInventorySlot.HousingInventory_First && fromSlot <= (ushort)eInventorySlot.HousingInventory_Last);
			bool toHousing = (toSlot >= (ushort)eInventorySlot.HousingInventory_First && toSlot <= (ushort)eInventorySlot.HousingInventory_Last);

			// Move an item from, to or inside a vault.
			if (fromHousing || toHousing)
			{
				GameVault houseVault = client.Player.ActiveVault;
				if (fromSlot >= (ushort)eInventorySlot.FirstBackpack && fromSlot <= (ushort)eInventorySlot.LastBackpack)
				{
					InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromSlot);
					if (!item.IsTradable)
					{
						client.Out.SendMessage("You can not put this Item into a House Vault!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						client.Out.SendInventoryItemsUpdate(null);
						return;
					}
				}

				if (houseVault == null)
				{
					client.Out.SendMessage("You are not actively viewing a vault!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
					client.Out.SendInventoryItemsUpdate(null);

					return;
				}

				if (toHousing && !houseVault.CanAddItems(client.Player))
				{
					client.Out.SendMessage("You don't have permission to add items!",  eChatType.CT_System, eChatLoc.CL_SystemWindow);
					return;
				}

				if (fromHousing && !houseVault.CanRemoveItems(client.Player))
				{
					client.Out.SendMessage("You don't have permission to remove items!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
					return;
				}

				houseVault.MoveItem(client.Player, (eInventorySlot)fromSlot, (eInventorySlot)toSlot);

				return;
			}

			// Move an item from, to or inside a GameConsignmentMerchant Merchant.

			if ((fromSlot >= (ushort)eInventorySlot.Consignment_First &&
			     fromSlot <= (ushort)eInventorySlot.Consignment_Last) ||
			    (toSlot >= (ushort)eInventorySlot.Consignment_First &&
			     toSlot <= (ushort)eInventorySlot.Consignment_Last))
			{
				GameConsignmentMerchant con = client.Player.ActiveConMerchant;
				if (con == null)
				{
					client.Out.SendMessage("You are not actively interacting with a GameConsignmentMerchant Merchant!",
					                       eChatType.CT_System, eChatLoc.CL_SystemWindow);
					client.Out.SendInventoryItemsUpdate(null);
					return;
				}
				if (fromSlot >= (ushort)eInventorySlot.FirstBackpack && fromSlot <= (ushort)eInventorySlot.LastBackpack)
				{
					InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromSlot);
					if (!item.IsTradable)
					{
						client.Out.SendMessage("You can not put this Item into a GameConsignmentMerchant Merchant!",
						                       eChatType.CT_System, eChatLoc.CL_SystemWindow);
						client.Out.SendInventoryItemsUpdate(null);
						return;
					}
				}
				if (toSlot >= (ushort)eInventorySlot.Consignment_First &&
				    toSlot <= (ushort)eInventorySlot.Consignment_Last)
				{
					if (!con.CanMove(client.Player))
					{
						client.Out.SendMessage("You don't have permission to add or move Items!",
						                       eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}
				}
				con.MoveItem(client.Player, client.Player.Inventory, (eInventorySlot)fromSlot, (eInventorySlot)toSlot);
				return;
			}

			if (fromSlot >= (ushort)eInventorySlot.MarketExplorerFirst && toSlot >= (ushort)eInventorySlot.FirstBackpack && toSlot <= (ushort)eInventorySlot.LastBackpack && client.Player.ActiveVault == null) // a possible buy from a market explorer
			{
				if (client.Player.TargetObject == null)
					return;

				if (!(client.Player.TargetObject is MarketExplorer))
					return;

				var list = client.Player.TempProperties.getProperty<List<InventoryItem>>(PlayerMarketSearchRequestHandler.EXPLORER_LIST, null);
				if (list == null)
					return;

				var me = client.Player.TargetObject as MarketExplorer;

				int itemnr = fromSlot - (int)eInventorySlot.MarketExplorerFirst;

				InventoryItem item = list[itemnr];

				me.BuyItem(item, client.Player);
			}
			
			//Do we want to move an item from inventory/vault/quiver into inventory/vault/quiver?
			if (((fromSlot>=(ushort)eInventorySlot.Ground && fromSlot<=(ushort)eInventorySlot.LastBackpack)
			    || (fromSlot >=(ushort)eInventorySlot.FirstVault && fromSlot<=(ushort)eInventorySlot.LastVault)
				|| (fromSlot >=(ushort)eInventorySlot.FirstBagHorse && fromSlot <=(ushort)eInventorySlot.LastBagHorse))
			    &&((toSlot >=(ushort)eInventorySlot.Ground && toSlot<=(ushort)eInventorySlot.LastBackpack)
			    || (toSlot >=(ushort)eInventorySlot.FirstVault && toSlot<=(ushort)eInventorySlot.LastVault)
			    || (toSlot >=(ushort)eInventorySlot.HousingInventory_First && toSlot<=(ushort)eInventorySlot.HousingInventory_Last)
				|| (toSlot >=(ushort)eInventorySlot.FirstBagHorse && toSlot <=(ushort)eInventorySlot.LastBagHorse)))
			{
				//We want to drop the item
				if (toSlot==(ushort)eInventorySlot.Ground)
				{
					InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromSlot);
					if (item == null)
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						client.Out.SendMessage("Invalid item (slot# "+fromSlot+").",eChatType.CT_System,eChatLoc.CL_SystemWindow);
						return;
					}
					if (fromSlot<(ushort)eInventorySlot.FirstBackpack)
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						return;
					}
					if(!item.IsDropable)
					{
						client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
						client.Out.SendMessage("You can not drop this item!",eChatType.CT_System,eChatLoc.CL_SystemWindow);
						return;
					}

					if (client.Player.DropItem((eInventorySlot)fromSlot))
					{
						client.Out.SendMessage("You drop " + item.GetName(0, false) + " on the ground!",eChatType.CT_System,eChatLoc.CL_SystemWindow);
						return;
					}
					client.Out.SendInventoryItemsUpdate(null);
					return;
				}

				//We want to move the item in inventory
				client.Player.Inventory.MoveItem((eInventorySlot)fromSlot, (eInventorySlot)toSlot, itemCount);
				return;
			}
			
			if (((fromSlot>=(ushort)eInventorySlot.Ground && fromSlot<=(ushort)eInventorySlot.LastBackpack)
			    || (fromSlot>=(ushort)eInventorySlot.FirstVault && fromSlot<=(ushort)eInventorySlot.LastVault)
				|| (fromSlot >=(ushort)eInventorySlot.FirstBagHorse && fromSlot <=(ushort)eInventorySlot.LastBagHorse))
			    && ((toSlot==(ushort)eInventorySlot.PlayerPaperDoll || toSlot==(ushort)eInventorySlot.NewPlayerPaperDoll)
				|| (toSlot>=(ushort)eInventorySlot.Ground && toSlot<=(ushort)eInventorySlot.LastBackpack)
			    || (toSlot>=(ushort)eInventorySlot.FirstVault && toSlot<=(ushort)eInventorySlot.LastVault)
				|| (toSlot >=(ushort)eInventorySlot.FirstBagHorse && toSlot <=(ushort)eInventorySlot.LastBagHorse)))
			{
				InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromSlot);
				if(item==null) return;

				toSlot=0;
				if(item.Item_Type >= (int)eInventorySlot.MinEquipable && item.Item_Type <= (int)eInventorySlot.MaxEquipable)
					toSlot = (ushort)item.Item_Type;
				if (toSlot==0)
				{
					client.Out.SendInventorySlotsUpdate(new int[] {fromSlot});
					return;
				}
				if( toSlot == (int)eInventorySlot.LeftBracer || toSlot == (int)eInventorySlot.RightBracer)
				{
					if(client.Player.Inventory.GetItem(eInventorySlot.LeftBracer) == null)
						toSlot = (int)eInventorySlot.LeftBracer;
					else
						toSlot = (int)eInventorySlot.RightBracer;
				}

				if( toSlot == (int)eInventorySlot.LeftRing || toSlot == (int)eInventorySlot.RightRing)
				{
					if(client.Player.Inventory.GetItem(eInventorySlot.LeftRing) == null)
						toSlot = (int)eInventorySlot.LeftRing;
					else
						toSlot = (int)eInventorySlot.RightRing;
				}

				client.Player.Inventory.MoveItem((eInventorySlot)fromSlot,(eInventorySlot)toSlot, itemCount);
				return;
			}
			client.Out.SendInventoryItemsUpdate(null);
		}
		private int CheckCharacterForUpdates(GameClient client, GSPacketIn packet, DOLCharacters character, string charName, byte customizationMode)
		{
			int newModel = character.CurrentModel;

			if (customizationMode == 1 || customizationMode == 2 || customizationMode == 3)
			{
				bool flagChangedStats = false;
				character.EyeSize = (byte)packet.ReadByte();
				character.LipSize = (byte)packet.ReadByte();
				character.EyeColor = (byte)packet.ReadByte();
				character.HairColor = (byte)packet.ReadByte();
				character.FaceType = (byte)packet.ReadByte();
				character.HairStyle = (byte)packet.ReadByte();
				packet.Skip(3);
				character.MoodType = (byte)packet.ReadByte();
				packet.Skip(89); // Skip location string, race string, classe string, level ,class ,realm and startRaceGender
				newModel = packet.ReadShortLowEndian(); //read new model

				if (customizationMode != 3 && client.Version >= GameClient.eClientVersion.Version189)
				{
					packet.Skip(6); // Region ID + character Internal ID
					int[] stats = new int[8];
					stats[0] = (byte)packet.ReadByte(); // Strength
					stats[2] = (byte)packet.ReadByte(); // Dexterity
					stats[1] = (byte)packet.ReadByte(); // Constitution
					stats[3] = (byte)packet.ReadByte(); // Quickness
					stats[4] = (byte)packet.ReadByte(); // Intelligence
					stats[5] = (byte)packet.ReadByte(); // Piety
					stats[6] = (byte)packet.ReadByte(); // Empathy
					stats[7] = (byte)packet.ReadByte(); // Charisma

					packet.Skip(43);// armor models/armor color/weapon models/active weapon slots/siZone
					if (client.Version >= GameClient.eClientVersion.Version199)
					{
						// skip 4 bytes added in 1.99
						packet.Skip(4);
					}

					// what is this?
					byte newConstitution = (byte)packet.ReadByte();
					if (newConstitution > 0 && newConstitution < 255) // added 255 check, still not sure why this is here - tolakram
						stats[1] = newConstitution;


					flagChangedStats |= stats[0] != character.Strength;
					flagChangedStats |= stats[1] != character.Constitution;
					flagChangedStats |= stats[2] != character.Dexterity;
					flagChangedStats |= stats[3] != character.Quickness;
					flagChangedStats |= stats[4] != character.Intelligence;
					flagChangedStats |= stats[5] != character.Piety;
					flagChangedStats |= stats[6] != character.Empathy;
					flagChangedStats |= stats[7] != character.Charisma;

					//
					// !! Stat changes disabled by Tolakram until someone figures out why this can create invalid stats !!
					//
					flagChangedStats = false;

					if (flagChangedStats)
					{
						ICharacterClass charClass = ScriptMgr.FindCharacterClass(character.Class);

						if (charClass != null)
						{
							int points = 0;
							int[] leveledStats = new int[8];
							int[] raceStats = new int[8];
							bool valid = true;
							for (int j = 0; j < 8; j++)
							{
								eStat stat = (eStat)ValidateCharacter.eStatIndex[j];
								raceStats[j] = ValidateCharacter.STARTING_STATS[character.Race][j];
								for (int level = character.Level; level > 5; level--)
								{
									if (charClass.PrimaryStat != eStat.UNDEFINED && charClass.PrimaryStat == stat)
									{
										leveledStats[j]++;
									}
									if (charClass.SecondaryStat != eStat.UNDEFINED && charClass.SecondaryStat == stat)
									{
										if ((level - 6) % 2 == 0)
											leveledStats[j]++;
									}
									if (charClass.TertiaryStat != eStat.UNDEFINED && charClass.TertiaryStat == stat)
									{
										if ((level - 6) % 3 == 0)
											leveledStats[j]++;
									}
								}

								int result = stats[j] - leveledStats[j] - raceStats[j];

								bool validBeginStat = result >= 0;
								int pointsUsed = result;
								string statCategory = "";

								if (charClass.PrimaryStat != eStat.UNDEFINED && charClass.PrimaryStat == stat)
									statCategory = "1)";
								if (charClass.SecondaryStat != eStat.UNDEFINED && charClass.SecondaryStat == stat)
									statCategory = "2)";
								if (charClass.TertiaryStat != eStat.UNDEFINED && charClass.TertiaryStat == stat)
									statCategory = "3)";

								pointsUsed += Math.Max(0, result - 10); //two points used
								pointsUsed += Math.Max(0, result - 15); //three points used

								log.Info(string.Format("{0,-2} {1,-3}:{2, 3} {3,3} {4,3} {5,3} {6,2} {7} {8}",
								                       statCategory,
								                       (stat == eStat.STR) ? "STR" : stat.ToString(),
								                       stats[j],
								                       leveledStats[j],
								                       stats[j] - leveledStats[j],
								                       raceStats[j],
								                       result,
								                       pointsUsed,
								                       (validBeginStat) ? "" : "Not Valid"));

								points += pointsUsed;

								if (!validBeginStat)
								{
									valid = false;
									if (client.Account.PrivLevel == 1)
									{
										if (ServerProperties.Properties.BAN_HACKERS)
										{
											DBBannedAccount b = new DBBannedAccount();
											b.Author = "SERVER";
											b.Ip = client.TcpEndpointAddress;
											b.Account = client.Account.Name;
											b.DateBan = DateTime.Now;
											b.Type = "B";
											b.Reason = String.Format("Autoban Hack char update : Wrong {0} point:{1}", (stat == eStat.STR) ? "STR" : stat.ToString(), result);
											GameServer.Database.AddObject(b);
											GameServer.Database.SaveObject(b);
											GameServer.Instance.LogCheatAction(b.Reason + ". Account: " + b.Account);
										}

										client.Disconnect();
										return 1;
									}
								}
							}

							if (valid)
							{
								character.Strength = (byte)stats[0];
								character.Constitution = (byte)stats[1];
								character.Dexterity = (byte)stats[2];
								character.Quickness = (byte)stats[3];
								character.Intelligence = (byte)stats[4];
								character.Piety = (byte)stats[5];
								character.Empathy = (byte)stats[6];
								character.Charisma = (byte)stats[7];

								DOLCharacters[] chars = client.Account.Characters;

								for (int z = 0; z < chars.Length; z++)
								{
									if (chars[z].Name != character.Name) continue;

									//Log.Error(string.Format("found activePlayer:[{0}] {1} {2}", client.ActiveCharIndex, client.Player.Name, character.Name));

									if (log.IsInfoEnabled)
										log.Info(String.Format("Character {0} updated in cache!\n", charName));

									if (client.Player != null)
									{
										client.Player.DBCharacter.Strength = (byte)stats[0];
										client.Player.DBCharacter.Constitution = (byte)stats[1];
										client.Player.DBCharacter.Dexterity = (byte)stats[2];
										client.Player.DBCharacter.Quickness = (byte)stats[3];
										client.Player.DBCharacter.Intelligence = (byte)stats[4];
										client.Player.DBCharacter.Piety = (byte)stats[5];
										client.Player.DBCharacter.Empathy = (byte)stats[6];
										client.Player.DBCharacter.Charisma = (byte)stats[7];
									}

									client.Account.Characters[z].Strength = (byte)stats[0];
									client.Account.Characters[z].Constitution = (byte)stats[1];
									client.Account.Characters[z].Dexterity = (byte)stats[2];
									client.Account.Characters[z].Quickness = (byte)stats[3];
									client.Account.Characters[z].Intelligence = (byte)stats[4];
									client.Account.Characters[z].Piety = (byte)stats[5];
									client.Account.Characters[z].Empathy = (byte)stats[6];
									client.Account.Characters[z].Charisma = (byte)stats[7];
								}
							}
						}
						else
						{
							if (log.IsErrorEnabled)
								log.Error("No CharacterClass with ID " + character.Class + " found");
						}
					}
				}
				else
				{
					packet.Skip(58); // skip all other things
					if (client.Version >= GameClient.eClientVersion.Version199)
					{
						// skip 4 bytes added in 1.99
						packet.Skip(4);
					}
				}

				if (customizationMode == 2) // change player customization
				{
					if (client.Account.PrivLevel == 1 && ((newModel >> 11) & 3) == 0) // Player size must be > 0 (from 1 to 3)
					{
						DBBannedAccount b = new DBBannedAccount();
						b.Author = "SERVER";
						b.Ip = client.TcpEndpointAddress;
						b.Account = client.Account.Name;
						b.DateBan = DateTime.Now;
						b.Type = "B";
						b.Reason = String.Format("Autoban Hack char update : zero character size in model:{0}", newModel);
						GameServer.Database.AddObject(b);
						GameServer.Database.SaveObject(b);
						GameServer.Instance.LogCheatAction(b.Reason + ". Account: " + b.Account);
						client.Disconnect();
						return 1;
					}

					if ((ushort)newModel != character.CreationModel)
					{
						character.CurrentModel = newModel;
					}

					character.CustomisationStep = 2; // disable config button

					GameServer.Database.SaveObject(character);

					if (log.IsInfoEnabled)
						log.Info(String.Format("Character {0} face proprieties configured by account {1}!\n", charName, client.Account.Name));
				}
				else if (customizationMode == 3) //auto config -- seems someone thinks this is not possible?
				{
					character.CustomisationStep = 3; // enable config button to player

					GameServer.Database.SaveObject(character);

					//if (log.IsInfoEnabled)
					//	log.Info(String.Format("Character {0} face proprieties auto updated!\n", charName));
				}
				else if (customizationMode == 1 && flagChangedStats) //changed stat only for 1.89+
				{
					GameServer.Database.SaveObject(character);

					if (log.IsInfoEnabled)
						log.Info(String.Format("Character {0} stat updated!\n", charName));
				}
			}

			return 1;
		}
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			string accountName = packet.ReadString(24);

			log.Debug("CharacterCreateRequestHandler for account " + accountName + " using version " + client.Version);

			if (!accountName.StartsWith(client.Account.Name))// TODO more correctly check, client send accountName as account-S, -N, -H (if it not fit in 20, then only account)
			{
				if (ServerProperties.Properties.BAN_HACKERS)
				{
					DBBannedAccount b = new DBBannedAccount();
					b.Author = "SERVER";
					b.Ip = client.TcpEndpointAddress;
					b.Account = client.Account.Name;
					b.DateBan = DateTime.Now;
					b.Type = "B";
					b.Reason = String.Format("Autoban wrong Account '{0}'", GameServer.Database.Escape(accountName));
					GameServer.Database.AddObject(b);
					GameServer.Database.SaveObject(b);
					GameServer.Instance.LogCheatAction(b.Reason + ". Account: " + b.Account);
				}

				client.Disconnect();
				return;
			}

			if (client.Version >= GameClient.eClientVersion.Version1104)
			{
				packet.ReadIntLowEndian(); //unk - probably indicates customize or create
			}

			int charsCount = client.Version < GameClient.eClientVersion.Version173 ? 8 : 10;
			for (int i = 0; i < charsCount; i++)
			{
				string charName = packet.ReadString(24);

				//log.DebugFormat("Character[{0}] = {1}", i, charName);

				if (charName.Length == 0)
				{
					// 1.104+  if character is not in list but is in DB then delete the character
					if (client.Version >= GameClient.eClientVersion.Version1104)
					{
						CheckForDeletedCharacter(accountName, client, i);
					}

					//If the charname is empty, skip the other bytes
					packet.Skip(160);
					if (client.Version >= GameClient.eClientVersion.Version199)
					{
						// skip 4 bytes added in 1.99
						packet.Skip(4);
					}
				}
				else
				{
					// Graveen: changed the following to allow GMs to have special chars in their names (_,-, etc..)
					Regex nameCheck = new Regex("^[A-Z][a-zA-Z]");
					if (charName.Length < 3 || !nameCheck.IsMatch(charName))
					{
						if (client.Account.PrivLevel == 1)
						{
							if (ServerProperties.Properties.BAN_HACKERS)
							{
								DBBannedAccount b = new DBBannedAccount();
								b.Author = "SERVER";
								b.Ip = client.TcpEndpointAddress;
								b.Account = client.Account.Name;
								b.DateBan = DateTime.Now;
								b.Type = "B";
								b.Reason = String.Format("Autoban bad CharName '{0}'", GameServer.Database.Escape(charName));
								GameServer.Database.AddObject(b);
								GameServer.Database.SaveObject(b);
								GameServer.Instance.LogCheatAction(b.Reason + ". Account: " + b.Account);
							}

							client.Disconnect();
							return;
						}
					}

					String select = String.Format("Name = '{0}'", GameServer.Database.Escape(charName));
					DOLCharacters character = GameServer.Database.SelectObject<DOLCharacters>(select);
					if (character != null)
					{
						if (character.AccountName != client.Account.Name)
						{
							if (Properties.BAN_HACKERS == true)
							{
								DBBannedAccount b = new DBBannedAccount();
								b.Author = "SERVER";
								b.Ip = client.TcpEndpointAddress;
								b.Account = client.Account.Name;
								b.DateBan = DateTime.Now;
								b.Type = "B";
								b.Reason = String.Format("Autoban CharName '{0}' on wrong Account '{1}'", GameServer.Database.Escape(charName), GameServer.Database.Escape(client.Account.Name));
								GameServer.Database.AddObject(b);
								GameServer.Database.SaveObject(b);
								GameServer.Instance.LogCheatAction(string.Format(b.Reason + ". Client Account: {0}, DB Account: {1}", client.Account.Name, character.AccountName));
							}

							client.Disconnect();
							return;
						}

						byte customizationMode = (byte)packet.ReadByte();

						// log.DebugFormat("CustomizationMode = {0} for charName {1}", customizationMode, charName);

						// check for update to existing character
						CheckCharacterForUpdates(client, packet, character, charName, customizationMode);
					}
					else
					{
						// create new character and return
						CreateCharacter(client, packet, charName, i);
					}
				}
			}
		}
		private void CreateCharacter(GameClient client, GSPacketIn packet, string charName, int accountSlot)
		{
			Account account = client.Account;
			DOLCharacters ch = new DOLCharacters();
			ch.AccountName = account.Name;
			ch.Name = charName;

			if (packet.ReadByte() == 0x01)
			{
				ch.EyeSize = (byte)packet.ReadByte();
				ch.LipSize = (byte)packet.ReadByte();
				ch.EyeColor = (byte)packet.ReadByte();
				ch.HairColor = (byte)packet.ReadByte();
				ch.FaceType = (byte)packet.ReadByte();
				ch.HairStyle = (byte)packet.ReadByte();
				packet.Skip(3);
				ch.MoodType = (byte)packet.ReadByte();
				ch.CustomisationStep = 2; // disable config button
				packet.Skip(13);
				log.Debug("Disable Config Button");
			}
			else
			{
				packet.Skip(23);
			}

			packet.Skip(24); //Location String
			ch.LastName = "";
			ch.GuildID = "";
			packet.Skip(24); //Skip class name
			packet.Skip(24); //Skip race name
			ch.Level = packet.ReadByte(); //not safe!
			ch.Level = 1;
			ch.Class = packet.ReadByte();
			if (ServerProperties.Properties.START_AS_BASE_CLASS)
			{
				ch.Class = RevertClass(ch);
			}
			ch.Realm = packet.ReadByte();

			if (log.IsDebugEnabled)
				log.Debug("Creation " + client.Version + " character, class:" + ch.Class + ", realm:" + ch.Realm);

			// Is class disabled ?
			int occurences = 0;
			List<string> disabled_classes = Properties.DISABLED_CLASSES.SplitCSV(true);
			occurences = (from j in disabled_classes
			              where j == ch.Class.ToString()
			              select j).Count();

			if (occurences > 0 && (ePrivLevel)client.Account.PrivLevel == ePrivLevel.Player)
			{
				log.Debug("Client " + client.Account.Name + " tried to create a disabled classe: " + (eCharacterClass)ch.Class);
				client.Out.SendCharacterOverview((eRealm)ch.Realm);
			    return;
			}

			if (client.Version >= GameClient.eClientVersion.Version193)
			{
				ValidateCharacter.init_post193_tables();
			}
			else
			{
				ValidateCharacter.init_pre193_tables();
			}

			if (!Enum.IsDefined(typeof(eCharacterClass), (eCharacterClass)ch.Class))
			{
				log.Error(client.Account.Name + " tried to create a character with wrong class ID: " + ch.Class + ", realm:" + ch.Realm);
				if (ServerProperties.Properties.BAN_HACKERS)
				{
					DBBannedAccount b = new DBBannedAccount();
					b.Author = "SERVER";
					b.Ip = client.TcpEndpointAddress;
					b.Account = client.Account.Name;
					b.DateBan = DateTime.Now;
					b.Type = "B";
					b.Reason = string.Format("Autoban character create class: id:{0} realm:{1} name:{2} account:{3}", ch.Class, ch.Realm, ch.Name, account.Name);
					GameServer.Database.AddObject(b);
					GameServer.Database.SaveObject(b);
					GameServer.Instance.LogCheatAction(b.Reason + ". Account: " + b.Account);
					client.Disconnect();
				}
			    return;
			}

			ch.AccountSlot = accountSlot + ch.Realm * 100;

			//The following byte contains
			//1bit=start location ... in ShroudedIsles you can choose ...
			//1bit=first race bit
			//1bit=unknown
			//1bit=gender (0=male, 1=female)
			//4bit=race
			byte startRaceGender = (byte)packet.ReadByte();

			ch.Race = (startRaceGender & 0x0F) + ((startRaceGender & 0x40) >> 2);

			List<string> disabled_races = new List<string>(Properties.DISABLED_RACES.SplitCSV(true));
			occurences = (from j in disabled_races
			              where j == ch.Race.ToString()
			              select j).Count();
			if (occurences > 0 && (ePrivLevel)client.Account.PrivLevel == ePrivLevel.Player)
			{
				log.Debug("Client " + client.Account.Name + " tried to create a disabled race: " + (eRace)ch.Race);
				client.Out.SendCharacterOverview((eRealm)ch.Realm);
			    return;
			}

			ch.Gender = ((startRaceGender >> 4) & 0x01);

			bool siStartLocation = ((startRaceGender >> 7) != 0);

			ch.CreationModel = packet.ReadShortLowEndian();
			ch.CurrentModel = ch.CreationModel;
			ch.Region = packet.ReadByte();
			packet.Skip(1); //TODO second byte of region unused currently
			packet.Skip(4); //TODO Unknown Int / last used?

			ch.Strength = (byte)packet.ReadByte();
			ch.Dexterity = (byte)packet.ReadByte();
			ch.Constitution = (byte)packet.ReadByte();
			ch.Quickness = (byte)packet.ReadByte();
			ch.Intelligence = (byte)packet.ReadByte();
			ch.Piety = (byte)packet.ReadByte();
			ch.Empathy = (byte)packet.ReadByte();
			ch.Charisma = (byte)packet.ReadByte();

			packet.Skip(44); //TODO equipment

			if (client.Version >= GameClient.eClientVersion.Version199)
			{
				// skip 4 bytes added in 1.99
				packet.Skip(4);
			}

			// log.DebugFormat("STR {0}, CON {1}, DEX {2}, QUI {3}, INT {4}, PIE {5}, EMP {6}, CHA {7}", ch.Strength, ch.Constitution, ch.Dexterity, ch.Quickness, ch.Intelligence, ch.Piety, ch.Empathy, ch.Charisma);

			// check if client tried to create invalid char
			if (!ValidateCharacter.IsCharacterValid(ch))
			{
				if (log.IsWarnEnabled)
				{
					log.Warn(ch.AccountName + " tried to create invalid character:" +
							 "\nchar name=" + ch.Name + ", gender=" + ch.Gender + ", race=" + ch.Race + ", realm=" + ch.Realm + ", class=" + ch.Class + ", region=" + ch.Region +
							 "\nstr=" + ch.Strength + ", con=" + ch.Constitution + ", dex=" + ch.Dexterity + ", qui=" + ch.Quickness + ", int=" + ch.Intelligence + ", pie=" + ch.Piety + ", emp=" + ch.Empathy + ", chr=" + ch.Charisma);
				}

				// This is not live like but unfortunately we are missing code / packet support to stay on character create screen if something is invalid
				client.Out.SendCharacterOverview((eRealm)ch.Realm);
			    return;
			}

			ch.CreationDate = DateTime.Now;

			ch.Endurance = 100;
			ch.MaxEndurance = 100;
			ch.Concentration = 100;
			ch.MaxSpeed = GamePlayer.PLAYER_BASE_SPEED;

			#region Starting Locations

			//if the server property for disable tutorial is set, we load in the classic starting locations
			if (ch.Region == 27 && ServerProperties.Properties.DISABLE_TUTORIAL)
			{
				switch (ch.Realm)
				{
						case 1: ch.Region = 1; break;
						case 2: ch.Region = 100; break;
						case 3: ch.Region = 200; break;
				}
			}

			ch.Xpos = 505603;
			ch.Ypos = 494709;
			ch.Zpos = 2463;
			ch.Direction = 5947;

			if (ch.Region == 51 && ch.Realm == 1)//Albion ShroudedIsles start point (I hope)
			{
				ch.Xpos = 526252;
				ch.Ypos = 542415;
				ch.Zpos = 3165;
				ch.Direction = 5286;
			}
			if (ch.Region != 51 && ch.Realm == 1)//Albion start point (Church outside Camelot/humberton)
			{
				ch.Xpos = 505603;
				ch.Ypos = 494709;
				ch.Zpos = 2463;
				ch.Direction = 5947;
				//ch.Region = 1;
				//DOLConsole.WriteLine(String.Format("Character ClassName:"+ch.ClassName+" created!"));
				//DOLConsole.WriteLine(String.Format("Character RaceName:"+ch.RaceName+" created!"));
			}
			if (ch.Region == 151 && ch.Realm == 2)//Midgard ShroudedIsles start point
			{
				ch.Xpos = 293720;
				ch.Ypos = 356408;
				ch.Zpos = 3488;
				ch.Direction = 6670;
			}
			if (ch.Region != 151 && ch.Realm == 2)//Midgard start point (Fort Atla)
			{
				ch.Xpos = 749103;
				ch.Ypos = 815835;
				ch.Zpos = 4408;
				ch.Direction = 7915;
				//ch.Region = 100;
				//DOLConsole.WriteLine(String.Format("Character ClassName:"+ch.ClassName+" created!"));
				//DOLConsole.WriteLine(String.Format("Character RaceName:"+ch.RaceName+" created!"));
			}
			if (ch.Region == 181 && ch.Realm == 3)//Hibernia ShroudedIsles start point
			{
				ch.Xpos = 426483;
				ch.Ypos = 440626;
				ch.Zpos = 5952;
				ch.Direction = 2403;
			}
			if (ch.Region != 181 && ch.Realm == 3)//Hibernia start point (Mag Mel)
			{
				ch.Xpos = 345900;
				ch.Ypos = 490867;
				ch.Zpos = 5200;
				ch.Direction = 4826;
				//ch.Region = 200;
				//DOLConsole.WriteLine(String.Format("Character ClassName:"+ch.ClassName+" created!"));
				//DOLConsole.WriteLine(String.Format("Character RaceName:"+ch.RaceName+" created!"));
			}

			// chars are bound on creation
			ch.BindRegion = ch.Region;
			ch.BindHeading = ch.Direction;
			ch.BindXpos = ch.Xpos;
			ch.BindYpos = ch.Ypos;
			ch.BindZpos = ch.Zpos;

			#endregion Starting Locations

			#region starting guilds

			if (account.PrivLevel == 1 && Properties.STARTING_GUILD)
			{
				switch (ch.Realm)
				{
					case 1:
						switch (ServerProperties.Properties.SERV_LANGUAGE)
						{
							case "EN":
								ch.GuildID = GuildMgr.GuildNameToGuildID("Clan Cotswold");
								break;
							case "DE":
								ch.GuildID = GuildMgr.GuildNameToGuildID("Klan Cotswold");
								break;
							default:
								ch.GuildID = GuildMgr.GuildNameToGuildID("Clan Cotswold");
								break;
						}
						break;
					case 2:
						switch (ServerProperties.Properties.SERV_LANGUAGE)
						{
							case "EN":
								ch.GuildID = GuildMgr.GuildNameToGuildID("Mularn Protectors");
								break;
							case "DE":
								ch.GuildID = GuildMgr.GuildNameToGuildID("Beschützer von Mularn");
								break;
							default:
								ch.GuildID = GuildMgr.GuildNameToGuildID("Mularn Protectors");
								break;
						}
						break;
					case 3:
						switch (ServerProperties.Properties.SERV_LANGUAGE)
						{
							case "EN":
								ch.GuildID = GuildMgr.GuildNameToGuildID("Tir na Nog Adventurers");
								break;
							case "DE":
								ch.GuildID = GuildMgr.GuildNameToGuildID("Tir na Nog-Abenteurer");
								break;
							default:
								ch.GuildID = GuildMgr.GuildNameToGuildID("Tir na Nog Adventurers");
								break;
						}
						break;
						default: break;
				}

				if (ch.GuildID != "")
					ch.GuildRank = 8;
			}

			#endregion Starting Guilds

			if (Properties.STARTING_BPS > 0)
				ch.BountyPoints = Properties.STARTING_BPS;
			
			if (Properties.STARTING_MONEY > 0)
			{
				long value = Properties.STARTING_MONEY;
				ch.Copper = Money.GetCopper(value);
				ch.Silver = Money.GetSilver(value);
				ch.Gold = Money.GetGold(value);
				ch.Platinum = Money.GetPlatinum(value);
			}

			if (Properties.STARTING_REALM_LEVEL > 0)
			{
				int realmLevel = Properties.STARTING_REALM_LEVEL;
				long rpamount = 0;
				if (realmLevel < GamePlayer.REALMPOINTS_FOR_LEVEL.Length)
					rpamount = GamePlayer.REALMPOINTS_FOR_LEVEL[realmLevel];

				// thanks to Linulo from http://daoc.foren.4players.de/viewtopic.php?t=40839&postdays=0&postorder=asc&start=0
				if (rpamount == 0)
					rpamount = (long)(25.0 / 3.0 * (realmLevel * realmLevel * realmLevel) - 25.0 / 2.0 * (realmLevel * realmLevel) + 25.0 / 6.0 * realmLevel);

				ch.RealmPoints = rpamount;
				ch.RealmLevel = realmLevel;
				ch.RealmSpecialtyPoints = realmLevel;
			}

			ch.RespecAmountRealmSkill += 2;

			SetBasicCraftingForNewCharacter(ch);

			//Save the character in the database
			GameServer.Database.AddObject(ch);
			//Fire the character creation event
			GameEventMgr.Notify(DatabaseEvent.CharacterCreated, null, new CharacterEventArgs(ch, client));
			//add equipment
			StartupEquipment.AddEquipment(ch);
			//write changes
			GameServer.Database.SaveObject(ch);

			// Log creation
			AuditMgr.AddAuditEntry(client, AuditType.Account, AuditSubtype.CharacterCreate, "", charName);

			client.Account.Characters = null;

			if (log.IsInfoEnabled)
				log.Info(String.Format("Character {0} created!", charName));

			GameServer.Database.FillObjectRelations(client.Account);
			client.Out.SendCharacterOverview((eRealm)ch.Realm);

		    return;
		}
Пример #14
0
		public void HandlePacket(GameClient client, GSPacketIn packet)
		{
			if (client.Player == null)
				return;

			ushort id = packet.ReadShort();
			ushort toClientSlot = packet.ReadShort();
			ushort fromClientSlot = packet.ReadShort();
			ushort itemCount = packet.ReadShort();

			//ChatUtil.SendDebugMessage(client, "GM: MoveItem; id=" + id.ToString() + " client fromSlot=" + fromClientSlot.ToString() + " client toSlot=" + toClientSlot.ToString() + " itemCount=" + itemCount.ToString());

			// If our toSlot is > 1000 then target is a game object (not a window) with an ObjectID of toSlot - 1000

			if (toClientSlot > 1000)
			{
				ushort objectID = (ushort)(toClientSlot - 1000);
				GameObject obj = WorldMgr.GetObjectByIDFromRegion(client.Player.CurrentRegionID, objectID);
				if (obj == null || obj.ObjectState != GameObject.eObjectState.Active)
				{
					client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
					client.Out.SendMessage("Invalid trade target. (" + objectID + ")", eChatType.CT_System, eChatLoc.CL_SystemWindow);
					return;
				}

				GamePlayer tradeTarget = obj as GamePlayer;
				// If our target is another player we set the tradetarget
				// trade permissions are done in GamePlayer
				if (tradeTarget != null)
				{
					if (tradeTarget.Client.ClientState != GameClient.eClientState.Playing)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						client.Out.SendMessage("Can't trade with inactive players.", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}
					if (tradeTarget == client.Player)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						client.Out.SendMessage("You can't trade with yourself, silly!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}
					if (!GameServer.ServerRules.IsAllowedToTrade(client.Player, tradeTarget, false))
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}
				}

				// Is the item we want to move in our backpack?
				// we also allow drag'n drop from equipped to blacksmith
				if ((fromClientSlot >= (ushort)eInventorySlot.FirstBackpack && 
					 fromClientSlot <= (ushort)eInventorySlot.LastBackpack) || 
					(obj is Blacksmith && 
					 fromClientSlot >= (ushort)eInventorySlot.MinEquipable && 
					 fromClientSlot <= (ushort)eInventorySlot.MaxEquipable))
				{
					if (!obj.IsWithinRadius(client.Player, WorldMgr.GIVE_ITEM_DISTANCE))
					{
						// show too far away message
						if (obj is GamePlayer)
						{
							client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerMoveItemRequestHandler.TooFarAway", client.Player.GetName((GamePlayer)obj)), eChatType.CT_System, eChatLoc.CL_SystemWindow);
						}
						else
						{
							client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerMoveItemRequestHandler.TooFarAway", obj.GetName(0, false)), eChatType.CT_System, eChatLoc.CL_SystemWindow);
						}

						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromClientSlot);
					if (item == null)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						client.Out.SendMessage("Null item (client slot# " + fromClientSlot + ").", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}

					if (obj is GameNPC == false || item.Count == 1)
					{
						// see if any event handlers will handle this move
						client.Player.Notify(GamePlayerEvent.GiveItem, client.Player, new GiveItemEventArgs(client.Player, obj, item));
					}

					//If the item has been removed by the event handlers, return;
					if (item == null || item.OwnerID == null)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					// if a player to a GM and item is not dropable then don't allow trade???? This seems wrong.
					if (client.Account.PrivLevel == (uint)ePrivLevel.Player && tradeTarget != null && tradeTarget.Client.Account.PrivLevel != (uint)ePrivLevel.Player)
					{
						if (!item.IsDropable && !(obj is GameNPC && (obj is Blacksmith || obj is Recharger || (obj as GameNPC).CanTradeAnyItem)))
						{
							client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
							client.Out.SendMessage("You can not remove this item!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
							return;
						}
					}

					if (tradeTarget != null)
					{
						// This is a player trade, let trade code handle
						tradeTarget.ReceiveTradeItem(client.Player, item);
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					if (obj.ReceiveItem(client.Player, item))
					{
						// this object was expecting an item and handled it
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
					return;
				}

				//Is the "item" we want to move money? For Version 1.78+
				if (client.Version >= GameClient.eClientVersion.Version178 && 
					fromClientSlot >= (int)eInventorySlot.Mithril178 && 
					fromClientSlot <= (int)eInventorySlot.Copper178)
				{
					fromClientSlot -= eInventorySlot.Mithril178 - eInventorySlot.Mithril;
				}

				//Is the "item" we want to move money?
				if (fromClientSlot >= (ushort)eInventorySlot.Mithril && fromClientSlot <= (ushort)eInventorySlot.Copper)
				{
					int[] money = new int[5];
					money[fromClientSlot - (ushort)eInventorySlot.Mithril] = itemCount;
					long flatMoney = Money.GetMoney(money[0], money[1], money[2], money[3], money[4]);

					if (client.Version >= GameClient.eClientVersion.Version178) // add it back for proper slot update...
					{
						fromClientSlot += eInventorySlot.Mithril178 - eInventorySlot.Mithril;
					}

					if (!obj.IsWithinRadius(client.Player, WorldMgr.GIVE_ITEM_DISTANCE))
					{
						// show too far away message
						if (obj is GamePlayer)
						{
							client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerMoveItemRequestHandler.TooFarAway", client.Player.GetName((GamePlayer)obj)), eChatType.CT_System, eChatLoc.CL_SystemWindow);
						}
						else
						{
							client.Out.SendMessage(LanguageMgr.GetTranslation(client.Account.Language, "PlayerMoveItemRequestHandler.TooFarAway", obj.GetName(0, false)), eChatType.CT_System, eChatLoc.CL_SystemWindow);
						}

						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					if (flatMoney > client.Player.GetCurrentMoney())
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					client.Player.Notify(GamePlayerEvent.GiveMoney, client.Player, new GiveMoneyEventArgs(client.Player, obj, flatMoney));

					if (tradeTarget != null)
					{
						tradeTarget.ReceiveTradeMoney(client.Player, flatMoney);
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					if (obj.ReceiveMoney(client.Player, flatMoney))
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}

					client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
					return;
				}

				client.Out.SendInventoryItemsUpdate(null);
				return;
			}

			// We did not drop an item on a game object, which means we should have valid from and to slots 
			// since we are moving an item from one window to another.

			// First check for an active InventoryObject

			if (client.Player.ActiveInventoryObject != null && client.Player.ActiveInventoryObject.MoveItem(client.Player, fromClientSlot, toClientSlot))
			{
				//ChatUtil.SendDebugMessage(client, "ActiveInventoryObject handled move");
				return;
			}

			//Do we want to move an item from immediate inventory to immediate inventory or drop on the ground
			if (((fromClientSlot >= (ushort)eInventorySlot.Ground && fromClientSlot <= (ushort)eInventorySlot.LastBackpack)
				|| (fromClientSlot >= (ushort)eInventorySlot.FirstVault && fromClientSlot <= (ushort)eInventorySlot.LastVault)
				|| (fromClientSlot >= (ushort)eInventorySlot.FirstBagHorse && fromClientSlot <= (ushort)eInventorySlot.LastBagHorse))
				&& ((toClientSlot >= (ushort)eInventorySlot.Ground && toClientSlot <= (ushort)eInventorySlot.LastBackpack)
				|| (toClientSlot >= (ushort)eInventorySlot.FirstVault && toClientSlot <= (ushort)eInventorySlot.LastVault)
				|| (toClientSlot >= (ushort)eInventorySlot.FirstBagHorse && toClientSlot <= (ushort)eInventorySlot.LastBagHorse)))
			{
				//We want to drop the item
				if (toClientSlot == (ushort)eInventorySlot.Ground)
				{
					InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromClientSlot);
					if (item == null)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						client.Out.SendMessage("Invalid item (slot# " + fromClientSlot + ").", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}
					if (fromClientSlot < (ushort)eInventorySlot.FirstBackpack)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						return;
					}
					if (!item.IsDropable)
					{
						client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
						client.Out.SendMessage("You can not drop this item!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}

					if (client.Player.DropItem((eInventorySlot)fromClientSlot))
					{
						client.Out.SendMessage("You drop " + item.GetName(0, false) + " on the ground!", eChatType.CT_System, eChatLoc.CL_SystemWindow);
						return;
					}
					client.Out.SendInventoryItemsUpdate(null);
					return;
				}

				client.Player.Inventory.MoveItem((eInventorySlot)fromClientSlot, (eInventorySlot)toClientSlot, itemCount);
				//ChatUtil.SendDebugMessage(client, "Player.Inventory handled move");
				return;
			}

			if (((fromClientSlot >= (ushort)eInventorySlot.Ground && fromClientSlot <= (ushort)eInventorySlot.LastBackpack)
				|| (fromClientSlot >= (ushort)eInventorySlot.FirstVault && fromClientSlot <= (ushort)eInventorySlot.LastVault)
				|| (fromClientSlot >= (ushort)eInventorySlot.FirstBagHorse && fromClientSlot <= (ushort)eInventorySlot.LastBagHorse))
				&& ((toClientSlot == (ushort)eInventorySlot.PlayerPaperDoll || toClientSlot == (ushort)eInventorySlot.NewPlayerPaperDoll)
				|| (toClientSlot >= (ushort)eInventorySlot.Ground && toClientSlot <= (ushort)eInventorySlot.LastBackpack)
				|| (toClientSlot >= (ushort)eInventorySlot.FirstVault && toClientSlot <= (ushort)eInventorySlot.LastVault)
				|| (toClientSlot >= (ushort)eInventorySlot.FirstBagHorse && toClientSlot <= (ushort)eInventorySlot.LastBagHorse)))
			{
				InventoryItem item = client.Player.Inventory.GetItem((eInventorySlot)fromClientSlot);
				if (item == null) return;

				toClientSlot = 0;
				if (item.Item_Type >= (int)eInventorySlot.MinEquipable && item.Item_Type <= (int)eInventorySlot.MaxEquipable)
					toClientSlot = (ushort)item.Item_Type;
				if (toClientSlot == 0)
				{
					client.Out.SendInventorySlotsUpdate(new int[] { fromClientSlot });
					return;
				}
				if (toClientSlot == (int)eInventorySlot.LeftBracer || toClientSlot == (int)eInventorySlot.RightBracer)
				{
					if (client.Player.Inventory.GetItem(eInventorySlot.LeftBracer) == null)
						toClientSlot = (int)eInventorySlot.LeftBracer;
					else
						toClientSlot = (int)eInventorySlot.RightBracer;
				}

				if (toClientSlot == (int)eInventorySlot.LeftRing || toClientSlot == (int)eInventorySlot.RightRing)
				{
					if (client.Player.Inventory.GetItem(eInventorySlot.LeftRing) == null)
						toClientSlot = (int)eInventorySlot.LeftRing;
					else
						toClientSlot = (int)eInventorySlot.RightRing;
				}

				client.Player.Inventory.MoveItem((eInventorySlot)fromClientSlot, (eInventorySlot)toClientSlot, itemCount);
				//ChatUtil.SendDebugMessage(client, "Player.Inventory handled move (2)");
				return;
			}

			client.Out.SendInventoryItemsUpdate(null);
		}