private void CheckWithTerrain(IAABBEntity entity, World world) { Vector3 collisionPoint, collisionDirection; if (entity.Position.Y + entity.Velocity.Y >= 0 && entity.Position.Y + entity.Velocity.Y <= 255) // Don't do checks outside the map { bool fireEvent = entity.Velocity != Vector3.Zero; // Do terrain collisions if (AdjustVelocityX(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) entity.TerrainCollision(this, collisionPoint, collisionDirection); } if (AdjustVelocityY(entity, world, out collisionPoint, out collisionDirection)) { entity.Velocity *= new Vector3(0.2, 1, 0.2); // TODO: More sophisticated friction if (fireEvent) entity.TerrainCollision(this, collisionPoint, collisionDirection); } if (AdjustVelocityZ(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) entity.TerrainCollision(this, collisionPoint, collisionDirection); } } }
public PhysicsEngine(World world) { World = world; Entities = new List<IPhysicsEntity>(); EntityLock = new object(); LastUpdate = DateTime.MinValue; }
internal ReadOnlyWorld() { World = new World("default"); Level = new Level(); Level.AddWorld(World); UnloadChunks = true; }
private void ItemUsedOnBlock(World world, Coordinates3D coordinates, BlockFace face, Coordinates3D cursor, ItemInfo item) { var info = world.GetBlockInfo(coordinates); if (Block.GetIsSolidOnFace(info, face) == false) return; coordinates += MathHelper.BlockFaceToCoordinates(face); switch (face) { case BlockFace.NegativeZ: world.SetBlockId(coordinates, item.ItemId); world.SetMetadata(coordinates, (byte)Orientation.FacingNorth); break; case BlockFace.PositiveZ: world.SetBlockId(coordinates, item.ItemId); world.SetMetadata(coordinates, (byte)Orientation.FacingSouth); break; case BlockFace.NegativeX: world.SetBlockId(coordinates, item.ItemId); world.SetMetadata(coordinates, (byte)Orientation.FacingWest); break; case BlockFace.PositiveX: world.SetBlockId(coordinates, item.ItemId); world.SetMetadata(coordinates, (byte)Orientation.FacingEast); break; default: // Ladders can't be placed lying flat. break; } }
public static World LoadWorld(string baseDirectory) { if (!Directory.Exists(baseDirectory)) throw new DirectoryNotFoundException(); var world = new World(Path.GetFileName(baseDirectory)); world.BaseDirectory = baseDirectory; return world; }
public static void DefaultBlockMinedHandler(BlockDescriptor block, World world, Coordinates3D destroyedBlock, ItemDescriptor? tool) { var drops = Block.GetBlockDrop(block, world, destroyedBlock); world.SetBlockId(destroyedBlock, 0); world.SetMetadata(destroyedBlock, 0); foreach (var drop in drops) world.OnSpawnEntityRequested(new ItemEntity((Vector3)destroyedBlock + new Vector3(0.5), drop)); }
public PhysicsEngine(World world, IBlockPhysicsProvider physicsProvider, int MillisecondsBetweenUpdates) { World = world; Entities = new List<IPhysicsEntity>(); EntityLock = new object(); LastUpdate = DateTime.MinValue; BlockPhysicsProvider = physicsProvider; this.MillisecondsBetweenUpdates = MillisecondsBetweenUpdates; }
/// <summary> /// Creates a region from the given region file. /// </summary> public Region(Coordinates2D position, World world, string file) : this(position, world) { if (File.Exists(file)) regionFile = File.Open(file, FileMode.OpenOrCreate); else { regionFile = File.Open(file, FileMode.OpenOrCreate); CreateRegionHeader(); } }
public void SpawnEntity(World world, Entity entity) { entity.EntityId = NextEntityId++; entity.World = world; if (entity is IDiskEntity) // Assign chunk { var chunk = GetEntityChunk(entity.World, entity.Position); chunk.Entities.Add((IDiskEntity)entity); chunk.IsModified = true; } entity.PropertyChanged -= EntityPropertyChanged; entity.PropertyChanged += EntityPropertyChanged; Entities.Add(entity); SpawnOnClients(entity); entity.Despawn -= EntityDespawn; entity.Despawn += EntityDespawn; if (entity is IPhysicsEntity) { // Add to physics engine var engine = Server.GetPhysicsForWorld(world); engine.AddEntity((IPhysicsEntity)entity); } }
protected internal PhysicsEngine GetPhysicsForWorld(World world) { return PhysicsEngines.FirstOrDefault(p => p.World == world); }
public void MoveClientToWorld(RemoteClient client, World world, Vector3? spawnPoint = null) { if (client.World == world) return; lock (client.LoadedChunks) client.PauseChunkUpdates = true; EntityManager.Despawn(client.Entity); while (client.KnownEntities.Any()) client.ForgetEntity(EntityManager.GetEntityById(client.KnownEntities[0])); EntityManager.Update(); EntityManager.SpawnEntity(world, client.Entity); client.UnloadAllChunks(); // TODO: Allow player to save their positions in each world if (spawnPoint == null) client.Entity.Position = world.WorldGenerator.SpawnPoint; else client.Entity.Position = spawnPoint.Value; client.UpdateChunks(true); client.SendPacket(new PlayerPositionAndLookPacket(client.Entity.Position.X, client.Entity.Position.Y + 0.1 + PlayerEntity.Height, client.Entity.Position.Z, client.Entity.Position.Y + 0.1, client.Entity.Yaw, client.Entity.Pitch, false)); EntityManager.SendClientEntities(client); lock (client.LoadedChunks) client.PauseChunkUpdates = false; }
public RemoteClient[] GetClientsInWorld(World world) { return Clients.Where(c => c.IsLoggedIn && c.World == world).ToArray(); }
/// <summary> /// Creates a new Region for server-side use at the given position using /// the provided terrain generator. /// </summary> public Region(Coordinates2D position, World world) { Chunks = new Dictionary<Coordinates2D, Chunk>(); Position = position; World = world; }
/// <summary> /// Creates and adds a world to this level, with the given name. /// </summary> public void AddWorld(string name, IWorldGenerator worldGenerator = null) { if (Worlds.Any(w => w.Name.ToUpper() == name.ToUpper())) throw new InvalidOperationException("A world with the same name already exists in this level."); var world = new World(name); if (worldGenerator == null) world.WorldGenerator = WorldGenerator; else world.WorldGenerator = worldGenerator; Worlds.Add(world); }
internal static SupportDirection DefaultGetSupportDirectionHandler(BlockDescriptor block, World world, Coordinates3D coordinates) { return SupportDirection.None; }
internal static void DefaultBlockPlacedHandler(BlockDescriptor block, World world, Coordinates3D clickedBlock, Coordinates3D clickedSide, Coordinates3D cursorPosition) { if (world.GetBlockId(clickedBlock + clickedSide) == 0) // TODO: There are more situations than just air when a block can be overwritten { world.SetBlockId(clickedBlock + clickedSide, block.Id); world.SetMetadata(clickedBlock + clickedSide, block.Metadata); } }
public static void DoBlockUpdates(World world, Coordinates3D coordinates) { OnBlockUpdate(world.GetBlock(coordinates), world, coordinates); if ((coordinates + Coordinates3D.Up).Y < Chunk.Height) OnBlockUpdate(world.GetBlock(coordinates + Coordinates3D.Up), world, coordinates + Coordinates3D.Up); if ((coordinates + Coordinates3D.Down).Y >= 0) OnBlockUpdate(world.GetBlock(coordinates + Coordinates3D.Down), world, coordinates + Coordinates3D.Down); OnBlockUpdate(world.GetBlock(coordinates + Coordinates3D.North), world, coordinates + Coordinates3D.North); OnBlockUpdate(world.GetBlock(coordinates + Coordinates3D.South), world, coordinates + Coordinates3D.South); OnBlockUpdate(world.GetBlock(coordinates + Coordinates3D.East), world, coordinates + Coordinates3D.East); OnBlockUpdate(world.GetBlock(coordinates + Coordinates3D.West), world, coordinates + Coordinates3D.West); }
public static void OnItemUsedOnBlock(ItemDescriptor item, World world, Coordinates3D clickedBlock, Coordinates3D clickedSide, Coordinates3D cursorPosition) { GetLogicDescriptor(item).ItemUsedOnBlock(item, world, clickedBlock, clickedSide, cursorPosition); }
internal static void DefaultItemUsedOnBlockHandler(ItemDescriptor item, World world, Coordinates3D clickedBlock, Coordinates3D clickedSide, Coordinates3D cursorPosition) { }
public static void OnItemUsedOnBlock(ItemDescriptor item, World world, Coordinates3D clickedBlock, Coordinates3D clickedSide, Coordinates3D cursorPosition) { if (world.GetBlockId(clickedBlock + clickedSide) == 0) world.SetBlockId(clickedBlock + clickedSide, CakeBlock.BlockId); }
public static ItemStack[] DefaultGetDropHandler(BlockDescriptor block, World world, Coordinates3D minedBlock) { return new[] { new ItemStack(block.Id, 1, block.Metadata) }; }
internal static void DefaultBlockUpdateHandler(BlockDescriptor block, World world, Coordinates3D updatedBlock) { var logic = GetLogicDescriptor(block); var support = logic.GetSupportDirection(block, world, updatedBlock); var supportingBlock = updatedBlock; switch (support) { case SupportDirection.Down: supportingBlock = updatedBlock + Coordinates3D.Down; break; case SupportDirection.Up: supportingBlock = updatedBlock + Coordinates3D.Up; break; case SupportDirection.East: supportingBlock = updatedBlock + Coordinates3D.East; break; case SupportDirection.West: supportingBlock = updatedBlock + Coordinates3D.West; break; case SupportDirection.North: supportingBlock = updatedBlock + Coordinates3D.North; break; case SupportDirection.South: supportingBlock = updatedBlock + Coordinates3D.South; break; default: return; } if (!World.IsValidPosition(supportingBlock)) return; if (world.GetBlockId(supportingBlock) == 0) // TODO: Air isn't the only thing that can't support some blocks OnBlockMined(block, world, updatedBlock, null); // TODO: Consider using a seperate delegate for blocks destroyed through non-player actions }
/// <summary> /// Adds a world to this level. /// </summary> public void AddWorld(World world) { if (Worlds.Any(w => w.Name.ToUpper() == world.Name.ToUpper())) throw new InvalidOperationException("A world with the same name already exists in this level."); Worlds.Add(world); }
internal static bool DefaultBlockRightClickedHandler(BlockDescriptor block, World world, Coordinates3D clickedBlock, Coordinates3D clickedSide, Coordinates3D cursorPosition) { return true; }
public static ItemStack[] GetBlockDrop (BlockDescriptor block, World world, Coordinates3D minedBlock) { if (!BlockLogicDescriptors.ContainsKey(block.Id)) throw new KeyNotFoundException("The given block does not exist."); return BlockLogicDescriptors[block.Id].GetDrop(block, world, minedBlock); }
internal static void DefaultBlockMinedHandler(BlockDescriptor block, World world, Coordinates3D destroyedBlock, ItemDescriptor? tool) { if (GlobalDefaultBlockMinedHandler == null) { world.SetBlockId(destroyedBlock, 0); world.SetMetadata(destroyedBlock, 0); } else GlobalDefaultBlockMinedHandler(block, world, destroyedBlock, tool); }
/// <summary> /// Performs terrain collision tests and adjusts the Y-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityY(IAABBEntity entity, World world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Y == 0) return false; // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Width) - (entity.Position.X < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minY, maxY; // Expand bounding box to include area to be tested if (entity.Velocity.Y < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); maxY = (int)(TempBoundingBox.Max.Y); minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y); } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min - (entity.Size / 2), new Vector3( entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); minY = (int)(entity.BoundingBox.Min.Y); maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y); } // Clamp Y into map boundaries if (minY < 0) minY = 0; if (minY >= World.Height) minY = World.Height - 1; if (maxY < 0) maxY = 0; if (maxY >= World.Height) maxY = World.Height - 1; // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Vector3(x, y, z); var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, (Coordinates3D)position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Y < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.Y; else if (collisionPoint.Value < blockBox.Max.Y) collisionPoint = blockBox.Max.Y; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.Y; else if (collisionPoint.Value > blockBox.Min.Y) collisionPoint = blockBox.Min.Y; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Y < 0) { // TODO: Do block event //var block = world.GetBlock(collision); //block.OnBlockWalkedOn(world, collision, this); entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y + (collisionPoint.Value - TempBoundingBox.Min.Y), entity.Velocity.Z); collisionDirection = Vector3.Down; } else if (entity.Velocity.Y > 0) { entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y - (TempBoundingBox.Max.Y - collisionPoint.Value), entity.Velocity.Z); collisionDirection = Vector3.Up; } return true; } return false; }
private Chunk GetEntityChunk(World world, Vector3 position) { return world.FindChunk(position); }
// TODO: There's a lot of code replication here, perhaps it can be consolidated /// <summary> /// Performs terrain collision tests and adjusts the X-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityX(IAABBEntity entity, World world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.X == 0) return false; // Do some enviornment guessing to improve speed int minY = (int)entity.Position.Y - (entity.Position.Y < 0 ? 1 : 0); int maxY = (int)(entity.Position.Y + entity.Size.Width) - (entity.Position.Y < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minX, maxX; // Expand bounding box to include area to be tested if (entity.Velocity.X < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X + entity.Velocity.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); maxX = (int)(TempBoundingBox.Max.X); minX = (int)(TempBoundingBox.Min.X + entity.Velocity.X); } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min - (entity.Size / 2), new Vector3( entity.BoundingBox.Max.X + entity.Velocity.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) ); minX = (int)(entity.BoundingBox.Min.X); maxX = (int)(entity.BoundingBox.Max.X + entity.Velocity.X); } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Vector3(x, y, z); var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, (Coordinates3D)position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.X < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.X; else if (collisionPoint.Value < blockBox.Max.X) collisionPoint = blockBox.Max.X; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.X; else if (collisionPoint.Value > blockBox.Min.X) collisionPoint = blockBox.Min.X; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.X < 0) { entity.Velocity = new Vector3( entity.Velocity.X - (TempBoundingBox.Min.X - collisionPoint.Value), entity.Velocity.Y, entity.Velocity.Z); collisionDirection = Vector3.Left; } else if (entity.Velocity.X > 0) { entity.Velocity = new Vector3( entity.Velocity.X - (TempBoundingBox.Max.X - collisionPoint.Value), entity.Velocity.Y, entity.Velocity.Z); collisionDirection = Vector3.Right; } return true; } return false; }
public static void OnBlockUpdate(BlockDescriptor block, World world, Coordinates3D updatedBlock) { if (!BlockLogicDescriptors.ContainsKey(block.Id)) throw new KeyNotFoundException("The given block does not exist."); BlockLogicDescriptors[block.Id].BlockUpdated(block, world, updatedBlock); }