public static void HandleLoginRequestPacket(IPacket packet, IRemoteClient client, IMultiplayerServer server) { var loginRequestPacket = (LoginRequestPacket)packet; var remoteClient = (RemoteClient)client; if (loginRequestPacket.ProtocolVersion < server.PacketReader.ProtocolVersion) remoteClient.QueuePacket(new DisconnectPacket("Client outdated! Use beta 1.7.3.")); else if (loginRequestPacket.ProtocolVersion > server.PacketReader.ProtocolVersion) remoteClient.QueuePacket(new DisconnectPacket("Server outdated! Use beta 1.7.3.")); else if (server.Worlds.Count == 0) remoteClient.QueuePacket(new DisconnectPacket("Server has no worlds configured.")); else if (!server.PlayerIsWhitelisted(remoteClient.Username) && server.PlayerIsBlacklisted(remoteClient.Username)) remoteClient.QueuePacket(new DisconnectPacket("You're banned from this server")); else if (server.Clients.Count(c => c.Username == client.Username) > 1) remoteClient.QueuePacket(new DisconnectPacket("The player with this username is already logged in")); else { remoteClient.LoggedIn = true; remoteClient.Entity = new PlayerEntity(remoteClient.Username); remoteClient.World = server.Worlds[0]; remoteClient.ChunkRadius = 2; if (!remoteClient.Load()) remoteClient.Entity.Position = remoteClient.World.SpawnPoint; // Make sure they don't spawn in the ground var collision = new Func<bool>(() => { var feet = client.World.GetBlockID((Coordinates3D)client.Entity.Position); var head = client.World.GetBlockID((Coordinates3D)(client.Entity.Position + Vector3.Up)); var feetBox = server.BlockRepository.GetBlockProvider(feet).BoundingBox; var headBox = server.BlockRepository.GetBlockProvider(head).BoundingBox; return feetBox != null || headBox != null; }); while (collision()) client.Entity.Position += Vector3.Up; // Send setup packets remoteClient.QueuePacket(new LoginResponsePacket(0, 0, Dimension.Overworld)); remoteClient.UpdateChunks(); remoteClient.QueuePacket(new WindowItemsPacket(0, remoteClient.Inventory.GetSlots())); remoteClient.QueuePacket(new SpawnPositionPacket((int)remoteClient.Entity.Position.X, (int)remoteClient.Entity.Position.Y, (int)remoteClient.Entity.Position.Z)); remoteClient.QueuePacket(new SetPlayerPositionPacket(remoteClient.Entity.Position.X, remoteClient.Entity.Position.Y + 1, remoteClient.Entity.Position.Y + remoteClient.Entity.Size.Height + 1, remoteClient.Entity.Position.Z, remoteClient.Entity.Yaw, remoteClient.Entity.Pitch, true)); remoteClient.QueuePacket(new TimeUpdatePacket(remoteClient.World.Time)); // Start housekeeping for this client var entityManager = server.GetEntityManagerForWorld(remoteClient.World); entityManager.SpawnEntity(remoteClient.Entity); entityManager.SendEntitiesToClient(remoteClient); server.Scheduler.ScheduleEvent("remote.keepalive", remoteClient, TimeSpan.FromSeconds(10), remoteClient.SendKeepAlive); server.Scheduler.ScheduleEvent("remote.chunks", remoteClient, TimeSpan.FromSeconds(1), remoteClient.ExpandChunkRadius); if (!string.IsNullOrEmpty(Program.ServerConfiguration.MOTD)) remoteClient.SendMessage(Program.ServerConfiguration.MOTD); if (!Program.ServerConfiguration.Singleplayer) server.SendMessage(ChatColor.Yellow + "{0} joined the server.", remoteClient.Username); } }
public static void HandleLoginRequestPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server) { var packet = (LoginRequestPacket)_packet; var client = (RemoteClient)_client; if (packet.ProtocolVersion < server.PacketReader.ProtocolVersion) { client.QueuePacket(new DisconnectPacket("Client outdated! Use beta 1.7.3.")); } else if (packet.ProtocolVersion > server.PacketReader.ProtocolVersion) { client.QueuePacket(new DisconnectPacket("Server outdated! Use beta 1.7.3.")); } else if (server.Worlds.Count == 0) { client.QueuePacket(new DisconnectPacket("Server has no worlds configured.")); } else { client.LoggedIn = true; client.Entity = new PlayerEntity(client.Username); client.World = server.Worlds[0]; client.ChunkRadius = 2; if (!client.Load()) { client.Entity.Position = client.World.SpawnPoint; } // Send setup packets client.QueuePacket(new LoginResponsePacket(0, 0, Dimension.Overworld)); client.UpdateChunks(); client.QueuePacket(new WindowItemsPacket(0, client.Inventory.GetSlots())); client.QueuePacket(new SpawnPositionPacket((int)client.Entity.Position.X, (int)client.Entity.Position.Y, (int)client.Entity.Position.Z)); client.QueuePacket(new SetPlayerPositionPacket(client.Entity.Position.X, client.Entity.Position.Y + 1, client.Entity.Position.Y + client.Entity.Size.Height + 1, client.Entity.Position.Z, client.Entity.Yaw, client.Entity.Pitch, true)); client.QueuePacket(new TimeUpdatePacket(client.World.Time)); // Start housekeeping for this client var entityManager = server.GetEntityManagerForWorld(client.World); entityManager.SpawnEntity(client.Entity); entityManager.SendEntitiesToClient(client); server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(10), client.SendKeepAlive); server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(1), client.ExpandChunkRadius); if (!string.IsNullOrEmpty(Program.Configuration.MOTD)) { client.SendMessage(Program.Configuration.MOTD); } server.SendMessage(ChatColor.Yellow + "{0} joined the server.", client.Username); } }
public static void HandlePlayerDiggingPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server) { var packet = (PlayerDiggingPacket)_packet; var client = (RemoteClient)_client; var world = _client.World; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var descriptor = world.GetBlockData(position); var provider = server.BlockRepository.GetBlockProvider(descriptor.ID); short damage; int time; switch (packet.PlayerAction) { case PlayerDiggingPacket.Action.DropItem: // Throwing item if (client.SelectedItem.Empty) { break; } var spawned = client.SelectedItem; spawned.Count = 1; var inventory = client.SelectedItem; inventory.Count--; var item = new ItemEntity(client.Entity.Position + new Vector3(0, PlayerEntity.Height, 0), spawned); item.Velocity = MathHelper.FowardVector(client.Entity.Yaw) * 0.3; client.Inventory[client.SelectedSlot] = inventory; server.GetEntityManagerForWorld(client.World).SpawnEntity(item); break; case PlayerDiggingPacket.Action.StartDigging: foreach (var nearbyClient in server.Clients) // TODO: Send this repeatedly during the course of the digging { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) { c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.SwingArm)); } } if (provider == null) { server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player digging)", descriptor.ID); } else { provider.BlockLeftClicked(descriptor, packet.Face, world, client); } // "But why on Earth does this behavior change if you use shears on leaves?" // "This is poor seperation of concerns" // "Let me do a git blame and flame whoever wrote the next line" // To answer all of those questions, here: // Minecraft sends a player digging packet when the player starts and stops digging a block (two packets) // However, it only sends ONE packet if the block would be mined immediately - which usually is only the case // for blocks that have a hardness equal to zero. // The exception to this rule is shears on leaves. Leaves normally have a hardness of 0.2, but when you mine them // using shears the client only sends the start digging packet and expects them to be mined immediately. // So if you want to blame anyone, send flames to Notch for the stupid idea of not sending "stop digging" packets // for hardness == 0 blocks. time = BlockProvider.GetHarvestTime(descriptor.ID, client.SelectedItem.ID, out damage); if (time <= 20) { provider.BlockMined(descriptor, packet.Face, world, client); break; } client.ExpectedDigComplete = DateTime.UtcNow.AddMilliseconds(time); break; case PlayerDiggingPacket.Action.StopDigging: foreach (var nearbyClient in server.Clients) { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) { c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None)); } } if (provider != null && descriptor.ID != 0) { time = BlockProvider.GetHarvestTime(descriptor.ID, client.SelectedItem.ID, out damage); if (time <= 20) { break; // Already handled earlier } var diff = (DateTime.UtcNow - client.ExpectedDigComplete).TotalMilliseconds; if (diff > -100) // Allow a small tolerance { provider.BlockMined(descriptor, packet.Face, world, client); // Damage the item if (damage != 0) { var tool = server.ItemRepository.GetItemProvider(client.SelectedItem.ID) as ToolItem; if (tool != null && tool.Uses != -1) { var slot = client.SelectedItem; slot.Metadata += damage; if (slot.Metadata >= tool.Uses) { slot.Count = 0; // Destroy item } client.Inventory[client.SelectedSlot] = slot; } } } } break; } }
public static void HandlePlayerBlockPlacementPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server) { var packet = (PlayerBlockPlacementPacket)_packet; var client = (RemoteClient)_client; var slot = client.SelectedItem; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); BlockDescriptor?block = null; if (position != -Coordinates3D.One) { if (position.DistanceTo((Coordinates3D)client.Entity.Position) > 10 /* TODO: Reach */) { return; } block = client.World.GetBlockData(position); } else { // TODO: Handle situations like firing arrows and such? Is that how it works? return; } bool use = true; if (block != null) { var provider = server.BlockRepository.GetBlockProvider(block.Value.ID); if (provider == null) { server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player placing)", block.Value.ID); server.SendMessage(ChatColor.Red + "Error occured from client {0} at coordinates {1}", client.Username, block.Value.Coordinates); server.SendMessage(ChatColor.Red + "Packet logged at {0}, please report upstream", DateTime.UtcNow); return; } if (!provider.BlockRightClicked(block.Value, packet.Face, client.World, client)) { position += MathHelper.BlockFaceToCoordinates(packet.Face); var oldID = client.World.GetBlockID(position); var oldMeta = client.World.GetMetadata(position); client.QueuePacket(new BlockChangePacket(position.X, (sbyte)position.Y, position.Z, (sbyte)oldID, (sbyte)oldMeta)); client.QueuePacket(new SetSlotPacket(0, client.SelectedSlot, client.SelectedItem.ID, client.SelectedItem.Count, client.SelectedItem.Metadata)); return; } } if (!slot.Empty) { if (use) { var itemProvider = server.ItemRepository.GetItemProvider(slot.ID); if (itemProvider == null) { server.SendMessage(ChatColor.Red + "WARNING: item provider for ID {0} is null (player placing)", block.Value.ID); server.SendMessage(ChatColor.Red + "Error occured from client {0} at coordinates {1}", client.Username, block.Value.Coordinates); server.SendMessage(ChatColor.Red + "Packet logged at {0}, please report upstream", DateTime.UtcNow); } if (block != null) { if (itemProvider != null) { itemProvider.ItemUsedOnBlock(position, slot, packet.Face, client.World, client); } } else { // TODO: Use item } } } }
public static void HandleLoginRequestPacket(IPacket packet, IRemoteClient client, IMultiplayerServer server) { var loginRequestPacket = (LoginRequestPacket)packet; var remoteClient = (RemoteClient)client; if (loginRequestPacket.ProtocolVersion < server.PacketReader.ProtocolVersion) { remoteClient.QueuePacket(new DisconnectPacket("Client outdated! Use beta 1.7.3.")); } else if (loginRequestPacket.ProtocolVersion > server.PacketReader.ProtocolVersion) { remoteClient.QueuePacket(new DisconnectPacket("Server outdated! Use beta 1.7.3.")); } else if (server.Worlds.Count == 0) { remoteClient.QueuePacket(new DisconnectPacket("Server has no worlds configured.")); } else if (!server.PlayerIsWhitelisted(remoteClient.Username) && server.PlayerIsBlacklisted(remoteClient.Username)) { remoteClient.QueuePacket(new DisconnectPacket("You're banned from this server")); } else if (server.Clients.Count(c => c.Username == client.Username) > 1) { remoteClient.QueuePacket(new DisconnectPacket("The player with this username is already logged in")); } else { remoteClient.LoggedIn = true; remoteClient.Entity = new PlayerEntity(remoteClient.Username); remoteClient.World = server.Worlds[0]; remoteClient.ChunkRadius = 2; if (!remoteClient.Load()) { remoteClient.Entity.Position = remoteClient.World.SpawnPoint; } // Make sure they don't spawn in the ground var collision = new Func <bool>(() => { var feet = client.World.GetBlockID((Coordinates3D)client.Entity.Position); var head = client.World.GetBlockID((Coordinates3D)(client.Entity.Position + Vector3.Up)); var feetBox = server.BlockRepository.GetBlockProvider(feet).BoundingBox; var headBox = server.BlockRepository.GetBlockProvider(head).BoundingBox; return(feetBox != null || headBox != null); }); while (collision()) { client.Entity.Position += Vector3.Up; } var entityManager = server.GetEntityManagerForWorld(remoteClient.World); entityManager.SpawnEntity(remoteClient.Entity); // Send setup packets remoteClient.QueuePacket(new LoginResponsePacket(client.Entity.EntityID, 0, Dimension.Overworld)); remoteClient.UpdateChunks(); remoteClient.QueuePacket(new WindowItemsPacket(0, remoteClient.Inventory.GetSlots())); remoteClient.QueuePacket(new UpdateHealthPacket((remoteClient.Entity as PlayerEntity).Health)); remoteClient.QueuePacket(new SpawnPositionPacket((int)remoteClient.Entity.Position.X, (int)remoteClient.Entity.Position.Y, (int)remoteClient.Entity.Position.Z)); remoteClient.QueuePacket(new SetPlayerPositionPacket(remoteClient.Entity.Position.X, remoteClient.Entity.Position.Y + 1, remoteClient.Entity.Position.Y + remoteClient.Entity.Size.Height + 1, remoteClient.Entity.Position.Z, remoteClient.Entity.Yaw, remoteClient.Entity.Pitch, true)); remoteClient.QueuePacket(new TimeUpdatePacket(remoteClient.World.Time)); // Start housekeeping for this client entityManager.SendEntitiesToClient(remoteClient); server.Scheduler.ScheduleEvent("remote.keepalive", remoteClient, TimeSpan.FromSeconds(10), remoteClient.SendKeepAlive); server.Scheduler.ScheduleEvent("remote.chunks", remoteClient, TimeSpan.FromSeconds(1), remoteClient.ExpandChunkRadius); if (!string.IsNullOrEmpty(Program.ServerConfiguration.MOTD)) { remoteClient.SendMessage(Program.ServerConfiguration.MOTD); } if (!Program.ServerConfiguration.Singleplayer) { server.SendMessage(ChatColor.Yellow + "{0} joined the server.", remoteClient.Username); } } }
public static void HandlePlayerDiggingPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server) { var packet = (PlayerDiggingPacket)_packet; var client = (RemoteClient)_client; var world = _client.World; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var descriptor = world.GetBlockData(position); var provider = server.BlockRepository.GetBlockProvider(descriptor.ID); switch (packet.PlayerAction) { case PlayerDiggingPacket.Action.DropItem: // Throwing item if (client.SelectedItem.Empty) { break; } var spawned = client.SelectedItem; spawned.Count = 1; var inventory = client.SelectedItem; inventory.Count--; var item = new ItemEntity(client.Entity.Position + new Vector3(0, PlayerEntity.Height, 0), spawned); item.Velocity = MathHelper.FowardVector(client.Entity.Yaw) * 0.3; client.Inventory[client.SelectedSlot] = inventory; server.GetEntityManagerForWorld(client.World).SpawnEntity(item); break; case PlayerDiggingPacket.Action.StartDigging: foreach (var nearbyClient in server.Clients) // TODO: Send this repeatedly during the course of the digging { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) { c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.SwingArm)); } } if (provider == null) { server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player digging)", descriptor.ID); } else { provider.BlockLeftClicked(descriptor, packet.Face, world, client); } if (provider != null && provider.Hardness == 0) { provider.BlockMined(descriptor, packet.Face, world, client); } break; case PlayerDiggingPacket.Action.StopDigging: foreach (var nearbyClient in server.Clients) { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) { c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None)); } } if (provider != null && descriptor.ID != 0) { provider.BlockMined(descriptor, packet.Face, world, client); } break; } }
public static void HandlePlayerDiggingPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server) { var packet = (PlayerDiggingPacket)_packet; var client = (RemoteClient)_client; var world = _client.World; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var descriptor = world.GetBlockData(position); var provider = server.BlockRepository.GetBlockProvider(descriptor.ID); short damage; int time; switch (packet.PlayerAction) { case PlayerDiggingPacket.Action.DropItem: // Throwing item if (client.SelectedItem.Empty) break; var spawned = client.SelectedItem; spawned.Count = 1; var inventory = client.SelectedItem; inventory.Count--; var item = new ItemEntity(client.Entity.Position + new Vector3(0, PlayerEntity.Height, 0), spawned); item.Velocity = MathHelper.FowardVector(client.Entity.Yaw) * 0.3; client.Inventory[client.SelectedSlot] = inventory; server.GetEntityManagerForWorld(client.World).SpawnEntity(item); break; case PlayerDiggingPacket.Action.StartDigging: foreach (var nearbyClient in server.Clients) // TODO: Send this repeatedly during the course of the digging { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.SwingArm)); } if (provider == null) server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player digging)", descriptor.ID); else provider.BlockLeftClicked(descriptor, packet.Face, world, client); // "But why on Earth does this behavior change if you use shears on leaves?" // "This is poor seperation of concerns" // "Let me do a git blame and flame whoever wrote the next line" // To answer all of those questions, here: // Minecraft sends a player digging packet when the player starts and stops digging a block (two packets) // However, it only sends ONE packet if the block would be mined immediately - which usually is only the case // for blocks that have a hardness equal to zero. // The exception to this rule is shears on leaves. Leaves normally have a hardness of 0.2, but when you mine them // using shears the client only sends the start digging packet and expects them to be mined immediately. // So if you want to blame anyone, send flames to Notch for the stupid idea of not sending "stop digging" packets // for hardness == 0 blocks. time = BlockProvider.GetHarvestTime(descriptor.ID, client.SelectedItem.ID, out damage); if (time <= 20) { provider.BlockMined(descriptor, packet.Face, world, client); break; } client.ExpectedDigComplete = DateTime.UtcNow.AddMilliseconds(time); break; case PlayerDiggingPacket.Action.StopDigging: foreach (var nearbyClient in server.Clients) { var c = (RemoteClient)nearbyClient; if (c.KnownEntities.Contains(client.Entity)) c.QueuePacket(new AnimationPacket(client.Entity.EntityID, AnimationPacket.PlayerAnimation.None)); } if (provider != null && descriptor.ID != 0) { time = BlockProvider.GetHarvestTime(descriptor.ID, client.SelectedItem.ID, out damage); if (time <= 20) break; // Already handled earlier var diff = (DateTime.UtcNow - client.ExpectedDigComplete).TotalMilliseconds; if (diff > -100) // Allow a small tolerance { provider.BlockMined(descriptor, packet.Face, world, client); // Damage the item if (damage != 0) { var tool = server.ItemRepository.GetItemProvider(client.SelectedItem.ID) as ToolItem; if (tool != null && tool.Uses != -1) { var slot = client.SelectedItem; slot.Metadata += damage; if (slot.Metadata >= tool.Uses) slot.Count = 0; // Destroy item client.Inventory[client.SelectedSlot] = slot; } } } } break; } }
public static void HandlePlayerBlockPlacementPacket(IPacket _packet, IRemoteClient _client, IMultiplayerServer server) { var packet = (PlayerBlockPlacementPacket)_packet; var client = (RemoteClient)_client; var slot = client.SelectedItem; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); BlockDescriptor? block = null; if (position != -Coordinates3D.One) { if (position.DistanceTo((Coordinates3D)client.Entity.Position) > 10 /* TODO: Reach */) return; block = client.World.GetBlockData(position); } else { // TODO: Handle situations like firing arrows and such? Is that how it works? return; } bool use = true; if (block != null) { var provider = server.BlockRepository.GetBlockProvider(block.Value.ID); if (provider == null) { server.SendMessage(ChatColor.Red + "WARNING: block provider for ID {0} is null (player placing)", block.Value.ID); server.SendMessage(ChatColor.Red + "Error occured from client {0} at coordinates {1}", client.Username, block.Value.Coordinates); server.SendMessage(ChatColor.Red + "Packet logged at {0}, please report upstream", DateTime.Now); return; } if (!provider.BlockRightClicked(block.Value, packet.Face, client.World, client)) { position += MathHelper.BlockFaceToCoordinates(packet.Face); var oldID = client.World.GetBlockID(position); var oldMeta = client.World.GetMetadata(position); client.QueuePacket(new BlockChangePacket(position.X, (sbyte)position.Y, position.Z, (sbyte)oldID, (sbyte)oldMeta)); client.QueuePacket(new SetSlotPacket(0, client.SelectedSlot, client.SelectedItem.ID, client.SelectedItem.Count, client.SelectedItem.Metadata)); return; } } if (!slot.Empty) { if (use) { var itemProvider = server.ItemRepository.GetItemProvider(slot.ID); if (itemProvider == null) { server.SendMessage(ChatColor.Red + "WARNING: item provider for ID {0} is null (player placing)", block.Value.ID); server.SendMessage(ChatColor.Red + "Error occured from client {0} at coordinates {1}", client.Username, block.Value.Coordinates); server.SendMessage(ChatColor.Red + "Packet logged at {0}, please report upstream", DateTime.Now); } if (block != null) { if (itemProvider != null) itemProvider.ItemUsedOnBlock(position, slot, packet.Face, client.World, client); } else { // TODO: Use item } } } }