mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-07-25 03:48:23 +02:00
navmeshes now super lightweight (<3 u devi)
- refactored some stuff - unhardcoded navmesh loading - included sharpnav license
This commit is contained in:
parent
b640c87c69
commit
872e56f8f9
10 changed files with 354 additions and 253 deletions
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
22
FFXIVClassic Map Server/navmesh/SHARPNAV_LICENSE
Normal file
22
FFXIVClassic Map Server/navmesh/SHARPNAV_LICENSE
Normal 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.
BIN
FFXIVClassic Map Server/navmesh/wil0Field01.snb
Normal file
BIN
FFXIVClassic Map Server/navmesh/wil0Field01.snb
Normal file
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue