// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "nel/misc/common.h" #include "game_share/utils.h" #include "move_checker.h" /****************************************************************\ Static Stats Stuff (temp) \****************************************************************/ class CStatsRecord { private: std::vector _Distances; uint32 _Counter; uint32 _LastTick; bool _Teleporting; uint32 _X,_Y; uint32 _MaxDist; uint32 _MaxDistTime; public: CStatsRecord() { _Distances.resize(50); _Counter=0; _LastTick=0; _Teleporting=true; _X=0; _Y=0; _MaxDist=0; _MaxDistTime=(uint32)_Distances.size()/2; } void start(TDataSetRow entityIndex, sint32 x, sint32 y, uint32 tick) { std::fill(_Distances.begin(),_Distances.end(),0); _Counter=0; _LastTick=tick; _Teleporting=true; _X=x; _Y=y; _MaxDist=0; _MaxDistTime=(uint32)_Distances.size()/2; } void add(TDataSetRow entityIndex, sint32 x, sint32 y, uint32 tick) { // if we've just teleported then there's no point worrying about the time since last update... if (_Teleporting) { // flag teleporting as finished _Teleporting= false; } else { // check whether entity has been updated recently // if((tick-_LastTick)!=1) // { // nlwarning("Move Checker Stats: entity %s has only received position update after %d ticks",entityIndex.toString().c_str(),tick-_LastTick); // } } // calculate the distance moved and check whether its a new record uint32 squaredist= (_X-x)*(_X-x) + (_Y-y)*(_Y-y); if (squaredist>_MaxDist) { _MaxDist=squaredist; _MaxDistTime= std::max((uint32)_Counter,(uint32)_Distances.size()/2); } _Distances[_Counter%_Distances.size()]= squaredist; // store away basic info for next time round _X=x; _Y=y; _LastTick=tick; // increment our counter... ++_Counter; // if we've hit a new record recently then display a log message if ( (_Counter-_MaxDistTime) == (_Distances.size()/2) ) { std::string s; for (uint32 i=(uint32)_Distances.size();i!=0;--i) { s+=NLMISC::toString(" %d",_Distances[(_Counter-i)%_Distances.size()]); } nlinfo("SpeedStats record for %s: %d (%.2f m/s): %s",entityIndex.toString().c_str(),_MaxDist,sqrt((double)_MaxDist)/200,s.c_str()); } } }; typedef std::map TStats; TStats Stats; static void startStats(TDataSetRow entityIndex, sint32 x, sint32 y, uint32 tick) { Stats[entityIndex].start(entityIndex,x,y,tick); } static void addStats(TDataSetRow entityIndex, sint32 x, sint32 y, uint32 tick) { Stats[entityIndex].add(entityIndex,x,y,tick); } static void endStats(TDataSetRow entityIndex) { Stats.erase(entityIndex); } /****************************************************************\ CMoveChecker Methods \****************************************************************/ void CMoveChecker::teleport(TDataSetRow entityIndex, sint32 x, sint32 y, uint32 tick) { // get hold of the entity's record in the move checker (and creat a new entry if need be) SPosition& thePosition= _Positions[entityIndex]; nlinfo("Move checker teleporting player: %s from (%d,%d) @tick: %d to (%d,%d) @tick: %d", entityIndex.toString().c_str(),thePosition.X,thePosition.Y,thePosition.Tick,x,y,tick); // record the new position thePosition.X= x; thePosition.Y= y; thePosition.Tick= tick; // generate a few stats startStats(entityIndex,x,y,tick); } bool CMoveChecker::checkMove(TDataSetRow entityIndex, sint32& x, sint32& y, uint32 tick) { // setup a refference to the entity's position record, and bomb if it can't be found TPositions::iterator positionIt= _Positions.find(entityIndex); BOMB_IF(positionIt==_Positions.end(),"Ignoring call to 'checkMove' for an entity who doesn't exist in the move checker",return false); SPosition& thePosition= positionIt->second; // if the character hasn't moved then just return if ( (x==thePosition.X) && (y==thePosition.Y) ) { return true; } // nlinfo("Checking player move: %s from (%d,%d) @tick: %d to (%d,%d) @tick: %d", // entityIndex.toString().c_str(),thePosition.X,thePosition.Y,thePosition.Tick,x,y,tick); // if the tick value is out of order (for instance, after an advanced tp request) then ignore the move DROP_IF(sint32(tick-thePosition.Tick)<0,"Ignoring out of order move for character "+entityIndex.toString(),return false); // *** todo: perform a speed test here // *** todo: perform collision test here // *** NOTE: If move not legal then we need to change values of x and y and return false // record the new position thePosition.X= x; thePosition.Y= y; thePosition.Tick= tick; // generate a few stats addStats(entityIndex,x,y,tick); return true; } void CMoveChecker::remove(TDataSetRow entityIndex) { // remove the entity from our positions map _Positions.erase(entityIndex); // generate a few stats endStats(entityIndex); }