private void Event_OnChunkDirty(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) { //api.Logger.Notification("NowDirty @{0}/{1}", chunkCoord.X, chunkCoord.Z); lock (chunksToGenLock) { if (!mapSink.IsOpened) { return; } tmpMccoord.Set(chunkCoord.X / MultiChunkMapComponent.ChunkLen, chunkCoord.Z / MultiChunkMapComponent.ChunkLen); tmpCoord.Set(chunkCoord.X, chunkCoord.Z); if (!loadedMapData.ContainsKey(tmpMccoord) && !curVisibleChunks.Contains(tmpCoord)) { return; } chunksToGen.Enqueue(new Vec2i(chunkCoord.X, chunkCoord.Z)); chunksToGen.Enqueue(new Vec2i(chunkCoord.X, chunkCoord.Z - 1)); chunksToGen.Enqueue(new Vec2i(chunkCoord.X - 1, chunkCoord.Z)); chunksToGen.Enqueue(new Vec2i(chunkCoord.X, chunkCoord.Z + 1)); chunksToGen.Enqueue(new Vec2i(chunkCoord.X + 1, chunkCoord.Z + 1)); } }
// Returns the block pos that is adjacent to a hole BlockPos FindHoleInPit() { smokeLocations.Clear(); HashSet <BlockPos> visitedPositions = new HashSet <BlockPos>(); Queue <BlockPos> bfsQueue = new Queue <BlockPos>(); bfsQueue.Enqueue(Pos); int firewoodBlockId = Api.World.GetBlock(new AssetLocation("firewoodpile")).BlockId; int charcoalPitBlockId = Api.World.GetBlock(new AssetLocation("charcoalpit")).BlockId; int maxHalfSize = 6; while (bfsQueue.Count > 0) { BlockPos bpos = bfsQueue.Dequeue(); BlockPos bposGround = bpos.Copy(); bposGround.Y = 0; int yMax = 0; smokeLocations.TryGetValue(bposGround, out yMax); smokeLocations[bposGround] = Math.Max(yMax, bpos.Y); foreach (BlockFacing facing in BlockFacing.ALLFACES) { BlockPos npos = bpos.AddCopy(facing); IWorldChunk chunk = Api.World.BlockAccessor.GetChunkAtBlockPos(npos); if (chunk == null) { continue; // Maybe at the endge of the loaded chunk } Block nBlock = chunk.GetLocalBlockAtBlockPos(Api.World, npos); if (!nBlock.SideSolid[facing.Opposite.Index] && nBlock.BlockId != firewoodBlockId && nBlock.BlockId != charcoalPitBlockId) { return(bpos); } // Only traverse inside the firewood pile if (nBlock.BlockId != firewoodBlockId) { continue; } // Only traverse within a 12x12x12 block cube bool inCube = Math.Abs(npos.X - Pos.X) <= maxHalfSize && Math.Abs(npos.Y - Pos.Y) <= maxHalfSize && Math.Abs(npos.Z - Pos.Z) <= maxHalfSize; if (inCube && !visitedPositions.Contains(npos)) { bfsQueue.Enqueue(npos); visitedPositions.Add(npos); } } } return(null); }
private void Event_ChunkDirty(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) { long index3d = MapUtil.Index3dL(chunkCoord.X, chunkCoord.Y, chunkCoord.Z, chunkMapSizeX, chunkMapSizeZ); ChunkRooms chunkrooms; Cuboidi cuboid; FastSetOfLongs set = new FastSetOfLongs(); set.Add(index3d); lock (roomsByChunkIndexLock) { roomsByChunkIndex.TryGetValue(index3d, out chunkrooms); if (chunkrooms != null) { set.Add(index3d); for (int i = 0; i < chunkrooms.Rooms.Count; i++) { cuboid = chunkrooms.Rooms[i].Location; int x1 = cuboid.Start.X / chunksize; int x2 = cuboid.End.X / chunksize; int y1 = cuboid.Start.Y / chunksize; int y2 = cuboid.End.Y / chunksize; int z1 = cuboid.Start.Z / chunksize; int z2 = cuboid.End.Z / chunksize; set.Add(MapUtil.Index3dL(x1, y1, z1, chunkMapSizeX, chunkMapSizeZ)); if (z2 != z1) { set.Add(MapUtil.Index3dL(x1, y1, z2, chunkMapSizeX, chunkMapSizeZ)); } if (y2 != y1) { set.Add(MapUtil.Index3dL(x1, y2, z1, chunkMapSizeX, chunkMapSizeZ)); if (z2 != z1) { set.Add(MapUtil.Index3dL(x1, y2, z2, chunkMapSizeX, chunkMapSizeZ)); } } if (x2 != x1) { set.Add(MapUtil.Index3dL(x2, y1, z1, chunkMapSizeX, chunkMapSizeZ)); if (z2 != z1) { set.Add(MapUtil.Index3dL(x2, y1, z2, chunkMapSizeX, chunkMapSizeZ)); } if (y2 != y1) { set.Add(MapUtil.Index3dL(x2, y2, z1, chunkMapSizeX, chunkMapSizeZ)); if (z2 != z1) { set.Add(MapUtil.Index3dL(x2, y2, z2, chunkMapSizeX, chunkMapSizeZ)); } } } } } foreach (long index in set) { roomsByChunkIndex.Remove(index); } } }
private void Event_DidPlaceBlock(IServerPlayer byPlayer, int oldblockId, BlockSelection blockSel, ItemStack withItemStack) { IMapChunk mapchunk = api.World.BlockAccessor.GetMapChunkAtBlockPos(blockSel.Position); if (mapchunk == null) { return; } int lx = blockSel.Position.X % chunksize; int lz = blockSel.Position.Z % chunksize; int y = mapchunk.RainHeightMap[lz * chunksize + lx]; int ly = y % chunksize; IWorldChunk chunk = api.World.BlockAccessor.GetChunkAtBlockPos(blockSel.Position.X, y, blockSel.Position.Z); if (chunk == null) { return; } chunk.Unpack(); int blockId = chunk.Blocks[(ly * chunksize + lz) * chunksize + lx]; if (blockId == 0) { int cx = blockSel.Position.X / chunksize; int cz = blockSel.Position.Z / chunksize; api.World.Logger.Notification("Huh. Found air block in rain map at chunk pos {0}/{1}. That seems invalid, will regenerate rain map", cx, cz); rebuildRainmap(cx, cz); } }
private void Event_ChunkDirty(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) { if (allNetworksFullyLoaded || reason == EnumChunkDirtyReason.MarkedDirty) { return; } allNetworksFullyLoaded = true; nowFullyLoaded.Clear(); foreach (var network in data.networksById.Values) { if (network.fullyLoaded) { continue; } allNetworksFullyLoaded = false; if (network.inChunks.ContainsKey(chunkCoord)) { testFullyLoaded(network); if (network.fullyLoaded) { nowFullyLoaded.Add(network); } } } for (int i = 0; i < nowFullyLoaded.Count; i++) { RebuildNetwork(nowFullyLoaded[i]); } }
private void onChunkData(ChunkReinforcementData msg) { IWorldChunk chunk = api.World.BlockAccessor.GetChunk(msg.chunkX, msg.chunkY, msg.chunkZ); if (chunk != null) { chunk.SetModdata("reinforcements", msg.Data); } }
// Custom implementation for mixed generating/loaded chunk access, since we can spawn entities just fine in either loaded or still generating chunks public bool IsColliding(Cuboidf entityBoxRel, Vec3d pos) { BlockPos blockPos = new BlockPos(); IBlockAccessor blockAccess; int chunksize = wgenBlockAccessor.ChunkSize; Cuboidd entityCuboid = entityBoxRel.ToDouble().Translate(pos); Vec3d blockPosAsVec = new Vec3d(); int minX = (int)(entityBoxRel.X1 + pos.X); int minY = (int)(entityBoxRel.Y1 + pos.Y); int minZ = (int)(entityBoxRel.Z1 + pos.Z); int maxX = (int)Math.Ceiling(entityBoxRel.X2 + pos.X); int maxY = (int)Math.Ceiling(entityBoxRel.Y2 + pos.Y); int maxZ = (int)Math.Ceiling(entityBoxRel.Z2 + pos.Z); for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { blockAccess = wgenBlockAccessor; IWorldChunk chunk = wgenBlockAccessor.GetChunkAtBlockPos(x, y, z); if (chunk == null) { chunk = api.World.BlockAccessor.GetChunkAtBlockPos(x, y, z); blockAccess = api.World.BlockAccessor; } if (chunk == null) { return(true); } chunk.Unpack(); int index = ((y % chunksize) * chunksize + (z % chunksize)) * chunksize + (x % chunksize); Block block = api.World.Blocks[chunk.Blocks[index]]; blockPos.Set(x, y, z); blockPosAsVec.Set(x, y, z); Cuboidf[] collisionBoxes = block.GetCollisionBoxes(blockAccess, blockPos); for (int i = 0; collisionBoxes != null && i < collisionBoxes.Length; i++) { Cuboidf collBox = collisionBoxes[i]; if (collBox != null && entityCuboid.Intersects(collBox, blockPosAsVec)) { return(true); } } } } } return(false); }
private void Event_ChunkDirty(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) { long index3d = MapUtil.Index3dL(chunkCoord.X, chunkCoord.Y, chunkCoord.Z, chunkMapSizeX, chunkMapSizeZ); lock (roomsByChunkIndexLock) { roomsByChunkIndex.Remove(index3d); } }
Dictionary <int, BlockReinforcement> getOrCreateReinforcmentsAt(BlockPos pos) { byte[] data; IWorldChunk chunk = api.World.BlockAccessor.GetChunkAtBlockPos(pos); if (chunk == null) { return(null); } data = chunk.GetModdata("reinforcements"); Dictionary <int, BlockReinforcement> reinforcmentsOfChunk = null; if (data != null) { try { reinforcmentsOfChunk = SerializerUtil.Deserialize <Dictionary <int, BlockReinforcement> >(data); } catch (Exception) { try { api.World.Logger.Warning("Failed reading block reinforcments at block position, maybe old format. Will attempt to convert."); Dictionary <int, BlockReinforcementOld> old = SerializerUtil.Deserialize <Dictionary <int, BlockReinforcementOld> >(data); reinforcmentsOfChunk = new Dictionary <int, BlockReinforcement>(); foreach (var val in old) { reinforcmentsOfChunk[val.Key] = val.Value.Update(); } saveReinforcments(reinforcmentsOfChunk, pos); api.World.Logger.Warning("Ok, converted"); } catch (Exception e2) { api.World.Logger.Error("Failed reading block reinforcments at block position {0}, will discard, sorry. Exception: {1}", pos, e2); } reinforcmentsOfChunk = new Dictionary <int, BlockReinforcement>(); } } else { reinforcmentsOfChunk = new Dictionary <int, BlockReinforcement>(); } return(reinforcmentsOfChunk); }
private void Event_OnChunkDirty(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) { if (reason == EnumChunkDirtyReason.NewlyCreated || !mapSink.IsOpened) { return; } if (!loadedMapData.ContainsKey(new Vec2i(chunkCoord.X, chunkCoord.Z))) { return; } lock (chunksToGenLock) { chunksToGen.Enqueue(new Vec2i(chunkCoord.X, chunkCoord.Z)); } }
private void Event_OnChunkDirty(Vec3i chunkCoord, IWorldChunk chunk, bool isNewChunk) { if (isNewChunk || !mapSink.IsOpened) { return; } if (!loadedMapData.ContainsKey(new Vec2i(chunkCoord.X, chunkCoord.Z))) { return; } lock (chunksToGenLock) { chunksToGen.Enqueue(new Vec2i(chunkCoord.X, chunkCoord.Z)); } }
public virtual void PlaceDecors(IBlockAccessor blockAccessor, BlockPos startPos, bool synchronize) { BlockPos curPos = new BlockPos(); for (int i = 0; i < DecorIndices.Count; i++) { uint index = DecorIndices[i]; int storedBlockid = DecorIds[i]; byte faceIndex = (byte)(storedBlockid >> 24); if (faceIndex > 5) { continue; } BlockFacing face = BlockFacing.ALLFACES[faceIndex]; storedBlockid &= 0xFFFFFF; int dx = (int)(index & 0x1ff); int dy = (int)((index >> 20) & 0x1ff); int dz = (int)((index >> 10) & 0x1ff); AssetLocation blockCode = BlockCodes[storedBlockid]; Block newBlock = blockAccessor.GetBlock(blockCode); if (newBlock == null) { continue; } curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); IWorldChunk chunk = blockAccessor.GetChunkAtBlockPos(curPos); if (chunk == null) { continue; } if (synchronize) { blockAccessor.MarkChunkDecorsModified(curPos); } chunk.SetDecor(blockAccessor, newBlock, curPos, face); chunk.MarkModified(); } }
void saveReinforcments(Dictionary <int, BlockReinforcement> reif, BlockPos pos) { int chunksize = api.World.BlockAccessor.ChunkSize; int chunkX = pos.X / chunksize; int chunkY = pos.Y / chunksize; int chunkZ = pos.Z / chunksize; byte[] data = SerializerUtil.Serialize(reif); IWorldChunk chunk = api.World.BlockAccessor.GetChunk(chunkX, chunkY, chunkZ); chunk.SetModdata("reinforcements", data); // Todo: Send only to players that have this chunk in their loaded range serverChannel?.BroadcastPacket(new ChunkReinforcementData() { chunkX = chunkX, chunkY = chunkY, chunkZ = chunkZ, Data = data }); }
public static int GetSunlight(this IBlockAccessor blockAcessor, int posX, int posY, int posZ) { IWorldAccessor world = (blockAcessor as BlockAccessorBase).GetField <BlockAccessorBase, IWorldAccessor>("worldAccessor"); WorldMap worldMap = (blockAcessor as BlockAccessorBase).GetField <BlockAccessorBase, WorldMap>("worldmap"); IWorldChunk chunkAtBlockPos = blockAcessor.GetChunkAtBlockPos(posX, posY, posZ); if (chunkAtBlockPos == null || !worldMap.IsValidPos(posX, posY, posZ)) { return(world.SunBrightness); } chunkAtBlockPos.Unpack(); int chunkSize = blockAcessor.ChunkSize; int index = (posY % chunkSize * chunkSize + posZ % chunkSize) * chunkSize + posX % chunkSize; int num = (int)chunkAtBlockPos.Light[index]; int val2 = num >> 5 & 31; int val1 = num & 31; return((int)Math.Round((double)val1 * (double)world.Calendar.GetDayLightStrength(posX, posZ))); }
private void Event_ChunkDirty(Vec3i chunkCoord, IWorldChunk chunk, bool isNewChunk) { long index3d = MapUtil.Index3dL(chunkCoord.X, chunkCoord.Y, chunkCoord.Z, chunkMapSizeX, chunkMapSizeZ); CellarsByChunkIndex.Remove(index3d); }
public override int GetLightAbsorption(IWorldChunk chunk, BlockPos pos) { BlockEntityMicroBlock bec = chunk?.GetLocalBlockEntityAtBlockPos(pos) as BlockEntityMicroBlock; return(bec?.GetLightAbsorption() ?? 0); }
void ConvertPit() { Dictionary <BlockPos, Vec3i> quantityPerColumn = new Dictionary <BlockPos, Vec3i>(); HashSet <BlockPos> visitedPositions = new HashSet <BlockPos>(); Queue <BlockPos> bfsQueue = new Queue <BlockPos>(); bfsQueue.Enqueue(Pos); int maxHalfSize = 6; int firewoodBlockId = Api.World.GetBlock(new AssetLocation("firewoodpile")).BlockId; Vec3i curQuantityAndYMinMax; while (bfsQueue.Count > 0) { BlockPos bpos = bfsQueue.Dequeue(); BlockPos bposGround = bpos.Copy(); bposGround.Y = 0; if (quantityPerColumn.TryGetValue(bposGround, out curQuantityAndYMinMax)) { curQuantityAndYMinMax.Y = Math.Min(curQuantityAndYMinMax.Y, bpos.Y); curQuantityAndYMinMax.Z = Math.Max(curQuantityAndYMinMax.Z, bpos.Y); } else { curQuantityAndYMinMax = quantityPerColumn[bposGround] = new Vec3i(0, bpos.Y, bpos.Y); } BlockEntityFirewoodPile be = Api.World.BlockAccessor.GetBlockEntity(bpos) as BlockEntityFirewoodPile; if (be != null) { curQuantityAndYMinMax.X += be.OwnStackSize; } foreach (BlockFacing facing in BlockFacing.ALLFACES) { BlockPos npos = bpos.AddCopy(facing); Block nBlock = Api.World.BlockAccessor.GetBlock(npos); // Only traverse inside the firewood pile if (nBlock.BlockId != firewoodBlockId) { IWorldChunk chunk = Api.World.BlockAccessor.GetChunkAtBlockPos(npos); if (chunk == null) { return; // Maybe at the endge of the loaded chunk, in which case return before changing any blocks and it can be converted next tick instead } continue; } // Only traverse within a 12x12x12 block cube bool inCube = Math.Abs(npos.X - Pos.X) <= maxHalfSize && Math.Abs(npos.Y - Pos.Y) <= maxHalfSize && Math.Abs(npos.Z - Pos.Z) <= maxHalfSize; if (inCube && !visitedPositions.Contains(npos)) { bfsQueue.Enqueue(npos); visitedPositions.Add(npos); } } } BlockPos lpos = new BlockPos(); foreach (var val in quantityPerColumn) { lpos.Set(val.Key.X, val.Value.Y, val.Key.Z); int logQuantity = val.Value.X; int charCoalQuantity = (int)(logQuantity * (0.125f + (float)Api.World.Rand.NextDouble() / 8)); int maxY = val.Value.Z; while (lpos.Y <= maxY) { Block nBlock = Api.World.BlockAccessor.GetBlock(lpos); if (nBlock.BlockId == firewoodBlockId) //test for the possibility someone had contiguous firewood both above and below a soil block for example { if (charCoalQuantity > 0) { Block charcoalBlock = Api.World.GetBlock(new AssetLocation("charcoalpile-" + GameMath.Clamp(charCoalQuantity, 1, 8))); Api.World.BlockAccessor.SetBlock(charcoalBlock.BlockId, lpos); charCoalQuantity -= 8; } else { //Set any free blocks still in this column (y <= maxY) to air Api.World.BlockAccessor.SetBlock(0, lpos); } } lpos.Up(); } } Api.World.BlockAccessor.SetBlock(0, Pos); }
protected virtual void HandleTeleportingServer(float dt) { toremove.Clear(); bool wasTeleporting = somebodyIsTeleporting; somebodyIsTeleporting &= tpingEntities.Count > 0; var sapi = Api as ICoreServerAPI; foreach (var val in tpingEntities) { if (val.Value.Entity.Teleporting) { continue; } val.Value.SecondsPassed += Math.Min(0.5f, dt); if (Api.World.ElapsedMilliseconds - val.Value.LastCollideMs > 100) { // Make sure its not just server lag Block block = Api.World.CollisionTester.GetCollidingBlock(Api.World.BlockAccessor, val.Value.Entity.SelectionBox, val.Value.Entity.Pos.XYZ, true); if (!(block is BlockStaticTranslocator)) { toremove.Add(val.Key); continue; } } if (val.Value.SecondsPassed > 0.1 && !somebodyIsTeleporting) { somebodyIsTeleporting = true; MarkDirty(); } if (val.Value.SecondsPassed > 1.5 && tpTarget != null) { // Preload the chunk IWorldChunk chunk = Api.World.BlockAccessor.GetChunkAtBlockPos(tpTarget); if (chunk != null) { chunk.MapChunk.MarkFresh(); } else { sapi.WorldManager.LoadChunkColumnPriority((int)tpTarget.X / Api.World.BlockAccessor.ChunkSize, (int)tpTarget.Z / Api.World.BlockAccessor.ChunkSize, new ChunkLoadOptions() { KeepLoaded = false }); } } if (val.Value.SecondsPassed > TeleportWarmupSec && tpTarget != null) { val.Value.Entity.TeleportTo(tpTarget.ToVec3d().Add(-0.3, 1, -0.3)); // Fugly, need some better exit pos thing toremove.Add(val.Key); Entity e = val.Value.Entity; if (e is EntityPlayer) { Api.World.Logger.Debug("Teleporting player {0} to {1}", (e as EntityPlayer).GetBehavior <EntityBehaviorNameTag>().DisplayName, tpTarget); } else { Api.World.Logger.Debug("Teleporting entity {0} to {1}", e.Code, tpTarget); } didTeleport(val.Value.Entity); somebodyIsTeleporting = false; somebodyDidTeleport = true; MarkDirty(); } } foreach (long entityid in toremove) { tpingEntities.Remove(entityid); } if (wasTeleporting && !somebodyIsTeleporting) { MarkDirty(); } }
public UpdateSnowLayerChunk UpdateSnowLayer(SnowAccumSnapshot sumsnapshot, bool ignoreOldAccum, IServerMapChunk mc, Vec2i chunkPos, IWorldChunk[] chunksCol) { UpdateSnowLayerChunk updateChunk = new UpdateSnowLayerChunk(); var layers = ws.GeneralConfig.SnowLayerBlocks; int chunkX = chunkPos.X; int chunkZ = chunkPos.Y; int regionX = (chunkX * chunksize) / regionsize; int regionZ = (chunkZ * chunksize) / regionsize; int regionBasePosX = regionX * regionsize; int regionBasePosZ = regionZ * regionsize; BlockPos pos = new BlockPos(); BlockPos placePos = new BlockPos(); float aboveSeaLevelHeight = sapi.World.BlockAccessor.MapSizeY - sapi.World.SeaLevel; int[] posIndices = randomShuffles[sapi.World.Rand.Next(randomShuffles.Length)]; int prevChunkY = -99999; IWorldChunk chunk = null; int maxY = sapi.World.BlockAccessor.MapSizeY - 1; for (int i = 0; i < posIndices.Length; i++) { int posIndex = posIndices[i]; int posY = GameMath.Clamp(mc.RainHeightMap[posIndex], 0, maxY); int chunkY = posY / chunksize; pos.Set( chunkX * chunksize + posIndex % chunksize, posY, chunkZ * chunksize + posIndex / chunksize ); if (prevChunkY != chunkY) { chunk = chunksCol?[chunkY] ?? sapi.WorldManager.GetChunk(chunkX, chunkY, chunkZ); prevChunkY = chunkY; } if (chunk == null) { return(null); } float relx = (pos.X - regionBasePosX) / (float)regionsize; float rely = GameMath.Clamp((pos.Y - sapi.World.SeaLevel) / aboveSeaLevelHeight, 0, 1); float relz = (pos.Z - regionBasePosZ) / (float)regionsize; // What needs to be done here? // 1. Get desired snow cover level // 2. Get current snow cover level // - Get topmmost block. Is it snow? // - Yes. Use it as reference pos and stuff // - No. Must have no snow, increment pos.Y by 1 // 3. Compare and place block accordingly // Idea: New method Block.UpdateSnowLayer() returns a new block instance if a block change is needed // What needs to be done here, take 2 // We have 3 possible cases per-block // 1: We find upside solid block. That means it has no snow on top // 2: We find snow. That means below is a solid block. // 3: We find some other block: That means we should try to find its snow-covered variant // We have the following input data // 1. Snow accumulation changes since the last update (usually an in-game hour or 2) // 2. A precise snow level value from the position (if not set, load from snowlayer block type) (set to zero if the snowlayer is removed) // 3. The current block at position, which is either // - A snow layer: Override with internal level + accum changes // - A solid block: Plase snow on top based on internal level + accum changes // - A snow variantable block: Call the method with the new level Block block = chunk.GetLocalLiquidOrBlockAtBlockPos(sapi.World, pos); float hereAccum = 0; Vec2i vec = new Vec2i(pos.X, pos.Z); if (!ignoreOldAccum && !mc.SnowAccum.TryGetValue(vec, out hereAccum)) { hereAccum = block.GetSnowLevel(pos); } float nowAccum = hereAccum + sumsnapshot.GetAvgSnowAccumByRegionCorner(relx, rely, relz); mc.SnowAccum[vec] = GameMath.Clamp(nowAccum, -1, ws.GeneralConfig.SnowLayerBlocks.Count + 0.6f); float hereShouldLevel = nowAccum - GameMath.MurmurHash3Mod(pos.X, 0, pos.Z, 150) / 300f; float shouldIndexf = GameMath.Clamp(hereShouldLevel - 1.1f, -1, ws.GeneralConfig.SnowLayerBlocks.Count - 1); int shouldIndex = shouldIndexf < 0 ? -1 : (int)shouldIndexf; placePos.Set(pos.X, Math.Min(pos.Y + 1, sapi.World.BlockAccessor.MapSizeY - 1), pos.Z); chunkY = placePos.Y / chunksize; if (prevChunkY != chunkY) { chunk = chunksCol?[chunkY] ?? sapi.WorldManager.GetChunk(chunkX, chunkY, chunkZ); prevChunkY = chunkY; } if (chunk == null) { return(null); } Block upBlock = chunk.GetLocalLiquidOrBlockAtBlockPos(sapi.World, placePos); // Case 1: We have a block that can become snow covered (or more snow covered) placePos.Set(pos); Block newblock = block.GetSnowCoveredVariant(placePos, hereShouldLevel); if (newblock != null) { if (block.Id != newblock.Id && upBlock.Replaceable > 6000) { updateChunk.SetBlocks[placePos.Copy()] = new BlockIdAndSnowLevel(newblock, hereShouldLevel); } } // Case 2: We have a solid block that can have snow on top else if (block.AllowSnowCoverage(sapi.World, placePos)) { placePos.Set(pos.X, pos.Y + 1, pos.Z); if (upBlock.Id != 0) { newblock = upBlock.GetSnowCoveredVariant(placePos, hereShouldLevel); if (newblock != null && upBlock.Id != newblock.Id) { updateChunk.SetBlocks[placePos.Copy()] = new BlockIdAndSnowLevel(newblock, hereShouldLevel); } continue; } if (shouldIndex >= 0) { Block toPlaceBlock = layers.GetKeyAtIndex(shouldIndex); updateChunk.SetBlocks[placePos.Copy()] = new BlockIdAndSnowLevel(toPlaceBlock, hereShouldLevel); } } } return(updateChunk); }
/// <summary> /// This performs a entity search inside a spacially partioned search grid thats refreshed every 16ms. /// This can be a lot faster for when there are thousands of entities on a small space. It is used by EntityBehaviorRepulseAgents to improve performance, because otherwise when spawning 1000 creatures nearby, it has to do 1000x1000 = 1mil search operations every frame /// A small search grid allows us to ignore most of those during the search. /// </summary> /// <param name="centerPos"></param> /// <param name="radius"></param> /// <param name="callback">Return false to stop the walk</param> public void WalkEntities(Vec3d centerPos, double radius, ActionConsumable <Entity> callback) { int mingx = (int)((centerPos.X - radius) / gridSizeInBlocks); int maxgx = (int)((centerPos.X + radius) / gridSizeInBlocks); //int mingy = (int)((centerPos.Y - radius) / gridSizeInBlocks); //int maxgy = (int)((centerPos.Y + radius) / gridSizeInBlocks); int mincy = (int)((centerPos.Y - radius) / chunkSize); int maxcy = (int)((centerPos.Y + radius) / chunkSize); int mingz = (int)((centerPos.Z - radius) / gridSizeInBlocks); int maxgz = (int)((centerPos.Z + radius) / gridSizeInBlocks); double radiusSq = radius * radius; long indexBefore = -1; IWorldChunk chunk = null; EntityPartitionChunk partitionChunk = null; int gridXMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks; //int gridYMax = api.World.BlockAccessor.MapSizeY / gridSizeInBlocks; int gridZMax = api.World.BlockAccessor.MapSizeZ / gridSizeInBlocks; int cyTop = api.World.BlockAccessor.MapSizeY / chunkSize; for (int gridX = mingx; gridX <= maxgx; gridX++) { //for (int gridY = mingy; gridY <= maxgy; gridY++) for (int cy = mincy; cy <= maxcy; cy++) { for (int gridZ = mingz; gridZ <= maxgz; gridZ++) { if (gridX < 0 || cy < 0 || gridZ < 0 || gridX >= gridXMax || cy >= cyTop || gridZ >= gridZMax) { continue; } int cx = gridX * gridSizeInBlocks / chunkSize; //int cy = gridY * gridSizeInBlocks / chunkSize; int cz = gridZ * gridSizeInBlocks / chunkSize; long index3d = MapUtil.Index3dL(cx, cy, cz, chunkMapSizeX, chunkMapSizeZ); if (index3d != indexBefore) { chunk = api.World.BlockAccessor.GetChunk(cx, cy, cz); Partitions.TryGetValue(index3d, out partitionChunk); indexBefore = index3d; } if (chunk == null || chunk.Entities == null || partitionChunk == null) { continue; } int lgx = gridX % partitionsLength; int lgz = gridZ % partitionsLength; List <Entity> entities = partitionChunk.Entities[lgz * partitionsLength + lgx]; for (int i = 0; i < entities.Count; i++) { double distSq = entities[i].SidedPos.SquareDistanceTo(centerPos); if (distSq <= radiusSq && !callback(entities[i])) { return; } } } } } }
void HandleTeleporting(float dt) { toremove.Clear(); bool wasTeleporting = somebodyIsTeleporting; somebodyIsTeleporting &= tpingEntities.Count > 0; foreach (var val in tpingEntities) { if (val.Value.Entity.Teleporting) { continue; } val.Value.SecondsPassed += Math.Min(0.5f, dt); if (Api.World.ElapsedMilliseconds - val.Value.LastCollideMs > 100) { // Make sure its not just server lag Block block = Api.World.CollisionTester.GetCollidingBlock(Api.World.BlockAccessor, val.Value.Entity.CollisionBox, val.Value.Entity.Pos.XYZ, true); if (!(block is BlockStaticTranslocator)) { toremove.Add(val.Key); continue; } } if (val.Value.SecondsPassed > 0.1 && !somebodyIsTeleporting) { somebodyIsTeleporting = true; MarkDirty(); } if (val.Value.SecondsPassed > 1.5 && tpLocation != null) { // Preload the chunk IWorldChunk chunk = sapi.World.BlockAccessor.GetChunkAtBlockPos(tpLocation); if (chunk != null) { chunk.MapChunk.MarkFresh(); } else { sapi.WorldManager.LoadChunkColumnPriority((int)tpLocation.X / Api.World.BlockAccessor.ChunkSize, (int)tpLocation.Z / Api.World.BlockAccessor.ChunkSize, new ChunkLoadOptions() { KeepLoaded = false }); } } if (val.Value.SecondsPassed > 4.4 && tpLocation != null) { val.Value.Entity.TeleportTo(tpLocation.ToVec3d().Add(-0.3, 1, -0.3)); // Fugly, need some better exit pos thing Entity e = val.Value.Entity; if (e is EntityPlayer) { Api.World.Logger.Debug("Teleporting player {0} to {1}", (e as EntityPlayer).GetBehavior <EntityBehaviorNameTag>().DisplayName, tpLocation); manager.DidTranslocateServer((e as EntityPlayer).Player as IServerPlayer); } else { Api.World.Logger.Debug("Teleporting entity {0} to {1}", e.Code, tpLocation); } toremove.Add(val.Key); activated = false; somebodyIsTeleporting = false; somebodyDidTeleport = true; ownBlock.teleportParticles.MinPos.Set(Pos.X, Pos.Y, Pos.Z); ownBlock.teleportParticles.AddPos.Set(1, 1.8, 1); ownBlock.teleportParticles.MinVelocity.Set(-1, -1, -1); ownBlock.teleportParticles.AddVelocity.Set(2, 2, 2); ownBlock.teleportParticles.MinQuantity = 150; ownBlock.teleportParticles.AddQuantity = 0.5f; int r = 53; int g = 221; int b = 172; ownBlock.teleportParticles.Color = (r << 16) | (g << 8) | (b << 0) | (100 << 24); ownBlock.teleportParticles.BlueEvolve = null; ownBlock.teleportParticles.RedEvolve = null; ownBlock.teleportParticles.GreenEvolve = null; ownBlock.teleportParticles.MinSize = 0.1f; ownBlock.teleportParticles.MaxSize = 0.2f; ownBlock.teleportParticles.SizeEvolve = null; ownBlock.teleportParticles.OpacityEvolve = EvolvingNatFloat.create(EnumTransformFunction.QUADRATIC, -10f); Api.World.SpawnParticles(ownBlock.teleportParticles); MarkDirty(); } } foreach (long entityid in toremove) { tpingEntities.Remove(entityid); } if (wasTeleporting && !somebodyIsTeleporting) { MarkDirty(); } }
/// <summary> /// Same as <see cref="WalkEntities(Vec3d, double, API.Common.Action{Entity})"/> but does no exact radius distance check, walks all entities that it finds in the grid /// </summary> /// <param name="centerPos"></param> /// <param name="radius"></param> /// <param name="callback"></param> public void WalkEntityPartitions(Vec3d centerPos, double radius, API.Common.Action <Entity> callback) { int mingx = (int)((centerPos.X - radius) / gridSizeInBlocks); int maxgx = (int)((centerPos.X + radius) / gridSizeInBlocks); /*int mingy = (int)((centerPos.Y - radius) / gridSizeInBlocks); * int maxgy = (int)((centerPos.Y + radius) / gridSizeInBlocks);*/ int mincy = (int)((centerPos.Y - radius) / chunkSize); int maxcy = (int)((centerPos.Y + radius) / chunkSize); int mingz = (int)((centerPos.Z - radius) / gridSizeInBlocks); int maxgz = (int)((centerPos.Z + radius) / gridSizeInBlocks); int cxBefore = -99, cyBefore = -99, czBefore = -99; IWorldChunk chunk = null; EntityPartitionChunk partitionChunk = null; int gridXMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks; //int gridYMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks; int gridZMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks; int cyTop = api.World.BlockAccessor.MapSizeY / chunkSize; for (int gridX = mingx; gridX <= maxgx; gridX++) { for (int cy = mincy; cy <= maxcy; cy++) //for (int gridY = mingy; gridY <= maxgy; gridY++) { for (int gridZ = mingz; gridZ <= maxgz; gridZ++) { if (gridX < 0 || cy < 0 || gridZ < 0 || gridX >= gridXMax || cy >= cyTop || gridZ >= gridZMax) { continue; } int cx = gridX * gridSizeInBlocks / chunkSize; //int cy = gridY * gridSizeInBlocks / chunkSize; int cz = gridZ * gridSizeInBlocks / chunkSize; long index3d = MapUtil.Index3dL(cx, cy, cz, chunkMapSizeX, chunkMapSizeZ); if (cx != cxBefore || cy != cyBefore || cz != czBefore) { chunk = api.World.BlockAccessor.GetChunk(cx, cy, cz); Partitions.TryGetValue(index3d, out partitionChunk); } if (chunk == null || chunk.Entities == null || partitionChunk == null) { continue; } cxBefore = cx; cyBefore = cy; czBefore = cz; int lgx = gridX % partitionsLength; int lgz = gridZ % partitionsLength; List <Entity> entities = partitionChunk.Entities[lgz * partitionsLength + lgx]; for (int i = 0; i < entities.Count; i++) { callback(entities[i]); } } } } }
private void WalkEntities(Vec3d centerPos, double radius, ActionConsumable <Entity> callback, RangeTestDelegate onRangeTest) { int gridXMax = api.World.BlockAccessor.MapSizeX / gridSizeInBlocks - 1; int cyTop = api.World.BlockAccessor.MapSizeY / chunkSize - 1; int gridZMax = api.World.BlockAccessor.MapSizeZ / gridSizeInBlocks - 1; int mingx = (int)GameMath.Clamp((centerPos.X - radius) / gridSizeInBlocks, 0, gridXMax); int maxgx = (int)GameMath.Clamp((centerPos.X + radius) / gridSizeInBlocks, 0, gridXMax); int mincy = (int)GameMath.Clamp((centerPos.Y - radius) / chunkSize, 0, cyTop); int maxcy = (int)GameMath.Clamp((centerPos.Y + radius) / chunkSize, 0, cyTop); int mingz = (int)GameMath.Clamp((centerPos.Z - radius) / gridSizeInBlocks, 0, gridZMax); int maxgz = (int)GameMath.Clamp((centerPos.Z + radius) / gridSizeInBlocks, 0, gridZMax); double radiusSq = radius * radius; long indexBefore = -1; IWorldChunk chunk = null; EntityPartitionChunk partitionChunk = null; for (int gridX = mingx; gridX <= maxgx; gridX++) { int cx = gridX * gridSizeInBlocks / chunkSize; int lgx = gridX % partitionsLength; for (int gridZ = mingz; gridZ <= maxgz; gridZ++) { int cz = gridZ * gridSizeInBlocks / chunkSize; int lgz = gridZ % partitionsLength; lgz = lgz * partitionsLength + lgx; for (int cy = mincy; cy <= maxcy; cy++) { long index3d = MapUtil.Index3dL(cx, cy, cz, chunkMapSizeX, chunkMapSizeZ); if (index3d != indexBefore) { indexBefore = index3d; chunk = api.World.BlockAccessor.GetChunk(cx, cy, cz); if (chunk == null || chunk.Entities == null) { continue; } Partitions.TryGetValue(index3d, out partitionChunk); } else if (chunk == null || chunk.Entities == null) { continue; } if (partitionChunk == null) { continue; } List <Entity> entities = partitionChunk.Entities[lgz]; for (int i = 0; i < entities.Count; i++) { if (onRangeTest(entities[i], centerPos, radiusSq) && !callback(entities[i])) { return; } } } } } }