mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
351 lines
12 KiB
C++
351 lines
12 KiB
C++
// Cyphesis Online RPG Server and AI Engine
|
|
// Copyright (C) 2017 Erik Ogenvik
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software Foundation,
|
|
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
|
#ifdef NDEBUG
|
|
#undef NDEBUG
|
|
#endif
|
|
#ifndef DEBUG
|
|
#define DEBUG
|
|
#endif
|
|
|
|
#include "TestBase.h"
|
|
#include "TestWorld.h"
|
|
|
|
#include "server/Ruleset.h"
|
|
#include "server/ServerRouting.h"
|
|
|
|
#include "rules/simulation/Entity.h"
|
|
|
|
#include "common/compose.hpp"
|
|
#include "common/debug.h"
|
|
|
|
#include <Atlas/Objects/Anonymous.h>
|
|
#include <Atlas/Objects/Operation.h>
|
|
#include <Atlas/Objects/SmartPtr.h>
|
|
|
|
#include <cassert>
|
|
#include <rules/PhysicalDomain.h>
|
|
#include <common/TypeNode.h>
|
|
#include <rules/ModeProperty.h>
|
|
#include <rules/simulation/TerrainProperty.h>
|
|
#include <Mercator/BasePoint.h>
|
|
#include <Mercator/Terrain.h>
|
|
#include <rules/PropelProperty.h>
|
|
#include <rules/AngularFactorProperty.h>
|
|
#include <chrono>
|
|
#include <rules/simulation/VisibilityProperty.h>
|
|
|
|
#include "stubs/common/stublog.h"
|
|
|
|
using Atlas::Message::Element;
|
|
using Atlas::Message::ListType;
|
|
using Atlas::Message::MapType;
|
|
using Atlas::Objects::Root;
|
|
using Atlas::Objects::Entity::Anonymous;
|
|
using Atlas::Objects::Entity::RootEntity;
|
|
|
|
using String::compose;
|
|
|
|
|
|
class PhysicalDomainBenchmark : public Cyphesis::TestBase
|
|
{
|
|
protected:
|
|
static long m_id_counter;
|
|
|
|
public:
|
|
PhysicalDomainBenchmark();
|
|
|
|
static long newId();
|
|
|
|
void setup();
|
|
|
|
void teardown();
|
|
|
|
void test_static_entities_no_move();
|
|
|
|
void test_determinism();
|
|
|
|
void test_visibilityPerformance();
|
|
};
|
|
|
|
long PhysicalDomainBenchmark::m_id_counter = 0L;
|
|
|
|
PhysicalDomainBenchmark::PhysicalDomainBenchmark()
|
|
{
|
|
ADD_TEST(PhysicalDomainBenchmark::test_static_entities_no_move);
|
|
ADD_TEST(PhysicalDomainBenchmark::test_determinism);
|
|
ADD_TEST(PhysicalDomainBenchmark::test_visibilityPerformance);
|
|
|
|
}
|
|
|
|
long PhysicalDomainBenchmark::newId()
|
|
{
|
|
return ++m_id_counter;
|
|
}
|
|
|
|
void PhysicalDomainBenchmark::setup()
|
|
{
|
|
m_id_counter = 0;
|
|
}
|
|
|
|
void PhysicalDomainBenchmark::teardown()
|
|
{
|
|
|
|
}
|
|
void PhysicalDomainBenchmark::test_static_entities_no_move()
|
|
{
|
|
|
|
double tickSize = 1.0 / 15.0;
|
|
|
|
TypeNode* rockType = new TypeNode("rock");
|
|
ModeProperty* modePlantedProperty = new ModeProperty();
|
|
modePlantedProperty->set("planted");
|
|
|
|
Entity* rootEntity = new Entity("0", newId());
|
|
TerrainProperty* terrainProperty = new TerrainProperty();
|
|
Mercator::Terrain& terrain = terrainProperty->getData();
|
|
terrain.setBasePoint(0, 0, Mercator::BasePoint(40));
|
|
terrain.setBasePoint(0, 1, Mercator::BasePoint(40));
|
|
terrain.setBasePoint(1, 0, Mercator::BasePoint(10));
|
|
terrain.setBasePoint(1, 1, Mercator::BasePoint(10));
|
|
rootEntity->setProperty("terrain", terrainProperty);
|
|
rootEntity->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
rootEntity->m_location.setBBox(WFMath::AxisBox<3>(WFMath::Point<3>(0, -64, 0), WFMath::Point<3>(64, 64, 64)));
|
|
PhysicalDomain* domain = new PhysicalDomain(*rootEntity);
|
|
|
|
Property<double>* massProp = new Property<double>();
|
|
massProp->data() = 100;
|
|
|
|
std::vector<Entity*> entities;
|
|
|
|
for (size_t i = 0; i < 60; ++i) {
|
|
for (size_t j = 0; j < 60; ++j) {
|
|
long id = newId();
|
|
std::stringstream ss;
|
|
ss << "planted" << id;
|
|
Entity* entity = new Entity(ss.str(), id);
|
|
entity->setProperty("mass", massProp);
|
|
entity->setType(rockType);
|
|
entity->setProperty(ModeProperty::property_name, modePlantedProperty);
|
|
entity->m_location.m_pos = WFMath::Point<3>(i, j, i + j);
|
|
entity->m_location.setBBox(WFMath::AxisBox<3>(WFMath::Point<3>(-0.25f, 0, -0.25f), WFMath::Point<3>(-0.25f, 0.5f, -0.25f)));
|
|
domain->addEntity(*entity);
|
|
entities.push_back(entity);
|
|
}
|
|
}
|
|
|
|
OpVector res;
|
|
|
|
//First tick is setup, so we'll exclude that from time measurement
|
|
domain->tick(tickSize, res);
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
//Inject ticks for two seconds
|
|
for (int i = 0; i < 30; ++i) {
|
|
domain->tick(tickSize, res);
|
|
}
|
|
|
|
std::stringstream ss;
|
|
long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start).count();
|
|
ss << "Average tick duration: " << milliseconds / 30.0 << " ms";
|
|
log(INFO, ss.str());
|
|
ss = std::stringstream();
|
|
ss << "Physics per second: " << (milliseconds / 2.0) / 10.0 << " %";
|
|
log(INFO, ss.str());
|
|
|
|
}
|
|
|
|
void PhysicalDomainBenchmark::test_determinism()
|
|
{
|
|
|
|
double tickSize = 1.0 / 15.0;
|
|
|
|
TypeNode* rockType = new TypeNode("rock");
|
|
|
|
Entity* rootEntity = new Entity("0", newId());
|
|
TerrainProperty* terrainProperty = new TerrainProperty();
|
|
Mercator::Terrain& terrain = terrainProperty->getData();
|
|
terrain.setBasePoint(0, 0, Mercator::BasePoint(40));
|
|
terrain.setBasePoint(0, 1, Mercator::BasePoint(40));
|
|
terrain.setBasePoint(1, 0, Mercator::BasePoint(10));
|
|
terrain.setBasePoint(1, 1, Mercator::BasePoint(10));
|
|
rootEntity->setProperty("terrain", terrainProperty);
|
|
rootEntity->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
rootEntity->m_location.setBBox(WFMath::AxisBox<3>(WFMath::Point<3>(0, -64, 0), WFMath::Point<3>(64, 64, 64)));
|
|
PhysicalDomain* domain = new PhysicalDomain(*rootEntity);
|
|
|
|
Property<double>* massProp = new Property<double>();
|
|
massProp->data() = 100;
|
|
|
|
std::vector<Entity*> entities;
|
|
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
for (size_t j = 0; j < 10; ++j) {
|
|
long id = newId();
|
|
std::stringstream ss;
|
|
ss << "free" << id;
|
|
Entity* freeEntity = new Entity(ss.str(), id);
|
|
freeEntity->setProperty("mass", massProp);
|
|
freeEntity->setType(rockType);
|
|
freeEntity->m_location.m_pos = WFMath::Point<3>(i, j, i + j);
|
|
freeEntity->m_location.setBBox(WFMath::AxisBox<3>(WFMath::Point<3>(-0.25f, 0, -0.25f), WFMath::Point<3>(-0.25f, 0.5f, -0.25f)));
|
|
domain->addEntity(*freeEntity);
|
|
entities.push_back(freeEntity);
|
|
}
|
|
}
|
|
|
|
OpVector res;
|
|
|
|
//First tick is setup, so we'll exclude that from time measurement
|
|
domain->tick(tickSize, res);
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
//Inject ticks for two seconds
|
|
for (int i = 0; i < 30; ++i) {
|
|
domain->tick(tickSize, res);
|
|
}
|
|
std::stringstream ss;
|
|
long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start).count();
|
|
ss << "Average tick duration: " << milliseconds / 30.0 << " ms";
|
|
log(INFO, ss.str());
|
|
ss = std::stringstream();
|
|
ss << "Physics per second: " << (milliseconds / 2.0) / 10.0 << " %";
|
|
log(INFO, ss.str());
|
|
}
|
|
|
|
void PhysicalDomainBenchmark::test_visibilityPerformance()
|
|
{
|
|
|
|
double tickSize = 1.0 / 15.0;
|
|
|
|
TypeNode* rockType = new TypeNode("rock");
|
|
TypeNode* humanType = new TypeNode("human");
|
|
|
|
PropelProperty* propelProperty = new PropelProperty();
|
|
////Move diagonally up
|
|
propelProperty->data() = WFMath::Vector<3>(5, 0, 5);
|
|
|
|
Property<double>* massProp = new Property<double>();
|
|
massProp->data() = 10000;
|
|
|
|
Entity* rootEntity = new Entity("0", newId());
|
|
rootEntity->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
WFMath::AxisBox<3> aabb(WFMath::Point<3>(-512, 0, -512), WFMath::Point<3>(512, 64, 512));
|
|
rootEntity->m_location.setBBox(aabb);
|
|
PhysicalDomain* domain = new PhysicalDomain(*rootEntity);
|
|
|
|
TestWorld testWorld(*rootEntity);
|
|
|
|
ModeProperty* modePlantedProperty = new ModeProperty();
|
|
modePlantedProperty->set("planted");
|
|
|
|
std::vector<Entity*> entities;
|
|
|
|
int counter = 0;
|
|
|
|
auto size = aabb.highCorner() - aabb.lowCorner();
|
|
|
|
for (float i = aabb.lowCorner().x(); i <= aabb.highCorner().x(); i = i + (size.x() / 100.0f)) {
|
|
for (float j = aabb.lowCorner().z(); j <= aabb.highCorner().z(); j = j + (size.z() / 100.0f)) {
|
|
counter++;
|
|
long id = newId();
|
|
std::stringstream ss;
|
|
ss << "planted" << id;
|
|
Entity* plantedEntity = new Entity(ss.str(), id);
|
|
plantedEntity->setProperty(ModeProperty::property_name, modePlantedProperty);
|
|
plantedEntity->setType(rockType);
|
|
plantedEntity->m_location.m_pos = WFMath::Point<3>(i, 0, j);
|
|
plantedEntity->m_location.setBBox(WFMath::AxisBox<3>(WFMath::Point<3>(-0.25f, 0, -0.25f), WFMath::Point<3>(-0.25f, .2f, -0.25f)));
|
|
domain->addEntity(*plantedEntity);
|
|
entities.push_back(plantedEntity);
|
|
}
|
|
}
|
|
|
|
{
|
|
std::stringstream ss;
|
|
ss << "Added " << counter << " planted entities at " << (size.x() / 100.0) << " meter interval.";
|
|
log(INFO, ss.str());
|
|
}
|
|
|
|
int numberOfObservers = 200;
|
|
|
|
std::vector<Entity*> observers;
|
|
for (int i = 0; i < numberOfObservers; ++i) {
|
|
long id = newId();
|
|
std::stringstream ss;
|
|
ss << "observer" << id;
|
|
Entity* observerEntity = new Entity(ss.str(), id);
|
|
observers.push_back(observerEntity);
|
|
observerEntity->m_location.setSolid(false);
|
|
observerEntity->setType(humanType);
|
|
observerEntity->m_location.m_pos = WFMath::Point<3>(aabb.lowCorner().x() + (i * 4), 0, aabb.lowCorner().z());
|
|
observerEntity->m_location.setBBox(WFMath::AxisBox<3>(WFMath::Point<3>(-0.1f, 0, -0.1f), WFMath::Point<3>(0.1, 2, 0.1)));
|
|
observerEntity->setProperty(PropelProperty::property_name, propelProperty);
|
|
observerEntity->addFlags(entity_perceptive);
|
|
observerEntity->setProperty("mass", massProp);
|
|
domain->addEntity(*observerEntity);
|
|
}
|
|
|
|
|
|
OpVector res;
|
|
|
|
//First tick is setup, so we'll exclude that from time measurement
|
|
domain->tick(2, res);
|
|
{
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
//Inject ticks for 20 seconds
|
|
for (int i = 0; i < 15 * 20; ++i) {
|
|
domain->tick(tickSize, res);
|
|
}
|
|
std::stringstream ss;
|
|
long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start).count();
|
|
ss << "Average tick duration with " << numberOfObservers << " moving observers: " << milliseconds / (15. * 20.0) << " ms";
|
|
log(INFO, ss.str());
|
|
ss = std::stringstream();
|
|
ss << "Physics per second with " << numberOfObservers << " moving observers: " << (milliseconds / 20.0) / 10.0 << " %";
|
|
log(INFO, ss.str());
|
|
}
|
|
std::set<LocatedEntity*> transformedEntities;
|
|
//Now stop the observers from moving, and measure again
|
|
for (Entity* observer : observers) {
|
|
domain->applyTransform(*observer, WFMath::Quaternion(), WFMath::Point<3>(), WFMath::Vector<3>::ZERO(), transformedEntities);
|
|
}
|
|
domain->tick(10, res);
|
|
{
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
//Inject ticks for 1 seconds
|
|
for (int i = 0; i < 15; ++i) {
|
|
domain->tick(tickSize, res);
|
|
}
|
|
std::stringstream ss;
|
|
long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start).count();
|
|
ss << "Average tick duration without moving observer: " << milliseconds / 15. << " ms";
|
|
log(INFO, ss.str());
|
|
ss = std::stringstream();
|
|
ss << "Physics per second without moving observer: " << (milliseconds / 1.0) / 10.0 << " %";
|
|
log(INFO, ss.str());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int main()
|
|
{
|
|
PhysicalDomainBenchmark t;
|
|
|
|
return t.run();
|
|
}
|
|
|