/// <summary> /// Initializes the base part of the world manager. /// </summary> protected WorldManager() { WorldHelper.SetGetEntitiesDelegate(GetEntities); WorldHelper.SetGetSegmentNeighboursDelegate(GetSegmentNeighboursForCollisions); // create cache dir if doesn't exist if (!Directory.Exists(WorldHelper.CachePath)) { Directory.CreateDirectory(WorldHelper.CachePath); } }
/// <summary> /// Resets the world instance. /// </summary> /// <param name="name">World name</param> /// <param name="dirName">Name in the world directory</param> public void Reset(string name, string dirName) { Console.WriteLine("Loading world..."); _Initialized = false; _Name = name; _Path = Path.Combine(_WorldsPath, dirName); WorldHelper.SetWorldPath(_Path); if (_Areas != null) { _Areas.Clear(); } _Areas = new Dictionary <int, TArea>(); if (_Segments != null) { _Segments.Clear(); } _Segments = new Dictionary <int, TSegment>(); if (_Entities != null) { _Entities.Clear(); } _Entities = new Dictionary <ushort, Entity>(); if (_LoadThread != null) { _LoadThread.Abort(); _LoadThread.Join(); } _LoadThread = new Thread(LoadWorker); _LoadThread.Priority = ThreadPriority.BelowNormal; _LoadThread.Start(); StopAutoSave(); if (_LoadQueue != null) { _LoadQueue.Clear(); } _LoadQueue = new Queue <LoadRequest>(); OnReset(); }
/// <summary> /// Lods the world from data files. /// </summary> /// <param name="server">Determines whether this is a server load</param> public void Load(bool server) { if (_Initialized) { throw new WorldAlreadyInitializedException(); } // first load world info file string worldPath = GetFullWorldPath(); string[] worldInfo = File.ReadAllLines(worldPath + WorldInfoFileName); _Name = worldInfo[WorldInfoNameLineIndex]; WorldHelper.SetWorldSize(Convert.ToInt32(worldInfo[WorldInfoXSizeLineIndex]), Convert.ToInt32(worldInfo[WorldInfoZSizeLineIndex])); // load entities DirectoryInfo entityDir = new DirectoryInfo(GetFullEntitiesDirectoryPath()); foreach (FileInfo entityFile in entityDir.GetFiles("*" + ExtensionEntity)) { Entity entity = Entity.CreateFromByteArray(File.ReadAllBytes(entityFile.FullName)); _Entities.Add(entity.ID, entity); if (!server) { LoadEntityColumns(new LoadRequest() { MessageId = -entity.ID, Entity = entity }); entity.InitializeTerrainDependencies(); } } _Initialized = true; if (_Configuration.AutoSaveConfigurationNode.EnableAutoSave) { _AutoSaveWait.Reset(); _AutoSaveThread = new Thread(AutoSaveWorker); _AutoSaveThread.Start(); } }
/// <summary> /// Creates an area from binary data. Note that this will also load the /// segments and their boxes. /// </summary> /// <param name="data">Array with serialized data</param> /// <param name="offset">Offset</param> public void InitializeFromByteArray <TSegment, TBox>(byte[] data, ref int offset) where TSegment : Segment, new() where TBox : Box, new() { int length = BitConverter.ToInt32(data, offset); X = BitConverter.ToUInt16(data, offset + OffsetX) * 64; Y = data[offset + OffsetY] * 64; Z = BitConverter.ToUInt16(data, offset + OffsetZ) * 64; // read segments int segmentOffset = offset + OffsetSegmentData; int x = 0; int y = 0; int z = 0; while (segmentOffset < offset + length) { TSegment segment = (TSegment)WorldHelper.GetPooledSegment(); segment.InitializeFromByteArray <TBox>(ref data, ref segmentOffset, this, ref x, ref y, ref z); AddSegment(segment); z += 8; if (z == 64) { z = 0; y += 8; if (y == 64) { y = 0; x += 8; } } } offset += length; InitializeFields(X, Y, Z); }
/// <summary> /// Initializes this segment from binary data. Note that this will also /// load the segment's boxes. /// </summary> /// <param name="data">Array with serialized data</param> /// <param name="offset">Offset</param> /// <param name="area">Wrapping area</param> /// <param name="x">X diff coordinate</param> /// <param name="y">z diff coordinate</param> /// <param name="z">Z diff coordinate</param> internal void InitializeFromByteArray <TBox>(ref byte[] data, ref int offset, Area area, ref int x, ref int y, ref int z) where TBox : Box, new() { short dataLength = BitConverter.ToInt16(data, offset + OffsetDataLength); X = x + area.X; Y = y + area.Y; Z = z + area.Z; _Version = BitConverter.ToUInt32(data, offset + OffsetVersion); InitializeFields(area); for (int i = OffsetBoxData; i < dataLength; i += Box.SerializedSize) { TBox box = (TBox)WorldHelper.GetPooledBox(); box.InitializeFromByteArray(ref data, offset + i, this); AddBox(box); } offset += dataLength; OnInitialized(); }
internal List <CollisionWall> GetCollisionWalls() { lock (_Lock) { if (_CollisionWalls == null) { _CollisionWalls = new List <CollisionWall>(); Segment neighbour = null; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { for (int z = 0; z < 8; z++) { if (!_VisMatrix.Get(ref x, ref y, ref z)) { // skip empty space continue; } Box box = new Box(new Cube(X + x, Y + y, Z + z, 0), this); // check front X wall if (x == 0) { neighbour = WorldHelper.GetSegment(X - 8, Y, Z); if (neighbour != null && !neighbour._VisMatrix.Get(7, y, z)) { _CollisionWalls.Add(new CollisionWall(box, Sides.FrontX)); } } else if (!_VisMatrix.Get(x - 1, y, z)) { _CollisionWalls.Add(new CollisionWall(box, Sides.FrontX)); } // check back X wall if (x == 7) { neighbour = WorldHelper.GetSegment(X + 8, Y, Z); if (neighbour != null && !neighbour._VisMatrix.Get(0, y, z)) { _CollisionWalls.Add(new CollisionWall(box, Sides.BackX)); } } else if (!_VisMatrix.Get(x + 1, y, z)) { _CollisionWalls.Add(new CollisionWall(box, Sides.BackX)); } // check front Z wall if (z == 0) { neighbour = WorldHelper.GetSegment(X, Y, Z - 8); if (neighbour != null && !neighbour._VisMatrix.Get(x, y, 7)) { _CollisionWalls.Add(new CollisionWall(box, Sides.FrontZ)); } } else if (!_VisMatrix.Get(x, y, z - 1)) { _CollisionWalls.Add(new CollisionWall(box, Sides.FrontZ)); } // check back Z wall if (z == 7) { neighbour = WorldHelper.GetSegment(X, Y, Z + 8); if (neighbour != null && !neighbour._VisMatrix.Get(x, y, 0)) { _CollisionWalls.Add(new CollisionWall(box, Sides.BackZ)); } } else if (!_VisMatrix.Get(x, y, z + 1)) { _CollisionWalls.Add(new CollisionWall(box, Sides.BackZ)); } } } } } return(_CollisionWalls); } }
/// <summary> /// Places or removes a block. /// </summary> /// <param name="place">True to place block</param> /// <param name="x">X coordinate of the placed/removed block</param> /// <param name="y">Y coordinate of the placed/removed block</param> /// <param name="z">Z coordinate of the placed/removed block</param> /// <param name="version">Current segment version</param> /// <returns>True if block has been placed/removed</returns> public bool PlaceOrRemoveBlock(bool place, out int x, out int y, out int z, out uint version) { x = -1; y = -1; z = -1; version = 0; if (!_CanPlaceBlocks) { return(false); } // check closest intersection Point3D eyeLocation = new Point3D(Location, 0.0f, _EyeHeightFromCenter, 0.0f); Vector3D line = new Vector3D(eyeLocation, NewDirection); Intersection closest = new Intersection(); List <Tuple <Box, int, int> > boxes = new List <Tuple <Box, int, int> >(); // first add current segment's boxes which have 0 shift foreach (Box box in _CurrentSegment.GetBoxesSynchronized()) { boxes.Add(Tuple.Create(box, 0, 0)); } // now add other boxes and determine possible shift foreach (Segment neighbour in _CurrentSegmentNeighbours) { int shiftX = 0; if (_CurrentSegment.X - neighbour.X > WorldHelper.HalfSizeX) { shiftX = WorldHelper.SizeX; } else if (neighbour.X - _CurrentSegment.X > WorldHelper.HalfSizeX) { shiftX = -WorldHelper.SizeX; } int shiftZ = 0; if (_CurrentSegment.Z - neighbour.Z > WorldHelper.HalfSizeZ) { shiftZ = WorldHelper.SizeZ; } else if (neighbour.Z - _CurrentSegment.Z > WorldHelper.HalfSizeZ) { shiftZ = -WorldHelper.SizeZ; } foreach (Box box in neighbour.GetBoxesSynchronized()) { boxes.Add(Tuple.Create(box, shiftX, shiftZ)); } } if (eyeLocation.Y * eyeLocation.Y < BlockInteractionRange) { boxes.Add(Tuple.Create(Box.FloorBox, 0, 0)); } int reverseShiftX = 0; int reverseShiftZ = 0; foreach (Tuple <Box, int, int> box in boxes) { Intersection intersection = box.Item1.GetIntersection(line, box.Item2, box.Item3); if (intersection.Distance < closest.Distance) { closest = intersection; reverseShiftX = -box.Item2; reverseShiftZ = -box.Item3; } } // check if there is a close intersection at all if (closest.IntersectionPoint == null || closest.Distance > BlockInteractionRange) { // not close enough or no point at all return(false); } // adjust the reverse shift by accounting for reverse number truncating // when below 0 if (closest.IntersectionPoint.X < 0 && closest.IntersectionPoint.X % 1 != 0) { reverseShiftX--; } if (closest.IntersectionPoint.Z < 0 && closest.IntersectionPoint.Z % 1 != 0) { reverseShiftZ--; } // calculate cube location without the shift x = Convert.ToInt32(Math.Truncate(closest.IntersectionPoint.X)) + reverseShiftX; y = Convert.ToInt32(Math.Truncate(closest.IntersectionPoint.Y)); z = Convert.ToInt32(Math.Truncate(closest.IntersectionPoint.Z)) + reverseShiftZ; if (place) { // correct the location based on intersection side switch (closest.Side) { case Sides.FrontX: x--; break; case Sides.FrontY: y--; break; case Sides.FrontZ: z--; break; } } else { // correct the location based on intersection side switch (closest.Side) { case Sides.BackX: x--; break; case Sides.BackY: y--; break; case Sides.BackZ: z--; break; } } // check for floor/ceiling limits if (y < 0 || y > byte.MaxValue) { return(false); } // adjust possible shift over the edge of the world after correction if (x < 0) { x += WorldHelper.SizeX; } else if (x >= WorldHelper.SizeX) { x -= WorldHelper.SizeX; } if (z < 0) { z += WorldHelper.SizeZ; } else if (z >= WorldHelper.SizeZ) { z -= WorldHelper.SizeZ; } // check if placing the block will clip into an entity if (place) { Box box = new Box(new Cube(x, y, z, 0), null); List <Entity> entities = WorldHelper.GetEntities(box.CenterPoint, BlockEntityCollisionSelectRange); bool collides = false; foreach (Entity entity in entities) { box.CheckSuffocation(entity.Location, ref entity._HalfSizeX, ref entity._HalfSizeY, ref collides); if (collides) { // at least one entity collides with the newly placed box return(false); } } } // retrieve the destination segment and place or remove the block Segment destinationSegment = WorldHelper.GetSegment(x, y, z); version = destinationSegment.PlaceOrRemoveBlockAndUpdateVersion(place, x, y, z, GetSelectedMaterial()); return(true); }
private void CheckSegmentChanged() { int segmentX = Convert.ToInt32(Math.Truncate(Location.X)); segmentX -= segmentX % 8; if (segmentX >= WorldHelper.SizeX) { segmentX -= WorldHelper.SizeX; } else if (segmentX < 0) { segmentX += WorldHelper.SizeX; } int segmentY = Convert.ToInt32(Math.Truncate(Location.Y)); segmentY -= segmentY % 8; int segmentZ = Convert.ToInt32(Math.Truncate(Location.Z)); segmentZ -= segmentZ % 8; if (segmentZ >= WorldHelper.SizeZ) { segmentZ -= WorldHelper.SizeZ; } else if (segmentZ < 0) { segmentZ += WorldHelper.SizeZ; } bool refreshNeighbours = false; // check if segment needs to be changed if (_CurrentSegment == null || !(_CurrentSegment.X == segmentX && _CurrentSegment.Y == segmentY && _CurrentSegment.Z == segmentZ)) { _CurrentSegment = WorldHelper.GetSegment(segmentX, segmentY, segmentZ); refreshNeighbours = true; } Point3D inBorderCheckPoint = new Point3D(Location.X - _CurrentSegment.X, Location.Y - _CurrentSegment.Y, Location.Z - _CurrentSegment.Z); bool xRefresh = (inBorderCheckPoint.X < 4 != _PreviousCheckPoint.X < 4); bool yRefresh = (inBorderCheckPoint.Y < 4 != _PreviousCheckPoint.Y < 4); bool zRefresh = (inBorderCheckPoint.Z < 4 != _PreviousCheckPoint.Z < 4); // check if neighbours are to be refreshed if (refreshNeighbours || xRefresh || yRefresh || zRefresh) { _CurrentSegmentNeighbours = WorldHelper.GetSegmentNeighboursForCollisions(_CurrentSegment, Location); } _PreviousCheckPoint = inBorderCheckPoint; // check if column area isn't changed int x = Convert.ToInt32(Math.Truncate(Location.X)); x -= x % 64; int z = Convert.ToInt32(Math.Truncate(Location.Z)); z -= z % 64; if (CurrentColumnX != x || CurrentColumnZ != z) { _CurrentColumnX = x; _CurrentColumnZ = z; if (OnColumnChanged != null) { OnColumnChanged(this, EventArgs.Empty); } } }