navmeshes now super lightweight (<3 u devi)

- refactored some stuff
- unhardcoded navmesh loading
- included sharpnav license
This commit is contained in:
Tahir Akhlaq 2017-06-09 04:17:08 +01:00
parent b640c87c69
commit 872e56f8f9
10 changed files with 354 additions and 253 deletions

View file

@ -76,7 +76,8 @@ namespace FFXIVClassic_Map_Server
isInn,
canRideChocobo,
canStealth,
isInstanceRaid
isInstanceRaid,
loadNavMesh
FROM server_zones
WHERE zoneName IS NOT NULL and serverIp = @ip and serverPort = @port";
@ -89,7 +90,8 @@ namespace FFXIVClassic_Map_Server
{
while (reader.Read())
{
Zone zone = new Zone(reader.GetUInt32(0), reader.GetString(1), reader.GetUInt16(2), reader.GetString(3), reader.GetUInt16(4), reader.GetUInt16(5), reader.GetUInt16(6), reader.GetBoolean(7), reader.GetBoolean(8), reader.GetBoolean(9), reader.GetBoolean(10), reader.GetBoolean(11));
Zone zone = new Zone(reader.GetUInt32(0), reader.GetString(1), reader.GetUInt16(2), reader.GetString(3), reader.GetUInt16(4), reader.GetUInt16(5),
reader.GetUInt16(6), reader.GetBoolean(7), reader.GetBoolean(8), reader.GetBoolean(9), reader.GetBoolean(10), reader.GetBoolean(11), reader.GetBoolean(12));
zoneList[zone.actorId] = zone;
count1++;
}

View file

@ -41,7 +41,6 @@ namespace FFXIVClassic_Map_Server.Actors
public List<LuaParam> classParams;
public List<utils.Vector3> positionUpdates = new List<utils.Vector3>();
public DateTime lastAiUpdate;
public DateTime lastMoveUpdate;
public Actor target;
@ -371,6 +370,10 @@ namespace FFXIVClassic_Map_Server.Actors
{
((Character)this).Update(deltaTime);
}
else if (this is Zone)
{
((Zone)this).Update(deltaTime);
}
}
public void GenerateActorName(int actorNumber)
@ -540,6 +543,7 @@ namespace FFXIVClassic_Map_Server.Actors
return zoneId;
}
// todo: do this properly
public bool IsFacing(float x, float y)
{
var rot1 = this.rotation;
@ -554,6 +558,7 @@ namespace FFXIVClassic_Map_Server.Actors
return rot1 == (float)dRot;
}
// todo: do this properly
public bool IsFacing(Actor target)
{
if (target == null)
@ -582,7 +587,7 @@ namespace FFXIVClassic_Map_Server.Actors
}
else
{
Program.Log.Error("{0} {1} Actor.LookAt() unable to find actor!", actorId, actorName);
Program.Log.Error("[{0}][{1}] Actor.LookAt() unable to find actor!", actorId, actorName);
}
}
@ -603,6 +608,36 @@ namespace FFXIVClassic_Map_Server.Actors
rotation = (float)dRot;
}
public void QueuePositionUpdate(utils.Vector3 pos)
{
if (positionUpdates == null)
positionUpdates = new List<utils.Vector3>();
positionUpdates.Add(pos);
this.hasMoved = true;
}
public void QueuePositionUpdate(float x, float y, float z)
{
QueuePositionUpdate(new utils.Vector3(x, y, z));
}
public void ClearPositionUpdates()
{
positionUpdates.Clear();
}
public utils.Vector3 FindRandomPointAroundActor(float minRadius, float maxRadius)
{
var angle = Program.Random.NextDouble() * Math.PI * 2;
var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius;
float x = (float)(radius * Math.Cos(angle));
float z = (float)(radius * Math.Sin(angle));
return new utils.Vector3(positionX + x, positionY, positionZ + z);
}
}
}

View file

@ -24,23 +24,28 @@ namespace FFXIVClassic_Map_Server.actors.area
public SharpNav.TiledNavMesh tiledNavMesh;
public SharpNav.NavMeshQuery navMeshQuery;
public Int64 pathCalls;
public Int64 pathCallTime;
public Zone(uint id, string zoneName, ushort regionId, string classPath, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid)
protected DateTime lastUpdate;
public Zone(uint id, string zoneName, ushort regionId, string classPath, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid, bool loadNavMesh = false)
: base(id, zoneName, regionId, classPath, bgmDay, bgmNight, bgmBattle, isIsolated, isInn, canRideChocobo, canStealth, isInstanceRaid)
{
// central thanalan navmesh
if (id == 170)
var navMeshName = loadNavMesh ? zoneName + ".snb" : "";
if (navMeshName != "")
{
try
{
//navMesh = utils.NavmeshUtils.LoadNavmesh("wil_w0_fld01.bin");
tiledNavMesh = utils.NavmeshUtils.LoadNavmesh(tiledNavMesh, "wil_w0_fld01.snb");
tiledNavMesh = utils.NavmeshUtils.LoadNavmesh(tiledNavMesh, navMeshName);
navMeshQuery = new SharpNav.NavMeshQuery(tiledNavMesh, 100);
GC.Collect(2);
if (tiledNavMesh != null)
Program.Log.Info($"Loaded navmesh for {zoneName}");
}
catch(Exception e)
catch (Exception e)
{
Program.Log.Error(e.Message);
}
@ -161,5 +166,24 @@ namespace FFXIVClassic_Map_Server.actors.area
}
}
public void Update(double deltaTime)
{
// todo: again, this is retarded but debug stuff
var diffTime = DateTime.Now - lastUpdate;
// arbitrary cap
if (diffTime.Milliseconds >= 33)
{
}
if (diffTime.Seconds >= 10)
{
if (this.pathCalls > 0)
{
Program.Log.Error("Number of pathfinding calls {0} average time {1}", pathCalls, pathCallTime / pathCalls);
}
}
}
}
}

View file

@ -60,6 +60,8 @@ namespace FFXIVClassic_Map_Server.Actors
public Group currentParty = null;
public ContentGroup currentContentGroup = null;
public DateTime lastAiUpdate;
public Character(uint actorID) : base(actorID)
{
//Init timer array to "notimer"
@ -150,10 +152,11 @@ namespace FFXIVClassic_Map_Server.Actors
sw.Stop();
((Zone)zone).pathCalls++;
((Zone)zone).pathCallTime += sw.ElapsedMilliseconds;
if (path.Count == 1)
Program.Log.Error($"mypos: {positionX} {positionY} {positionZ} | targetPos: {x} {y} {z} | step {stepSize} | maxPath {maxPath} | polyRadius {polyRadius}");
Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", actorId, actorName, path.Count, sw.ElapsedMilliseconds);
if (path.Count == 1)
Program.Log.Info($"mypos: {positionX} {positionY} {positionZ} | targetPos: {x} {y} {z} | step {stepSize} | maxPath {maxPath} | polyRadius {polyRadius}");
//Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", actorId, actorName, path.Count, sw.ElapsedMilliseconds);
}
}
@ -210,6 +213,11 @@ namespace FFXIVClassic_Map_Server.Actors
public void Update(double deltaTime)
{
// todo: actual ai controllers
// todo: mods to control different params instead of hardcode
// todo: other ai helpers
// time elapsed since last ai update
var diffTime = (DateTime.Now - lastAiUpdate);
if (this is Player)
@ -235,106 +243,89 @@ namespace FFXIVClassic_Map_Server.Actors
{
var spawnDistance = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ);
// despawn if too far from spawn so client can reload me
if (spawnDistance >= 64.4)
{
despawnOutOfRange = true;
if (target != null)
{
var player = target as Player;
// target zoned, deaggro
target = null;
// tell player to despawn us and we can move back to spawn
if (player != null)
{
// make sure we dont tell player to despawn us twice
targId = player.actorId;
//player.QueuePacket(RemoveActorPacket.BuildPacket(player.actorId, actorId));
}
}
this.isMovingToSpawn = true;
this.positionUpdates.Clear();
this.lastMoveUpdate = this.lastMoveUpdate.AddSeconds(-5);
}
// todo: actual spawn leash and modifiers read from table
// set a leash to path back to spawn even if have target
else if (spawnDistance >= 55)
if (spawnDistance >= 55)
{
this.isMovingToSpawn = true;
this.target = null;
this.positionUpdates.Clear();
this.lastMoveUpdate = this.lastMoveUpdate.AddSeconds(-5);
ClearPositionUpdates();
}
}
// check if player
if (target != null && target is Player)
{
var player = target as Player;
// deaggro if zoning/logging
if (player.playerSession.isUpdatesLocked || player.isZoneChanging || player.isZoning)
{
target = null;
ClearPositionUpdates();
}
}
Player closestPlayer = null;
float closestPlayerDistance = 1000.0f;
foreach (var actor in zone.GetActorsAroundActor(this, 65))
// dont bother checking for any in-range players if going back to spawn
if (!this.isMovingToSpawn)
{
if (actor is Player && actor != this)
foreach (var actor in zone.GetActorsAroundActor(this, 65))
{
var player = actor as Player;
// dont despawn again if we already told target to despawn us
if (despawnOutOfRange && player.actorId != targId)
if (actor is Player && actor != this)
{
//player.QueuePacket(RemoveActorPacket.BuildPacket(player.actorId, this.actorId));
continue;
var player = actor as Player;
// skip if zoning/logging
if (player != null && player.isZoning || player.isZoning || player.playerSession.isUpdatesLocked)
continue;
// find distance between self and target
var distance = Utils.Distance(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ);
int maxDistance = player == target ? 27 : 10;
// check target isnt too far
// todo: create cone thing for IsFacing
if (distance <= maxDistance && distance <= closestPlayerDistance && (IsFacing(player) || true))
{
closestPlayerDistance = distance;
closestPlayer = player;
foundActor = true;
}
}
}
// dont aggro if moving to spawn
if (this.isMovingToSpawn)
continue;
// find distance between self and target
var distance = Utils.Distance(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ);
int maxDistance = player == target ? 27 : 10;
// check target isnt too far
// todo: create cone thing for IsFacing
if (distance <= maxDistance && distance <= closestPlayerDistance && (IsFacing(player) || true))
// found a target
if (foundActor)
{
// make sure we're not already moving so we dont spam packets
if (!hasMoved)
{
closestPlayerDistance = distance;
closestPlayer = player;
foundActor = true;
// todo: include model size and mob specific distance checks
if (closestPlayerDistance >= 3)
{
FollowTarget(closestPlayer, 2.4f, 5);
}
// too close, spread out
else if (closestPlayerDistance <= 0.64f)
{
QueuePositionUpdate(target.FindRandomPointAroundActor(0.65f, 0.85f));
}
// we have a target, face them
if (target != null)
{
LookAt(target);
}
}
}
}
if (foundActor)
{
if (!hasMoved)
{
if (closestPlayerDistance >= 3)
{
FollowTarget(closestPlayer, 2.4f, 5);
}
// too close, spread out
else if (closestPlayerDistance <= 0.64f)
{
var minRadius = 0.65f;
var maxRadius = 0.85f;
var angle = Program.Random.NextDouble() * Math.PI * 2;
var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius;
float x = (float)(radius * Math.Cos(angle));
float z = (float)(radius * Math.Sin(angle));
positionUpdates.Add(new utils.Vector3(positionX + x, positionY, positionZ + z));
hasMoved = true;
}
if (target != null)
{
LookAt(target);
}
}
}
// time elapsed since last move update
var diffMove = (DateTime.Now - lastMoveUpdate);
// player disappeared
@ -357,6 +348,7 @@ namespace FFXIVClassic_Map_Server.Actors
// within spawn range, find a random point
else if (diffMove.Seconds >= 15 && !hasMoved)
{
// pick a random point within 10 yalms or spawn
PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.5f, 7, 10.5f);
// face destination
@ -377,6 +369,7 @@ namespace FFXIVClassic_Map_Server.Actors
// todo: this is retarded. actually no it isnt, i didnt deaggro if out of range..
target = null;
}
// update last ai update time to now
lastAiUpdate = DateTime.Now;
}
}

