public static void RightClick(RemoteClient client, MinecraftServer server, IPacket _packet) { var packet = (RightClickPacket)_packet; var slot = client.Entity.Inventory[client.Entity.SelectedSlot]; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var cursorPosition = new Coordinates3D(packet.CursorX, packet.CursorY, packet.CursorZ); BlockInfo?block = null; if (position != -Coordinates3D.One) { if (position.DistanceTo((Coordinates3D)client.Entity.Position) > client.Reach) { return; } block = client.World.GetBlockInfo(position); } bool use = true; if (block != null) { use = client.World.RightClickBlock(position, packet.Face, cursorPosition, slot.AsItem()); } if (!slot.Empty) { var item = slot.AsItem(); if (use) { if (block != null) { client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value); if (item.Value.ItemId < 0x100) { client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId), position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch)); } if (client.GameMode != GameMode.Creative) { slot.Count--; // TODO: This is probably a bad place to put this code if (slot.Count == 0) { client.Entity.Inventory[client.Entity.SelectedSlot] = ItemStack.EmptyStack; } else { client.Entity.Inventory[client.Entity.SelectedSlot] = slot; } } } else { client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value); if (item.Value.ItemId < 0x100) { client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId), position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch)); } } } } }
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; if (position != -Coordinates3D.One) { if (position.DistanceTo((Coordinates3D)client.Entity.Position) > 10 /* TODO: Reach */) { return; } block = client.World.GetBlockData(position); } else { return; } 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) { 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); } itemProvider?.ItemUsedOnBlock(position, slot, packet.Face, client.World, client); } }
public PathResult FindPath(IWorld world, BoundingBox subject, Coordinates3D start, Coordinates3D goal) { // Thanks to www.redblobgames.com/pathfinding/a-star/implementation.html var parents = new Dictionary <Coordinates3D, Coordinates3D>(); var costs = new Dictionary <Coordinates3D, double>(); var openset = new PriorityQueue <Coordinates3D>(); var closedset = new HashSet <Coordinates3D>(); openset.Enqueue(start, 0); parents[start] = start; costs[start] = start.DistanceTo(goal); while (openset.Count > 0) { var current = openset.Dequeue(); if (current == goal) { return(TracePath(start, goal, parents)); } closedset.Add(current); foreach (var next in GetNeighbors(world, subject, current)) { if (closedset.Contains(next)) { continue; } var cost = (int)(costs[current] + current.DistanceTo(next)); if (!costs.ContainsKey(next) || cost < costs[next]) { costs[next] = cost; var priority = cost + next.DistanceTo(goal); openset.Enqueue(next, priority); parents[next] = current; } } } return(null); }
public PathResult FindPath(IWorld world, BoundingBox subject, Coordinates3D start, Coordinates3D goal) { // Thanks to www.redblobgames.com/pathfinding/a-star/implementation.html var parents = new Dictionary<Coordinates3D, Coordinates3D>(); var costs = new Dictionary<Coordinates3D, double>(); var openset = new PriorityQueue<Coordinates3D>(); var closedset = new HashSet<Coordinates3D>(); openset.Enqueue(start, 0); parents[start] = start; costs[start] = start.DistanceTo(goal); while (openset.Count > 0) { var current = openset.Dequeue(); if (current == goal) return TracePath(start, goal, parents); closedset.Add(current); foreach (var next in GetNeighbors(world, subject, current)) { if (closedset.Contains(next)) continue; var cost = (int)(costs[current] + current.DistanceTo(next)); if (!costs.ContainsKey(next) || cost < costs[next]) { costs[next] = cost; var priority = cost + next.DistanceTo(goal); openset.Enqueue(next, priority); parents[next] = current; } } } return null; }
public PathResult FindPath(IWorld world, BoundingBox subject, Coordinates3D start, Coordinates3D goal) { // Thanks to www.redblobgames.com/pathfinding/a-star/implementation.html var parents = new Dictionary<Coordinates3D, Coordinates3D>(); var costs = new Dictionary<Coordinates3D, double>(); var openset = new PriorityQueue<Coordinates3D>(); var closedset = new HashSet<Coordinates3D>(); openset.Enqueue(start, 0); parents[start] = start; costs[start] = start.DistanceTo(goal); while (openset.Count > 0) { var current = openset.Dequeue(); if (current == goal) return TracePath(start, goal, parents); closedset.Add(current); // Test directly adjacent voxels for (int i = 0; i < Neighbors.Length; i++) { var next = Neighbors[i] + current; if (closedset.Contains(next)) continue; if (!CanOccupyVoxel(world, subject, next)) continue; var cost = (int)(costs[current] + current.DistanceTo(next)); if (!costs.ContainsKey(next) || cost < costs[next]) { costs[next] = cost; var priority = cost + next.DistanceTo(goal); openset.Enqueue(next, priority); parents[next] = current; } } // Test diagonally for (int i = 0; i < DiagonalNeighbors.Length; i++) { var pair = DiagonalNeighbors[i]; var next = pair[0] + pair[1] + current; if (closedset.Contains(next)) continue; if (!CanOccupyVoxel(world, subject, next)) continue; if (!CanOccupyVoxel(world, subject, pair[0] + current)) continue; if (!CanOccupyVoxel(world, subject, pair[1] + current)) continue; var cost = (int)(costs[current] + current.DistanceTo(next)); if (!costs.ContainsKey(next) || cost < costs[next]) { costs[next] = cost; var priority = cost + next.DistanceTo(goal); openset.Enqueue(next, priority); parents[next] = current; } } } return null; }
/// <summary> /// Produces a list of outward flow targets that this block may flow towards. /// </summary> protected LiquidFlow[] DetermineOutwardFlow(IWorld world, Coordinates3D coords) { // The maximum distance we will search for lower ground to flow towards const int dropCheckDistance = 5; var outwardFlow = new List<LiquidFlow>(5); var currentLevel = world.GetMetadata(coords); var blockBelow = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down)); if (blockBelow.Hardness == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID) { outwardFlow.Add(new LiquidFlow(coords + Coordinates3D.Down, 1)); if (currentLevel != 0) return outwardFlow.ToArray(); } if (currentLevel < MaximumFluidDepletion) { // This code is responsible for seeking out candidates for flowing towards. // Fluid in Minecraft will flow in the direction of the nearest drop-off where // there is at least one block removed on the Y axis. // It will flow towards several equally strong candidates at once. var candidateFlowPoints = new List<Coordinates3D>(4); var furthestPossibleCandidate = new Coordinates3D(x: dropCheckDistance + 1, z: dropCheckDistance + 1) + Coordinates3D.Down; var nearestCandidate = furthestPossibleCandidate; for (int x = -dropCheckDistance; x < dropCheckDistance; x++) { for (int z = -dropCheckDistance; z < dropCheckDistance; z++) { if (Math.Abs(z) + Math.Abs(x) > dropCheckDistance) continue; var check = new Coordinates3D(x: x, z: z) + Coordinates3D.Down; var c = world.BlockRepository.GetBlockProvider(world.GetBlockID(check + coords)); if (c.Hardness == 0) { if (!LineOfSight(world, check + coords, coords)) continue; if (coords.DistanceTo(check + coords) == coords.DistanceTo(nearestCandidate + coords)) candidateFlowPoints.Add(check); if (coords.DistanceTo(check + coords) < coords.DistanceTo(nearestCandidate + coords)) { candidateFlowPoints.Clear(); nearestCandidate = check; } } } } if (nearestCandidate == furthestPossibleCandidate) { candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: dropCheckDistance + 1) + Coordinates3D.Down); candidateFlowPoints.Add(new Coordinates3D(x: dropCheckDistance + 1, z: -dropCheckDistance - 1) + Coordinates3D.Down); candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: -dropCheckDistance - 1) + Coordinates3D.Down); } candidateFlowPoints.Add(nearestCandidate); // For each candidate, determine if we are actually capable of flowing towards it. // We are able to flow through blocks with a hardness of zero, but no others. We are // not able to flow through established fluid blocks. for (int i = 0; i < candidateFlowPoints.Count; i++) { var location = candidateFlowPoints[i]; location.Clamp(1); var xCoordinateCheck = new Coordinates3D(x: location.X) + coords; var zCoordinateCheck = new Coordinates3D(z: location.Z) + coords; var xID = world.BlockRepository.GetBlockProvider(world.GetBlockID(xCoordinateCheck)); var zID = world.BlockRepository.GetBlockProvider(world.GetBlockID(zCoordinateCheck)); if (xID.Hardness == 0 && xID.ID != FlowingID && xID.ID != StillID) { if (outwardFlow.All(f => f.TargetBlock != xCoordinateCheck)) outwardFlow.Add(new LiquidFlow(xCoordinateCheck, (byte)(currentLevel + 1))); } if (zID.Hardness == 0 && zID.ID != FlowingID && zID.ID != StillID) { if (outwardFlow.All(f => f.TargetBlock != zCoordinateCheck)) outwardFlow.Add(new LiquidFlow(zCoordinateCheck, (byte)(currentLevel + 1))); } } // Occasionally, there are scenarios where the nearest candidate hole is not acceptable, but // there is space immediately next to the block. We should fill that space. if (outwardFlow.Count == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID) { for (int i = 0; i < Neighbors.Length; i++) { var b = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Neighbors[i])); if (b.Hardness == 0 && b.ID != StillID && b.ID != FlowingID) outwardFlow.Add(new LiquidFlow(Neighbors[i] + coords, (byte)(currentLevel + 1))); } } } return outwardFlow.ToArray(); }
/// <summary> /// Produces a list of outward flow targets that this block may flow towards. /// </summary> protected LiquidFlow[] DetermineOutwardFlow(IWorld world, Coordinates3D coords) { // The maximum distance we will search for lower ground to flow towards const int dropCheckDistance = 5; var outwardFlow = new List <LiquidFlow>(5); var currentLevel = world.GetMetadata(coords); var blockBelow = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Coordinates3D.Down)); if (blockBelow.Hardness == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID) { outwardFlow.Add(new LiquidFlow(coords + Coordinates3D.Down, 1)); if (currentLevel != 0) { return(outwardFlow.ToArray()); } } if (currentLevel < MaximumFluidDepletion) { // This code is responsible for seeking out candidates for flowing towards. // Fluid in Minecraft will flow in the direction of the nearest drop-off where // there is at least one block removed on the Y axis. // It will flow towards several equally strong candidates at once. var candidateFlowPoints = new List <Coordinates3D>(4); var furthestPossibleCandidate = new Coordinates3D(x: dropCheckDistance + 1, z: dropCheckDistance + 1) + Coordinates3D.Down; var nearestCandidate = furthestPossibleCandidate; for (int x = -dropCheckDistance; x < dropCheckDistance; x++) { for (int z = -dropCheckDistance; z < dropCheckDistance; z++) { if (Math.Abs(z) + Math.Abs(x) > dropCheckDistance) { continue; } var check = new Coordinates3D(x: x, z: z) + Coordinates3D.Down; var c = world.BlockRepository.GetBlockProvider(world.GetBlockID(check + coords)); if (c.Hardness == 0) { if (!LineOfSight(world, check + coords, coords)) { continue; } if (coords.DistanceTo(check + coords) == coords.DistanceTo(nearestCandidate + coords)) { candidateFlowPoints.Add(check); } if (coords.DistanceTo(check + coords) < coords.DistanceTo(nearestCandidate + coords)) { candidateFlowPoints.Clear(); nearestCandidate = check; } } } } if (nearestCandidate == furthestPossibleCandidate) { candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: dropCheckDistance + 1) + Coordinates3D.Down); candidateFlowPoints.Add(new Coordinates3D(x: dropCheckDistance + 1, z: -dropCheckDistance - 1) + Coordinates3D.Down); candidateFlowPoints.Add(new Coordinates3D(x: -dropCheckDistance - 1, z: -dropCheckDistance - 1) + Coordinates3D.Down); } candidateFlowPoints.Add(nearestCandidate); // For each candidate, determine if we are actually capable of flowing towards it. // We are able to flow through blocks with a hardness of zero, but no others. We are // not able to flow through established fluid blocks. for (int i = 0; i < candidateFlowPoints.Count; i++) { var location = candidateFlowPoints[i]; location.Clamp(1); var xCoordinateCheck = new Coordinates3D(x: location.X) + coords; var zCoordinateCheck = new Coordinates3D(z: location.Z) + coords; var xID = world.BlockRepository.GetBlockProvider(world.GetBlockID(xCoordinateCheck)); var zID = world.BlockRepository.GetBlockProvider(world.GetBlockID(zCoordinateCheck)); if (xID.Hardness == 0 && xID.ID != FlowingID && xID.ID != StillID) { if (outwardFlow.All(f => f.TargetBlock != xCoordinateCheck)) { outwardFlow.Add(new LiquidFlow(xCoordinateCheck, (byte)(currentLevel + 1))); } } if (zID.Hardness == 0 && zID.ID != FlowingID && zID.ID != StillID) { if (outwardFlow.All(f => f.TargetBlock != zCoordinateCheck)) { outwardFlow.Add(new LiquidFlow(zCoordinateCheck, (byte)(currentLevel + 1))); } } } // Occasionally, there are scenarios where the nearest candidate hole is not acceptable, but // there is space immediately next to the block. We should fill that space. if (outwardFlow.Count == 0 && blockBelow.ID != FlowingID && blockBelow.ID != StillID) { for (int i = 0; i < Neighbors.Length; i++) { var b = world.BlockRepository.GetBlockProvider(world.GetBlockID(coords + Neighbors[i])); if (b.Hardness == 0 && b.ID != StillID && b.ID != FlowingID) { outwardFlow.Add(new LiquidFlow(Neighbors[i] + coords, (byte)(currentLevel + 1))); } } } } return(outwardFlow.ToArray()); }
public static void RightClick(RemoteClient client, MinecraftServer server, IPacket _packet) { var packet = (RightClickPacket)_packet; var slot = client.Entity.Inventory[client.Entity.SelectedSlot]; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var cursorPosition = new Coordinates3D(packet.CursorX, packet.CursorY, packet.CursorZ); BlockDescriptor? block = null; if (position != -Coordinates3D.One) { if (position.DistanceTo(client.Entity.Position) > client.Reach) return; block = client.World.GetBlock(position); } bool use = true; if (block != null) use = Block.OnBlockRightClicked(block.Value, client.World, position, AdjustByDirection(packet.Direction), cursorPosition); if (!slot.Empty) { var item = new ItemDescriptor(slot.Id, slot.Metadata); if (use) { if (block != null) { Item.OnItemUsedOnBlock(item, client.World, position, AdjustByDirection(packet.Direction), cursorPosition); if (client.GameMode != GameMode.Creative) { slot.Count--; // TODO: This is probably a bad place to put this code if (slot.Count == 0) client.Entity.Inventory[client.Entity.SelectedSlot] = ItemStack.EmptyStack; else client.Entity.Inventory[client.Entity.SelectedSlot] = slot; } } else Item.OnItemUsed(item); } } }
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 } } } }
public static void RightClick(RemoteClient client, MinecraftServer server, IPacket _packet) { var packet = (RightClickPacket)_packet; var slot = client.Entity.Inventory[client.Entity.SelectedSlot]; var position = new Coordinates3D(packet.X, packet.Y, packet.Z); var cursorPosition = new Coordinates3D(packet.CursorX, packet.CursorY, packet.CursorZ); BlockInfo? block = null; if (position != -Coordinates3D.One) { if (position.DistanceTo((Coordinates3D)client.Entity.Position) > client.Reach) return; block = client.World.GetBlockInfo(position); } bool use = true; if (block != null) use = client.World.RightClickBlock(position, packet.Face, cursorPosition, slot.AsItem()); if (!slot.Empty) { var item = slot.AsItem(); if (use) { if (block != null) { client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value); if (item.Value.ItemId < 0x100) { client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId), position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch)); } if (client.GameMode != GameMode.Creative) { slot.Count--; // TODO: This is probably a bad place to put this code if (slot.Count == 0) client.Entity.Inventory[client.Entity.SelectedSlot] = ItemStack.EmptyStack; else client.Entity.Inventory[client.Entity.SelectedSlot] = slot; } } else { client.World.UseItemOnBlock(position, packet.Face, cursorPosition, item.Value); if (item.Value.ItemId < 0x100) { client.SendPacket(new SoundEffectPacket(Block.GetPlacementSoundEffect(item.Value.ItemId), position.X, position.Y, position.Z, SoundEffectPacket.DefaultVolume, SoundEffectPacket.DefaultPitch)); } } } } }