From ee43f5f5f7d4834d3a346beffa38e34e2ede33bc Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 2 Sep 2017 22:14:59 +1000 Subject: [PATCH] Majorly optimise bots movement AABB testing. --- MCGalaxy/Bots/PlayerBot.cs | 140 ++++++++++++++++++++----------- MCGalaxy/Player/PlayerPhysics.cs | 2 +- MCGalaxy/util/Math/AABB.cs | 75 +++++++++++------ 3 files changed, 141 insertions(+), 76 deletions(-) diff --git a/MCGalaxy/Bots/PlayerBot.cs b/MCGalaxy/Bots/PlayerBot.cs index 3c908c968..950861f82 100644 --- a/MCGalaxy/Bots/PlayerBot.cs +++ b/MCGalaxy/Bots/PlayerBot.cs @@ -70,7 +70,7 @@ namespace MCGalaxy { public static void Add(PlayerBot bot, bool save = true) { bot.level.Bots.Add(bot); - bot.GlobalSpawn(); + bot.GlobalSpawn(); if (save) BotsFile.Save(bot.level); } @@ -87,7 +87,7 @@ namespace MCGalaxy { } public static void RemoveAllFromLevel(Level lvl) { - RemoveLoadedBots(lvl, false); + RemoveLoadedBots(lvl, false); BotsFile.Save(lvl); } @@ -113,40 +113,6 @@ namespace MCGalaxy { } } - public static void GlobalUpdatePosition() { - Level[] levels = LevelInfo.Loaded.Items; - for (int i = 0; i < levels.Length; i++) { - PlayerBot[] bots = levels[i].Bots.Items; - for (int j = 0; j < bots.Length; j++) { bots[j].UpdatePosition(); } - } - } - - void UpdatePosition() { - if (movement) { - double scale = Math.Ceiling(ServerConfig.PositionUpdateInterval / 25.0); - int steps = movementSpeed * (int)scale; - for (int i = 0; i < steps; i++) - DoMove(); - } - - Position pos = Pos; Orientation rot = Rot; - if (pos == lastPos && rot.HeadX == lastRot.HeadX && rot.RotY == lastRot.RotY) return; - lastPos = pos; lastRot = rot; - - // TODO: relative position updates, combine packets - byte[] packet = Packet.Teleport(id, pos, rot, false); - byte[] extPacket = Packet.Teleport(id, pos, rot, true); - - Player[] players = PlayerInfo.Online.Items; - foreach (Player p in players) { - if (p.level != level) continue; - - if (p.hasExtPositions) p.Send(extPacket); - else p.Send(packet); - } - } - - unsafe static byte NextFreeId(PlayerBot bot) { byte* used = stackalloc byte[256]; for (int i = 0; i < 256; i++) @@ -189,39 +155,118 @@ namespace MCGalaxy { cur++; if (cur == Instructions.Count) cur = 0; } + - void DoMove() { + public static void GlobalUpdatePosition() { + Level[] levels = LevelInfo.Loaded.Items; + for (int i = 0; i < levels.Length; i++) { + PlayerBot[] bots = levels[i].Bots.Items; + for (int j = 0; j < bots.Length; j++) { bots[j].UpdatePosition(); } + } + } + + void UpdatePosition() { + if (movement) PerformMovement(); + + Position pos = Pos; Orientation rot = Rot; + if (pos == lastPos && rot.HeadX == lastRot.HeadX && rot.RotY == lastRot.RotY) return; + lastPos = pos; lastRot = rot; + + // TODO: relative position updates, combine packets + byte[] packet = Packet.Teleport(id, pos, rot, false); + byte[] extPacket = Packet.Teleport(id, pos, rot, true); + + Player[] players = PlayerInfo.Online.Items; + foreach (Player p in players) { + if (p.level != level) continue; + + if (p.hasExtPositions) p.Send(extPacket); + else p.Send(packet); + } + } + + static AABB[] downs = new AABB[16], ups = new AABB[16]; + static int downsCount, upsCount; + + void RecalcDownExtent(ref AABB bb, int steps, int dx, int dz) { + AABB downExtent = bb.Adjust(dx * steps, -32, dz * steps); + downsCount = AABB.FindIntersectingSolids(downExtent, level, ref downs); + } + + void RecalcUpExtent(ref AABB bb, int steps, int dx, int dz) { + AABB upExtent = bb.Adjust(dx * steps, 32, dz * steps); + upsCount = AABB.FindIntersectingSolids(upExtent, level, ref ups); + } + + void PerformMovement() { + double scale = Math.Ceiling(ServerConfig.PositionUpdateInterval / 25.0); + int steps = movementSpeed * (int)scale; + + downsCount = -1; + for (int i = 0; i < steps; i++) DoMove(steps); + } + + void DoMove(int steps) { Position pos = Pos; AABB bb = ModelBB.OffsetPosition(pos); + int dx = Math.Sign(TargetPos.X - pos.X); + int dz = Math.Sign(TargetPos.Z - pos.Z); + + if (downsCount == -1) { + RecalcDownExtent(ref bb, steps, dx, dz); + RecalcUpExtent(ref bb, steps, dx, dz); + } + // Advance the AABB to the bot's next position - int dx = Math.Sign(TargetPos.X - Pos.X), dz = Math.Sign(TargetPos.Z - Pos.Z); bb = bb.Offset(dx, 0, dz); AABB bbCopy = bb; // Attempt to drop the bot down up to 1 block int hitY = -32; for (int dy = 0; dy >= -32; dy--) { - if (AABB.IntersectsSolidBlocks(bb, level)) { hitY = dy + 1; break; } + bool intersectsAny = false; + for (int i = 0; i < downsCount; i++) { + if (AABB.Intersects(ref bb, ref downs[i])) { intersectsAny = true; break; } + } + + if (intersectsAny) { hitY = dy + 1; break; } bb.Min.Y--; bb.Max.Y--; - } + } // Does the bot fall down a block if (hitY < 0) { - pos.X += dx; pos.Y += hitY; pos.Z += dz; - Pos = pos; return; + pos.X += dx; pos.Y += hitY; pos.Z += dz; Pos = pos; + RecalcDownExtent(ref bb, steps, dx, dz); + RecalcUpExtent(ref bb, steps, dx, dz); + return; } // Attempt to move the bot up to 1 block bb = bbCopy; + for (int dy = 0; dy <= 32; dy++) { - if (!AABB.IntersectsSolidBlocks(bb, level)) { - pos.X += dx; pos.Y += dy; pos.Z += dz; - Pos = pos; return; + bool intersectsAny = false; + for (int i = 0; i < upsCount; i++) { + if (AABB.Intersects(ref bb, ref ups[i])) { intersectsAny = true; break; } + } + + if (!intersectsAny) { + pos.X += dx; pos.Y += dy; pos.Z += dz; Pos = pos; + + if (dy != 0) { + RecalcDownExtent(ref bb, steps, dx, dz); + RecalcUpExtent(ref bb, steps, dx, dz); + } + return; } bb.Min.Y++; bb.Max.Y++; } - - /* + } + + + /* + * Old water/lava swimming code - TODO: need to fix. + * if ((ushort)(foundPos[1] / 32) > y) { if (b1 == Block.water || b1 == Block.waterstill || b1 == Block.lava || b1 == Block.lavastill) { if (Block.Walkthrough(b2)) { @@ -235,6 +280,5 @@ namespace MCGalaxy { pos[1] = (ushort)(pos[1] + (Math.Sign(foundPos[1] - pos[1]))); } }*/ - } } } \ No newline at end of file diff --git a/MCGalaxy/Player/PlayerPhysics.cs b/MCGalaxy/Player/PlayerPhysics.cs index 758c7bf4b..56b7556a3 100644 --- a/MCGalaxy/Player/PlayerPhysics.cs +++ b/MCGalaxy/Player/PlayerPhysics.cs @@ -35,7 +35,7 @@ namespace MCGalaxy.Blocks.Physics { if (block.BlockID == Block.Invalid) continue; AABB blockBB = p.level.blockAABBs[block.Index].Offset(x * 32, y * 32, z * 32); - if (!bb.Intersects(blockBB)) continue; + if (!AABB.Intersects(ref bb, ref blockBB)) continue; // We can activate only one walkthrough block per movement if (!hitWalkthrough) { diff --git a/MCGalaxy/util/Math/AABB.cs b/MCGalaxy/util/Math/AABB.cs index 152e8a3af..846321b4e 100644 --- a/MCGalaxy/util/Math/AABB.cs +++ b/MCGalaxy/util/Math/AABB.cs @@ -61,6 +61,14 @@ namespace MCGalaxy.Maths { return bb; } + public AABB Adjust(int x, int y, int z) { + AABB bb = this; + if (x >= 0) { bb.Max.X += x; } else { bb.Min.X += x; } + if (y >= 0) { bb.Max.Y += y; } else { bb.Min.Y += y; } + if (z >= 0) { bb.Max.Z += z; } else { bb.Min.Z += z; } + return bb; + } + public AABB Expand(int amount) { AABB bb = this; bb.Min.X -= amount; bb.Min.Y -= amount; bb.Min.Z -= amount; @@ -68,38 +76,18 @@ namespace MCGalaxy.Maths { return bb; } - /// Returns a new bounding box, with the minimum and maximum coordinates - /// of the original bounding box scaled away from origin the given value. - public AABB Scale(float scale) { - return new AABB(Min * scale, Max * scale); - } - /// Determines whether this bounding box intersects /// the given bounding box on any axes. - public bool Intersects(AABB other) { - if (Max.X >= other.Min.X && Min.X <= other.Max.X) { - if (Max.Y < other.Min.Y || Min.Y > other.Max.Y) { + public static bool Intersects(ref AABB a, ref AABB b) { + if (a.Max.X >= b.Min.X && a.Min.X <= b.Max.X) { + if (a.Max.Y < b.Min.Y || a.Min.Y > b.Max.Y) { return false; } - return Max.Z >= other.Min.Z && Min.Z <= other.Max.Z; + return a.Max.Z >= b.Min.Z && a.Min.Z <= b.Max.Z; } return false; } - - /// Determines whether this bounding box entirely contains - /// the given bounding box on all axes. - public bool Contains(AABB other) { - return other.Min.X >= Min.X && other.Min.Y >= Min.Y && other.Min.Z >= Min.Z && - other.Max.X <= Max.X && other.Max.Y <= Max.Y && other.Max.Z <= Max.Z; - } - - /// Determines whether this bounding box entirely contains - /// the coordinates on all axes. - public bool Contains(Vec3S32 P) { - return P.X >= Min.X && P.Y >= Min.Y && P.Z >= Min.Z && - P.X <= Max.X && P.Y <= Max.Y && P.Z <= Max.Z; - } - + public override string ToString() { return Min + " : " + Max; } @@ -127,7 +115,7 @@ namespace MCGalaxy.Maths { float maxScale = model.CaselessEq("chibi") ? 3 : 2; if (scale > maxScale) scale = maxScale; - return baseBB.Scale(scale); + return new AABB(baseBB.Min * scale, baseBB.Max * scale); } static Vec3S32 BaseSize(string model) { @@ -154,7 +142,7 @@ namespace MCGalaxy.Maths { ExtBlock block = lvl.GetBlock(xP, yP, zP); AABB blockBB = lvl.blockAABBs[block.Index].Offset(x * 32, y * 32, z * 32); - if (!bb.Intersects(blockBB)) continue; + if (!AABB.Intersects(ref bb, ref blockBB)) continue; BlockDefinition def = lvl.GetBlockDef(block); if (def != null) { @@ -165,5 +153,38 @@ namespace MCGalaxy.Maths { } return false; } + + public static int FindIntersectingSolids(AABB bb, Level lvl, ref AABB[] aabbs) { + Vec3S32 min = bb.BlockMin, max = bb.BlockMax; + int volume = (max.X - min.X + 1) * (max.Y - min.Y + 1) * (max.Z - min.Z + 1); + if (volume > aabbs.Length) aabbs = new AABB[volume]; + int count = 0; + + for (int y = min.Y; y <= max.Y; y++) + for (int z = min.Z; z <= max.Z; z++) + for (int x = min.X; x <= max.X; x++) + { + ExtBlock block = lvl.GetBlock((ushort)x, (ushort)y, (ushort)z); + AABB blockBB = lvl.blockAABBs[block.Index]; + + blockBB.Min.X += x * 32; blockBB.Min.Y += y * 32; blockBB.Min.Z += z * 32; + blockBB.Max.X += x * 32; blockBB.Max.Y += y * 32; blockBB.Max.Z += z * 32; + if (!AABB.Intersects(ref bb, ref blockBB)) continue; + + BlockDefinition def = lvl.GetBlockDef(block); + bool solid = false; + + if (def != null) { + solid = CollideType.IsSolid(def.CollideType); + } else { + solid = block.BlockID == Block.Invalid || !Block.Walkthrough(Block.Convert(block.BlockID)); + } + if (!solid) continue; + + aabbs[count] = blockBB; + count++; + } + return count; + } } } \ No newline at end of file