View file

@ -120,8 +120,6 @@ namespace FFXIVClassic_Map_Server.dataobjects
}
// todo: this is retarded (checking moved crap demo added)
bool checkedThisTick = false;
//Add new actors or move
for (int i = 0; i < list.Count; i++)
{
@ -137,17 +135,6 @@ namespace FFXIVClassic_Map_Server.dataobjects
if (actor is Character && !actor.hasMoved)
continue;
// todo: again, this is retarded but debug stuff
var zone = (actors.area.Zone)actor.zone;
if(zone != null && !checkedThisTick)
{
if (zone.pathCalls > 0)
{
checkedThisTick = true;
Program.Log.Error("Number of pathfinding calls {0} average time {1}", zone.pathCalls, zone.pathCallTime / zone.pathCalls);
}
}
var packet = actor.CreatePositionUpdatePacket(playerActor.actorId);
if (packet != null)

View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2013-2016 Robert Rouhani <robert.rouhani@gmail.com> and other contributors (see CONTRIBUTORS file).
SharpNav contains some altered source code from Recast Navigation, Copyright (c) 2009 Mikko Mononen memon@inside.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Binary file not shown.

View file

@ -97,7 +97,7 @@ namespace FFXIVClassic_Map_Server.utils
public static SharpNav.TiledNavMesh LoadNavmesh(TiledNavMesh navmesh, string filePath)
{
var serialiser = new SharpNav.IO.Json.NavMeshJsonSerializer();
return serialiser.Deserialize(filePath);
return serialiser.Deserialize(System.IO.Path.Combine("../../navmesh/", filePath));
//return navmesh = new SharpNav.IO.Json.NavMeshJsonSerializer().Deserialize(filePath);
}
@ -110,7 +110,14 @@ namespace FFXIVClassic_Map_Server.utils
var navMesh = zone.tiledNavMesh;
var navMeshQuery = zone.navMeshQuery;
if (navMesh == null || (startVec.X == endVec.X && startVec.Y == endVec.Y && startVec.Z == endVec.Z && polyRadius == 0.0f))
// no navmesh loaded, run straight to player
if (navMesh == null)
{
return new List<Vector3>() { endVec };
}
// no need to waste cycles finding path to same point
if (startVec.X == endVec.X && startVec.Y == endVec.Y && startVec.Z == endVec.Z && polyRadius == 0.0f)
{
return null;
}
@ -226,7 +233,7 @@ namespace FFXIVClassic_Map_Server.utils
Program.Log.Error(e.Message);
Program.Log.Error("Start pos {0} {1} {2} end pos {3} {4} {5}", startVec.X, startVec.Y, startVec.Z, endVec.X, endVec.Y, endVec.Z);
// todo: probably log this
return new List<Vector3>() { };
return new List<Vector3>() { endVec };
}
return smoothPath;
}