public void RemoveCloudsFrom(Chunk chunk) { for (int i = Clouds.Count - 1; i >= 0; i--) { if (chunk.Contains(Clouds[i].Position)) { DeleteCloud(Clouds[i]); } } }
public void AddCloudsToNewChunk(Chunk chunk) { if (chunk.WorldPosition.Z >= 3 && chunk.WorldPosition.Z <= 7 && Utilities.UtilRandom.Next(100) > 90) { double d1 = Utilities.UtilRandom.NextDouble() * Chunk.CHUNK_SIZE; double d2 = Utilities.UtilRandom.NextDouble() * Chunk.CHUNK_SIZE; double d3 = Utilities.UtilRandom.NextDouble() * Chunk.CHUNK_SIZE; Cloud cloud = new Cloud(this, chunk.WorldPosition.ToLocation() * Chunk.CHUNK_SIZE + new Location(d1, d2, d3)); int rand = Utilities.UtilRandom.Next(7) > 2 ? Utilities.UtilRandom.Next(50) + 50: Utilities.UtilRandom.Next(100); for (int i = 0; i < rand; i++) { AddToCloud(cloud, 10f); } SpawnCloud(cloud); } }
void RenderChunkInternal(WorldSystem.Region tregion, Vector3i chunkCoords, Chunk chunk) { Stopwatch sw = new Stopwatch(); sw.Start(); MaterialImage bmp = new MaterialImage() { Colors = new Color[BmpSize, BmpSize] }; for (int x = 0; x < Chunk.CHUNK_SIZE; x++) { for (int y = 0; y < Chunk.CHUNK_SIZE; y++) { // TODO: async chunk read locker? BlockInternal topOpaque = BlockInternal.AIR; int topZ = 0; for (int z = 0; z < Chunk.CHUNK_SIZE; z++) { BlockInternal bi = chunk.GetBlockAt(x, y, z); if (bi.IsOpaque()) { topOpaque = bi; topZ = z; } } if (!topOpaque.Material.RendersAtAll()) { DrawImage(bmp, MaterialImages[0], x * TexWidth, y * TexWidth, Color.Transparent); } for (int z = topZ; z < Chunk.CHUNK_SIZE; z++) { BlockInternal bi = chunk.GetBlockAt(x, y, z); if (bi.Material.RendersAtAll()) { MaterialImage zmatbmp = MaterialImages[bi.Material.TextureID(MaterialSide.TOP)]; if (zmatbmp == null) { continue; } Color zcolor = Colors.ForByte(bi.BlockPaint); if (zcolor.A == 0) { zcolor = Color.White; } DrawImage(bmp, zmatbmp, x * TexWidth, y * TexWidth, zcolor); } } } } sw.Stop(); Timings_A += sw.ElapsedTicks / (double)Stopwatch.Frequency; sw.Reset(); sw.Start(); Bitmap tbmp = new Bitmap(BmpSize2, BmpSize2); BitmapData bdat = tbmp.LockBits(new Rectangle(0, 0, tbmp.Width, tbmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int stride = bdat.Stride; // Surely there's a better way to do this! unsafe { byte* ptr = (byte*)bdat.Scan0; for (int x = 0; x < BmpSize; x++) { for (int y = 0; y < BmpSize; y++) { Color tcol = bmp.Colors[x, y]; ptr[(x * 4) + y * stride + 0] = tcol.B; ptr[(x * 4) + y * stride + 1] = tcol.G; ptr[(x * 4) + y * stride + 2] = tcol.R; ptr[(x * 4) + y * stride + 3] = tcol.A; } } } tbmp.UnlockBits(bdat); sw.Stop(); Timings_B += sw.ElapsedTicks / (double)Stopwatch.Frequency; sw.Reset(); sw.Start(); DataStream ds = new DataStream(); tbmp.Save(ds, ImageFormat.Png); tbmp.Dispose(); sw.Stop(); Timings_C += sw.ElapsedTicks / (double)Stopwatch.Frequency; sw.Reset(); sw.Start(); lock (OneAtATimePlease) // NOTE: We can probably make this grab off an array of locks to reduce load a little. { KeyValuePair<int, int> maxes = tregion.ChunkManager.GetMaxes((int)chunkCoords.X, (int)chunkCoords.Y); tregion.ChunkManager.SetMaxes((int)chunkCoords.X, (int)chunkCoords.Y, Math.Min(maxes.Key, (int)chunkCoords.Z), Math.Max(maxes.Value, (int)chunkCoords.Z)); } tregion.ChunkManager.WriteImage((int)chunkCoords.X, (int)chunkCoords.Y, (int)chunkCoords.Z, ds.ToArray()); sw.Stop(); Timings_D += sw.ElapsedTicks / (double)Stopwatch.Frequency; }
public void RenderChunk(WorldSystem.Region tregion, Vector3i chunkCoords, Chunk chunk) { #if TIMINGS Stopwatch sw = new Stopwatch(); sw.Start(); #endif if (tregion.TheServer.CVars.g_renderblocks.ValueB) { RenderChunkInternal(tregion, chunkCoords, chunk); } if (tregion.TheServer.CVars.n_rendersides.ValueB) { RenderChunkInternalAngle(tregion, chunkCoords, chunk); } #if TIMINGS sw.Stop(); Timings_General += sw.ElapsedTicks / (double)Stopwatch.Frequency; #endif }
// TODO: Efficiency? public bool TryChunk(Vector3i cworldPos, int posMult, Chunk chi = null) { if (pkick) { return false; } if (!ChunksAwareOf.ContainsKey(cworldPos) || ChunksAwareOf[cworldPos].LOD > posMult) // TODO: Efficiency - TryGetValue? { double dist = (cworldPos.ToLocation() * Chunk.CHUNK_SIZE - LoadRelPos).LengthSquared(); bool async = chi == null && dist > (Chunk.CHUNK_SIZE * Chunk.CHUNK_SIZE * 2 * 2); if (async) { TheRegion.LoadChunk_Background(cworldPos, (chn) => { if (!pkick && chn != null) { ChunkNetwork.SendPacket(new ChunkInfoPacketOut(chn, posMult)); } }); } else { Chunk chk = chi != null ? chi : TheRegion.LoadChunk(cworldPos); ChunkNetwork.SendPacket(new ChunkInfoPacketOut(chk, posMult)); } ChunksAwareOf.Remove(cworldPos); ChunksAwareOf.Add(cworldPos, new ChunkAwarenessInfo() { ChunkPos = cworldPos, LOD = posMult }); return true; } return false; }
public bool ForgetChunk(Chunk ch, Vector3i cpos) { if (ChunksAwareOf.Remove(cpos)) { foreach (Entity ent in TheRegion.Entities) { if (ch.Contains(ent.GetPosition())) { Network.SendPacket(new DespawnEntityPacketOut(ent.EID)); } } ChunkNetwork.SendPacket(new ChunkForgetPacketOut(cpos)); return true; } return false; }
/// <summary> /// Immediately populates a chunk. /// </summary> /// <param name="chunk">The chunk to populate.</param> /// <param name="allowFile">Whether loading from file is allowed.</param> /// <param name="fileOnly">Whether we can ONLY load from file.</param> /// <returns>Whether it successfully populated the chunk.</returns> public bool PopulateChunk(Chunk chunk, bool allowFile, bool fileOnly = false) { try { if (allowFile) { ChunkDetails dat = null; lock (chunk.GetLocker()) { try { dat = ChunkManager.GetChunkDetails((int)chunk.WorldPosition.X, (int)chunk.WorldPosition.Y, (int)chunk.WorldPosition.Z); } catch (Exception ex) { SysConsole.Output("Reading chunk " + chunk.WorldPosition, ex); } } ChunkDetails ents = null; lock (chunk.GetLocker()) { try { ents = ChunkManager.GetChunkEntities((int)chunk.WorldPosition.X, (int)chunk.WorldPosition.Y, (int)chunk.WorldPosition.Z); } catch (Exception ex) { SysConsole.Output("Reading chunk " + chunk.WorldPosition, ex); } } if (dat != null) { if (ents == null) { ents = new ChunkDetails() { X = dat.X, Y = dat.Y, Z = dat.Z, Version = dat.Version, Flags = dat.Flags, Reachables = null, Blocks = new byte[0] }; } chunk.LoadFromSaveData(dat, ents); if (!chunk.Flags.HasFlag(ChunkFlags.ISCUSTOM)) { chunk.Flags &= ~ChunkFlags.POPULATING; } if (!chunk.Flags.HasFlag(ChunkFlags.POPULATING)) { return(true); } } } } catch (Exception ex) { Utilities.CheckException(ex); SysConsole.Output(OutputType.ERROR, "Loading chunk: " + chunk.WorldPosition.ToString() + ": " + ex.ToString()); return(false); } if (fileOnly) { return(false); } try { Generator.Populate(TheWorld.Seed, TheWorld.Seed2, TheWorld.Seed3, TheWorld.Seed4, TheWorld.Seed5, chunk); chunk.LastEdited = GlobalTickTime; chunk.Flags &= ~(ChunkFlags.POPULATING | ChunkFlags.ISCUSTOM); chunk.Flags |= ChunkFlags.NEEDS_DETECT; chunk.IsNew = true; } catch (Exception ex) { Utilities.CheckException(ex); SysConsole.Output(OutputType.ERROR, "Loading chunk" + chunk.WorldPosition.ToString() + ": " + ex.ToString()); return(false); } return(true); }
public Chunk LoadChunkNoPopulate(Vector3i cpos) { Chunk chunk; if (LoadedChunks.TryGetValue(cpos, out chunk)) { // Be warned, it may still be loading here! return chunk; } chunk = new Chunk(); chunk.Flags = ChunkFlags.ISCUSTOM | ChunkFlags.POPULATING; chunk.OwningRegion = this; chunk.WorldPosition = cpos; if (PopulateChunk(chunk, true, true)) { LoadedChunks.Add(cpos, chunk); chunk.Flags &= ~ChunkFlags.ISCUSTOM; chunk.AddToWorld(); } chunk.LastEdited = GlobalTickTime; return chunk; }
/// <summary> /// Ticks the entire region. /// </summary> public void Tick() { if (Delta <= 0) { return; } PostPhysics(); opsat += Delta; while (opsat > 1.0) { opsat -= 1.0; OncePerSecondActions(); } Stopwatch sw = new Stopwatch(); sw.Start(); if (Delta > TheWorld.TargetDelta * 2) { PhysicsWorld.TimeStepSettings.TimeStepDuration = Delta * 0.5; } else { PhysicsWorld.TimeStepSettings.TimeStepDuration = TheWorld.TargetDelta; } PhysicsWorld.Update(Delta); sw.Stop(); TheServer.PhysicsTimeC += sw.Elapsed.TotalMilliseconds; TheServer.PhysicsTimes++; sw.Reset(); // TODO: Async tick sw.Start(); for (int i = 0; i < Tickers.Count; i++) { if (!Tickers[i].Removed && Tickers[i] is PhysicsEntity) { (Tickers[i] as PhysicsEntity).PreTick(); } } for (int i = 0; i < Tickers.Count; i++) { if (!Tickers[i].Removed) { Tickers[i].Tick(); } } for (int i = 0; i < Tickers.Count; i++) { if (!Tickers[i].Removed && Tickers[i] is PhysicsEntity) { (Tickers[i] as PhysicsEntity).EndTick(); } } for (int i = 0; i < DespawnQuick.Count; i++) { DespawnEntity(DespawnQuick[i]); } DespawnQuick.Clear(); for (int i = 0; i < Joints.Count; i++) // TODO: Optimize! { if (Joints[i].Enabled && Joints[i] is BaseFJoint) { ((BaseFJoint)Joints[i]).Solve(); } } sw.Stop(); TheServer.EntityTimeC += sw.Elapsed.TotalMilliseconds; TheServer.EntityTimes++; while (ChunkFixQueue.TryDequeue(out Vector3i res)) { Chunk chkres = GetChunk(res); chkres?.LateSpawn(); } }
public abstract void Populate(int seed, int seed2, int seed3, int seed4, int seed5, Chunk chunk);
void HandleChunkBGOne(Chunk chunk, Action<Chunk> callback) { if (chunk.Flags.HasFlag(ChunkFlags.ISCUSTOM)) { chunk.Flags &= ~ChunkFlags.ISCUSTOM; chunk.LoadSchedule = TheWorld.Schedule.StartASyncTask(() => { chunk.UnloadTimer = 0; PopulateChunk(chunk, false, false, true); chunk.UnloadTimer = 0; chunk.LoadSchedule = null; TheWorld.Schedule.ScheduleSyncTask(() => { chunk.UnloadTimer = 0; chunk.AddToWorld(); callback.Invoke(chunk); }); }); return; } if (chunk.Flags.HasFlag(ChunkFlags.POPULATING)) { LoadedChunks.Remove(chunk.WorldPosition); ChunkManager.ClearChunkDetails(chunk.WorldPosition); SysConsole.Output(OutputType.ERROR, "non-custom chunk was still loading when grabbed: " + chunk.WorldPosition); } chunk.LoadSchedule = TheWorld.Schedule.StartASyncTask(() => { chunk.UnloadTimer = 0; PopulateChunk(chunk, true, false, true); chunk.UnloadTimer = 0; chunk.LoadSchedule = null; TheWorld.Schedule.ScheduleSyncTask(() => { chunk.UnloadTimer = 0; chunk.AddToWorld(); callback.Invoke(chunk); }); }); }
public bool PopulateChunk(Chunk chunk, bool allowFile, bool fileOnly = false, bool async = false) { try { if (allowFile) { ChunkDetails dat; lock (chunk.GetLocker()) { dat = ChunkManager.GetChunkDetails((int)chunk.WorldPosition.X, (int)chunk.WorldPosition.Y, (int)chunk.WorldPosition.Z); } ChunkDetails ents; lock (chunk.GetLocker()) { ents = ChunkManager.GetChunkEntities((int)chunk.WorldPosition.X, (int)chunk.WorldPosition.Y, (int)chunk.WorldPosition.Z); } if (dat != null) { if (ents == null) { ents = new ChunkDetails() { X = dat.X, Y = dat.Y, Z = dat.Z, Version = dat.Version, Flags = dat.Flags, Reachables = null, Blocks = new byte[0] }; } chunk.LoadFromSaveData(dat, ents); if (!chunk.Flags.HasFlag(ChunkFlags.ISCUSTOM)) { chunk.Flags &= ~ChunkFlags.POPULATING; } return true; } } } catch (Exception ex) { Utilities.CheckException(ex); SysConsole.Output(OutputType.ERROR, "Loading chunk: " + chunk.WorldPosition.ToString() + ": " + ex.ToString()); return false; } if (fileOnly) { return false; } try { Generator.Populate(TheWorld.Seed, TheWorld.Seed2, TheWorld.Seed3, TheWorld.Seed4, TheWorld.Seed5, chunk); chunk.LastEdited = GlobalTickTime; chunk.Flags &= ~(ChunkFlags.POPULATING | ChunkFlags.ISCUSTOM); chunk.Flags |= ChunkFlags.NEEDS_DETECT; } catch (Exception ex) { Utilities.CheckException(ex); SysConsole.Output(OutputType.ERROR, "Loading chunk" + chunk.WorldPosition.ToString() + ": " + ex.ToString()); return false; } return true; }
public void LoadChunk_Background(Vector3i cpos, Action<Chunk> callback = null) { Chunk chunk; if (LoadedChunks.TryGetValue(cpos, out chunk)) { if (chunk.LoadSchedule != null) { TheWorld.Schedule.StartASyncTask(() => { while (chunk.LoadSchedule != null) { Thread.Sleep(1); // TODO: Handle loading a loading chunk more cleanly. } TheWorld.Schedule.ScheduleSyncTask(() => { HandleChunkBGOne(chunk, callback); }); }); return; } HandleChunkBGOne(chunk, callback); return; } chunk = new Chunk(); chunk.Flags = ChunkFlags.POPULATING; chunk.OwningRegion = this; chunk.WorldPosition = cpos; LoadedChunks.Add(cpos, chunk); chunk.UnloadTimer = 0; chunk.LoadSchedule = TheWorld.Schedule.StartASyncTask(() => { chunk.UnloadTimer = 0; PopulateChunk(chunk, true, false, true); chunk.LoadSchedule = null; TheWorld.Schedule.ScheduleSyncTask(() => { chunk.UnloadTimer = 0; chunk.AddToWorld(); callback.Invoke(chunk); }); }); }
void RenderChunkInternalAngle(WorldSystem.Region tregion, Vector3i chunkCoords, Chunk chunk) { MaterialImage bmp = new MaterialImage() { Colors = new Color[BmpSize2, BmpSize2] }; for (int z = 0; z < Chunk.CHUNK_SIZE; z++) { for (int x = 0; x < Chunk.CHUNK_SIZE; x++) { for (int y = 0; y < Chunk.CHUNK_SIZE; y++) { // TODO: async chunk read locker? BlockInternal bi = chunk.GetBlockAt(x, y, z); if (bi.Material.RendersAtAll()) { RenderBlockIntoAngle(bi, x, y, z, bmp); } } } } Bitmap tbmp = new Bitmap(BmpSize2, BmpSize2); BitmapData bdat = tbmp.LockBits(new Rectangle(0, 0, tbmp.Width, tbmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); int stride = bdat.Stride; // Surely there's a better way to do this! unsafe { byte* ptr = (byte*)bdat.Scan0; for (int x = 0; x < BmpSize2; x++) { for (int y = 0; y < BmpSize2; y++) { Color tcol = bmp.Colors[x, y]; ptr[(x * 4) + y * stride + 0] = tcol.B; ptr[(x * 4) + y * stride + 1] = tcol.G; ptr[(x * 4) + y * stride + 2] = tcol.R; ptr[(x * 4) + y * stride + 3] = tcol.A; } } } DataStream ds = new DataStream(); tbmp.Save(ds, ImageFormat.Png); tregion.ChunkManager.WriteImageAngle((int)chunkCoords.X, (int)chunkCoords.Y, (int)chunkCoords.Z, ds.ToArray()); }
public ChunkInfoPacketOut(Chunk chunk, int lod) { UsageType = NetUsageType.CHUNKS; if (chunk.Flags.HasFlag(ChunkFlags.POPULATING) && (lod != 5 || chunk.LOD == null)) { throw new Exception("Trying to transmit chunk while it's still loading! For chunk at " + chunk.WorldPosition); } ID = ServerToClientPacket.CHUNK_INFO; byte[] data_orig; if (lod == 1) { bool isAir = true; data_orig = new byte[chunk.BlocksInternal.Length * 4]; for (int x = 0; x < chunk.BlocksInternal.Length; x++) { ushort mat = chunk.BlocksInternal[x].BlockMaterial; if (mat != 0) { isAir = false; } data_orig[x * 2] = (byte)(mat & 0xFF); data_orig[x * 2 + 1] = (byte)((mat >> 8) & 0xFF); } if (isAir) { data_orig = null; } else { for (int i = 0; i < chunk.BlocksInternal.Length; i++) { data_orig[chunk.BlocksInternal.Length * 2 + i] = chunk.BlocksInternal[i].BlockData; data_orig[chunk.BlocksInternal.Length * 3 + i] = chunk.BlocksInternal[i]._BlockPaintInternal; } } } else { data_orig = chunk.LODBytes(lod, true); } if (data_orig == null) { Data = new byte[12]; // TODO: This is a bit hackish ID = ServerToClientPacket.CHUNK_FORGET; Utilities.IntToBytes((int)chunk.WorldPosition.X).CopyTo(Data, 0); Utilities.IntToBytes((int)chunk.WorldPosition.Y).CopyTo(Data, 4); Utilities.IntToBytes((int)chunk.WorldPosition.Z).CopyTo(Data, 8); return; } byte[] gdata = FileHandler.Compress(data_orig); DataStream ds = new DataStream(gdata.Length + 16); DataWriter dw = new DataWriter(ds); dw.WriteInt((int)chunk.WorldPosition.X); dw.WriteInt((int)chunk.WorldPosition.Y); dw.WriteInt((int)chunk.WorldPosition.Z); dw.WriteInt(lod); byte[] reach = new byte[chunk.Reachability.Length]; for (int i = 0; i < reach.Length; i++) { reach[i] = (byte)(chunk.Reachability[i] ? 1 : 0); } dw.WriteBytes(reach); dw.WriteBytes(gdata); Data = ds.ToArray(); }
public void SpawnTree(string tree, Location opos, Chunk chunk) { // TODO: Efficiency! ModelEntity me = new ModelEntity("plants/trees/" + tree, this); Location pos = opos + new Location(0, 0, 1); /*RayCastResult rcr; bool h = SpecialCaseRayTrace(pos, -Location.UnitZ, 50, MaterialSolidity.FULLSOLID, IgnoreEntities, out rcr); me.SetPosition(h ? new Location(rcr.HitData.Location) : pos);*/ Vector3 treealign = new Vector3(0, 0, 1); Vector3 norm = /*h ? rcr.HitData.Normal : */new Vector3(0, 0, 1); Quaternion orient; Quaternion.GetQuaternionBetweenNormalizedVectors(ref treealign, ref norm, out orient); orient *= Quaternion.CreateFromAxisAngle(Vector3.UnitZ, (double)(Utilities.UtilRandom.NextDouble() * Math.PI * 2)); me.SetOrientation(orient); me.SetPosition(pos); me.CanLOD = true; me.GenBlockShadow = true; Action res = () => { SpawnEntity(me); me.SetPosition(pos - new Location(norm) - new Location(Quaternion.Transform(me.offset.ToBVector(), orient))); me.ForceNetwork(); }; if (chunk == null) { res.Invoke(); } else { chunk.fixesToRun.Add(TheWorld.Schedule.GetSyncTask(res)); } }
public Chunk LoadChunk(Vector3i cpos) { Chunk chunk; if (LoadedChunks.TryGetValue(cpos, out chunk)) { while (chunk.LoadSchedule != null) { Thread.Sleep(1); // TODO: Handle loading a loading chunk more cleanly. } if (chunk.Flags.HasFlag(ChunkFlags.ISCUSTOM)) { chunk.Flags &= ~ChunkFlags.ISCUSTOM; PopulateChunk(chunk, false); chunk.AddToWorld(); } if (chunk.Flags.HasFlag(ChunkFlags.POPULATING)) { LoadedChunks.Remove(cpos); ChunkManager.ClearChunkDetails(cpos); SysConsole.Output(OutputType.ERROR, "non-custom chunk was still loading when grabbed: " + chunk.WorldPosition); } else { chunk.UnloadTimer = 0; return chunk; } } chunk = new Chunk(); chunk.Flags = ChunkFlags.POPULATING; chunk.OwningRegion = this; chunk.WorldPosition = cpos; LoadedChunks.Add(cpos, chunk); PopulateChunk(chunk, true); chunk.AddToWorld(); return chunk; }