Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+114
View File
@@ -0,0 +1,114 @@
#pragma once
#include "design/effector.h"
#include "design/design.h"
#include "util/basic_type.h"
#include "util/hex_grid.h"
class SaveMessage;
enum HexFlags {
HF_Active = 0x1,
HF_Destroyed = 0x2,
HF_NoHP = 0x4,
HF_Gone = 0x8,
HF_NoRepair = 0x10,
};
enum DamageFlags {
DF_DestroyedObject = 0x40000000,
};
namespace net {
struct Message;
};
class CScriptAny;
class Blueprint {
public:
struct HexStatus {
unsigned char flags;
unsigned char hp;
};
struct SysStatus {
unsigned short workingHexes;
EffectStatus status;
};
const Design* design;
HexStatus* hexes;
SysStatus* subsystems;
BasicType* states;
double* effectorStates;
EffectorTarget* effectorTargets;
double currentHP;
double quadrantHP[4];
float removedHP;
double shipEffectiveness;
unsigned statusID;
unsigned short destroyedHexes;
bool designChanged;
bool hpDelta;
bool holdFire;
float hpFactor;
vec2i repairingHex;
CScriptAny** data;
HexStatus* getHexStatus(unsigned index);
HexStatus* getHexStatus(unsigned x, unsigned y);
SysStatus* getSysStatus(unsigned index);
SysStatus* getSysStatus(unsigned x, unsigned y);
CScriptAny* getHookData(unsigned index);
Blueprint();
void destroy(Object* obj);
void preClear();
~Blueprint();
void init(Object* obj);
float think(Object* obj, double time);
void ownerChange(Object* obj, Empire* prevEmpire, Empire* newEmpire);
bool hasTagActive(int index);
double getTagEfficiency(int index, bool ignoreInactive = true);
double getEfficiencySum(int variable, int tag = -1, bool ignoreInactive = true);
double getEfficiencyFactor(int variable, int tag = -1, bool ignoreInactive = true);
bool doesAutoTarget(Object* obj, Object* target);
bool canTarget(Object* obj, Object* target);
void target(Object* obj, Object* target, TargetFlags flags = TF_Target);
void target(Object* obj, unsigned efftrIndex, Object* target, TargetFlags flags = TF_Target);
void target(Object* obj, const Subsystem* sys, Object* target, TargetFlags flags = TF_Target);
void clearTracking(Object* obj);
Object* getCombatTarget();
vec3d getOptimalFacing(int sysVariable, int tag = -1, bool ignoreInactive = true);
void damage(Object* obj, DamageEvent& evt, const vec2u& hex);
void damage_internal(Object* obj, DamageEvent& evt, const vec2u& hex);
void damage(Object* obj, DamageEvent& evt, const vec2u& hex, HexGridAdjacency dir, bool runGlobal);
void damage(Object* obj, DamageEvent& evt, const vec2u& hex, bool runGlobal);
bool globalDamage(Object* obj, DamageEvent& evt);
void damage(Object* obj, DamageEvent& evt, const vec2d& direction);
void damage(Object* obj, DamageEvent& evt, double position, const vec2d& direction);
void damage(Object* obj, DamageEvent& evt, const vec2u& position, const vec2d& endPoint);
void create(Object* obj, const Design* design);
void start(Object* obj, bool fromRetrofit = false);
void retrofit(Object* obj, const Design* design);
double repair(Object* obj, double amount);
double repair(Object* obj, const vec2u& hex, double amount);
void sendDetails(Object* obj, net::Message& msg);
void recvDetails(Object* obj, net::Message& msg);
bool sendDelta(Object* obj, net::Message& msg);
void recvDelta(Object* obj, net::Message& msg);
void save(Object* obj, SaveMessage& file);
void load(Object* obj, SaveMessage& file);
};
+886
View File
@@ -0,0 +1,886 @@
#include "obj/lock.h"
#include "obj/universe.h"
#include "compat/misc.h"
#include "util/random.h"
#include "str_util.h"
#include "main/references.h"
#include "main/logging.h"
#include "network/network_manager.h"
#include "memory/AllocOnlyPool.h"
#include "processing.h"
#include <deque>
#include <assert.h>
#include "empire.h"
#ifdef PROFILE_PROCESSING
namespace processing {
extern Threaded(ProcessingData*) threadProc;
};
#endif
#ifndef LOCK_SWITCH_CHANCE
#define LOCK_SWITCH_CHANCE 0.1
#endif
#ifndef TARGET_UPDATE_INTERVAL
#define TARGET_UPDATE_INTERVAL 0.2
#endif
#ifndef SCRIPT_GC_INTERVAL
#define SCRIPT_GC_INTERVAL 0.05
#endif
threads::atomic_int tickingLocks;
threads::atomic_int remainingMessages, queuedChildren;
std::vector<LockGroup*> lockGroups;
std::deque<LockGroup*> lockQueue;
#ifdef PROFILE_LOCKS
threads::Mutex lockQueueLock("lockQueueLock");
threads::Mutex lockGlobalLock("lockGlobalLock");
threads::Mutex switchedLock("switchedLock");
#else
threads::Mutex lockQueueLock;
threads::Mutex lockGlobalLock;
threads::Mutex switchedLock;
#endif
std::vector<Object*> switchedObjects;
Threaded(LockGroup*) activeLockGroup = 0;
Threaded(Object*) activeObject = 0;
double nextTargetUpdateTime = 0;
double nextScriptGCTime = 0;
double prevTick_s = 0;
threads::Mutex delayedReleaseLock;
std::vector<Object*> delayedReleaseObjects;
void delayObjectRelease(Object* obj) {
threads::Lock lock(delayedReleaseLock);
delayedReleaseObjects.push_back(obj);
}
void performDelayedObjectReleases() {
threads::Lock lock(delayedReleaseLock);
for(auto it = delayedReleaseObjects.begin(), end = delayedReleaseObjects.end(); it != end; ++it)
(*it)->drop();
delayedReleaseObjects.clear();
}
threads::atomic_int switches;
double statTime = 0.0;
bool switchStage = false;
memory::AllocOnlyRegion<threads::Mutex> objMessagePool(4096 * sizeof(void*));
void* ObjectMessage::operator new(size_t bytes) {
return objMessagePool.alloc(bytes);
}
void ObjectMessage::operator delete(void* p) {
objMessagePool.dealloc(p);
}
unsigned getLockCount() {
return (unsigned)lockGroups.size();
}
LockGroup* getLock(unsigned index) {
return lockGroups[index];
}
LockGroup* getActiveLockGroup() {
return activeLockGroup;
}
Object* getActiveObject() {
return activeObject;
}
void setActiveObject(Object* obj) {
activeObject = obj;
}
threads::Mutex clearLock;
std::vector<Object*> queuedClearObjects;
void queueObjectClear(Object* obj) {
threads::Lock lock(clearLock);
obj->grab();
queuedClearObjects.push_back(obj);
}
void handleObjectClears() {
if(queuedClearObjects.empty())
return;
threads::Lock lock(clearLock);
for(auto i = queuedClearObjects.begin(), end = queuedClearObjects.end(); i != end; ++i) {
Object* obj = *i;
obj->clearScripts();
obj->drop();
}
queuedClearObjects.clear();
}
bool printNextLockStats = false;
void printLockStats() {
printNextLockStats = true;
}
LockGroup::LockGroup()
: tickIndex(0) {
#ifdef PROFILE_LOCKS
mutex.name = "LockGroup "+toString(getLockCount());
mutex.observed = true;
addMutex.name = mutex.name+" Add Mutex";
addMutex.observed = true;
#endif
}
bool LockGroup::hasLock() {
return mutex.hasLock();
}
void tickSwitchedObjects(double time) {
switchStage = true;
foreach(it, switchedObjects) {
Object* obj = *it;
ObjectLock olock(obj);
if(obj->isValid()) {
//Only tick if the object wants to be ticked
if(time > obj->nextTick && !obj->getFlag(objStopTicking)) {
activeObject = obj;
double delay = obj->think(time - obj->lastTick);
obj->lastTick = time;
obj->nextTick = time + delay;
}
}
}
switchedObjects.clear();
switchStage = false;
activeObject = nullptr;
}
double lastLockGlobalUpdate = 0.0;
bool updateLockGlobals(double time) {
if(lockGroups.empty() || Object::GALAXY_CREATION)
return false;
if(time < lastLockGlobalUpdate + 0.001)
return false;
lockGlobalLock.lock();
lockQueueLock.lock();
double prevUpdate = lastLockGlobalUpdate;
lastLockGlobalUpdate = time;
if(lockQueue.size() + tickingLocks != 0) {
lockGlobalLock.release();
lockQueueLock.release();
return true;
}
prevTick_s = time - prevUpdate;
lockQueueLock.release();
#ifdef PROFILE_PROCESSING
double startTime = devices.driver->getAccurateTime();
processing::threadProc->globalCount += 1;
processing::threadProc->switchedCount += switchedObjects.size();
#endif
//TODO: This should probably be threaded and not
//block the entire tick cycle.
tickSwitchedObjects(time);
#ifdef PROFILE_PROCESSING
double curTime = devices.driver->getAccurateTime();
processing::threadProc->switchedTime += curTime - startTime;
startTime = curTime;
#endif
bool doGC = nextScriptGCTime < devices.driver->getFrameTime();
if(doGC)
devices.scripts.server->pauseScriptThreads();
processing::pauseMessageHandling();
//Record stats
if(statTime < time) {
statTime = time + 1.0;
for(size_t i = 0, cnt = lockGroups.size(); i < cnt; ++i) {
LockGroup* grp = lockGroups[i];
if(printNextLockStats) {
print("LockGroup %d: %d objects", i, grp->objects.size());
print(" Bad Locks: %d / %d (%.0g%)",
grp->badLocks, (grp->goodLocks + grp->badLocks),
(double)grp->badLocks / (double)(grp->goodLocks
+ grp->badLocks) * 100.0);
print(" Gained %d objects, Lost %d objects.",
grp->gainedObjects, grp->lostObjects);
}
grp->gainedObjects = 0;
grp->lostObjects = 0;
grp->goodLocks = 0;
grp->badLocks = 0;
}
if(printNextLockStats) {
print("Total Switches: %d\n", switches);
printNextLockStats = false;
}
switches = 0;
}
//Update universe children
if(devices.universe)
devices.universe->doQueued();
//Update target system periodically
if(nextTargetUpdateTime < time) {
#ifdef PROFILE_PROCESSING
startTime = devices.driver->getAccurateTime();
processing::threadProc->targetUpdates += 1;
#endif
nextTargetUpdateTime = time + TARGET_UPDATE_INTERVAL;
foreach(it, devices.universe->children) {
Object* obj = *it;
if(obj->isValid())
obj->updateTargets();
}
unsigned childCount = (unsigned)devices.universe->children.size();
for(unsigned i = 0, cnt = (childCount + 255) / 256; i < cnt; ++i)
devices.universe->setRandomTarget(devices.universe->children[randomi(0, childCount-1)]->targets[randomi(0, OBJ_TARGETS-1)]);
#ifdef PROFILE_PROCESSING
curTime = devices.driver->getAccurateTime();
processing::threadProc->targUpdateTime += curTime - startTime;
#endif
}
//We need to process deletion of objects' script classes while the server is paused, or component calls could randomly crash
handleObjectClears();
//Do server script garbage collection here,
//so we don't need a lock around object tick calls
if(doGC) {
nextScriptGCTime = devices.driver->getFrameTime() + SCRIPT_GC_INTERVAL;
#ifdef PROFILE_PROCESSING
double gcStart = devices.driver->getAccurateTime();
#endif
int mode = devices.scripts.server->garbageCollect();
#ifdef PROFILE_PROCESSING
double gcEnd = devices.driver->getAccurateTime();
if(gcEnd >= gcStart + 0.01)
print("Server GC took %dms (mode %d)", (int)((gcEnd - gcStart) * 1000.0), mode);
#endif
// Do delayed object releases here too
performDelayedObjectReleases();
}
processing::resumeMessageHandling();
if(doGC)
devices.scripts.server->resumeScriptThreads();
//Queue up all locks for ticking again
lockQueueLock.lock();
foreach(it, lockGroups)
lockQueue.push_back(*it);
lockQueueLock.release();
lockGlobalLock.release();
return true;
}
void tickRandomMessages(int limit) {
if(lockGroups.empty())
return;
if(activeLockGroup)
throw "Can't lock multiple lock groups.";
unsigned count = lockGroups.size();
unsigned off = randomi(0, count-1);
for(unsigned i = 0; i < count; ++i) {
LockGroup* lock = lockGroups[(i+off) % count];
if(!lock->messages.empty() && !lock->hasLock()) {
devices.scripts.server->threadedCallMutex.readLock();
if(lock->mutex.try_lock()) {
activeLockGroup = lock;
lock->processMessages(limit);
activeLockGroup = 0;
lock->mutex.release();
}
devices.scripts.server->threadedCallMutex.release();
break;
}
}
}
void acquireRandomChildren() {
if(lockGroups.empty())
return;
if(activeLockGroup)
throw "Can't lock multiple lock groups.";
LockGroup* lock = lockGroups[randomi(0,(int)lockGroups.size()-1)];
if(!lock->addQueue.empty()) {
devices.scripts.server->threadedCallMutex.readLock();
lock->acquireChildren();
devices.scripts.server->threadedCallMutex.release();
}
}
void tickLockMessages(LockGroup* lock, int limit) {
if(activeLockGroup)
throw "Can't lock multiple lock groups.";
if(!lock->messages.empty()) {
devices.scripts.server->threadedCallMutex.readLock();
if(lock->mutex.try_lock()) {
activeLockGroup = lock;
lock->processMessages(limit);
activeLockGroup = 0;
lock->mutex.release();
}
devices.scripts.server->threadedCallMutex.release();
}
}
//Find a random lock group that's still queued up for ticking and tick it, or
//update global data and requeue if the queue is empty.
bool tickRandomLock(double time, int limit) {
if(lockQueue.empty()) {
if(tickingLocks == 0)
return updateLockGlobals(time);
else
return false;
}
lockQueueLock.lock();
LockGroup* lock = nullptr;
if(!lockQueue.empty()) {
for(unsigned i = 0, cnt = lockQueue.size(); i < cnt; ++i) {
lock = lockQueue.front();
lockQueue.pop_front();
if(!lock->hasLock())
break;
lockQueue.push_back(lock);
lock = nullptr;
}
if(lock)
++tickingLocks;
}
lockQueueLock.release();
if(!lock)
return false;
#ifdef TRACE_GC_LOCK
devices.scripts.server->markGCImpossible();
#endif
if(!lock->process(time, limit)) {
lockQueueLock.lock();
lockQueue.push_back(lock);
lockQueueLock.release();
}
#ifdef TRACE_GC_LOCK
devices.scripts.server->markGCPossible();
#endif
--tickingLocks;
return true;
}
void LockGroup::addMessage(ObjectMessage* message) {
remainingMessages++;
messageLock.lock();
messages.push_back(message);
messageLock.release();
}
void LockGroup::add(Object* obj) {
obj->grab();
addMutex.lock();
++queuedChildren;
addQueue.push_back(obj);
addMutex.release();
}
void LockGroup::processMessages(int limit) {
while(!messages.empty() && limit--) {
messageLock.lock();
if(messages.empty()) {
messageLock.release();
return;
}
ObjectMessage* msg = messages.front();
messages.pop_front();
messageLock.release();
if(msg->object->lockGroup == this) {
//Only execute messages that are
//actually for this group
Object* prevObj = activeObject;
activeObject = msg->object;
msg->process();
activeObject = prevObj;
delete msg;
remainingMessages--;
}
else {
//Sometimes, an object will have switched
//lock groups before we get to a message
LockGroup* lockGroup = msg->object->lockGroup;
if(lockGroup) {
lockGroup->addMessage(msg);
remainingMessages--;
}
else {
//The object was trashed, we don't care
//about this message anymore
delete msg;
remainingMessages--;
}
}
}
}
void LockGroup::acquireChildren() {
if(addQueue.empty())
return;
lockGroup(this);
addMutex.lock();
int offset = 0;
for(int i = 0, size = (int)addQueue.size(); i < size; ++i) {
Object* obj = addQueue[i];
if(auto* messages = obj->fetchMessages()) {
DeferredObjMessage* prev = nullptr;
while(messages) {
messages->prev = prev;
prev = messages;
messages = messages->next;
}
messages = prev;
while(messages) {
auto* msg = messages;
Object* prevObj = activeObject;
activeObject = msg->msg->object;
msg->msg->process();
activeObject = prevObj;
delete msg->msg;
messages = msg->prev;
delete msg;
}
}
//Remove objects we already have or that are not ours
//any longer.
if(obj->originalLock == this || obj->lockGroup != this) {
++offset;
continue;
}
//Wait for the other group to release it
if(obj->originalLock != 0) {
//Since we're skipping this tick, put it in the switched
//objects queue to tick it.
switchedLock.lock();
switchedObjects.push_back(obj);
switchedLock.release();
if(offset > 0)
addQueue[i - offset] = obj;
continue;
}
//Don't do anything until the object has started ticking
if(obj->getFlag(objStopTicking)) {
if(offset > 0)
addQueue[i - offset] = obj;
continue;
}
//Add object to list
objects.push_back(obj);
obj->originalLock = this;
if(devices.network->isServer && devices.network->hasSyncedClients)
devices.network->sendObject(obj);
++offset;
}
queuedChildren -= offset;
addQueue.resize(addQueue.size() - offset);
addMutex.release();
unlockGroup(this);
}
bool LockGroup::process(double time, int limit) {
#ifdef OBJECT_LOCK_NAGGING
if(activeLockGroup && activeLockGroup != this)
throw "Trying to process a LockGroup with an object locked.";
#endif
lockGroup(this);
if(tickIndex >= objects.size()) {
//Add queued objects
addMutex.lock();
int offset = 0;
for(int i = 0, size = (int)addQueue.size(); i < size; ++i) {
Object* obj = addQueue[i];
if(auto* messages = obj->fetchMessages()) {
DeferredObjMessage* prev = nullptr;
while(messages) {
messages->prev = prev;
prev = messages;
messages = messages->next;
}
messages = prev;
while(messages) {
auto* msg = messages;
Object* prevObj = activeObject;
activeObject = msg->msg->object;
msg->msg->process();
activeObject = prevObj;
delete msg->msg;
messages = msg->prev;
delete msg;
}
}
//Remove objects we already have or that are not ours
//any longer.
if(obj->originalLock == this || obj->lockGroup != this) {
++offset;
continue;
}
//Wait for the other group to release it
if(obj->originalLock != 0) {
//Since we're skipping this tick, put it in the switched
//objects queue to tick it.
switchedLock.lock();
switchedObjects.push_back(obj);
switchedLock.release();
if(offset > 0)
addQueue[i - offset] = obj;
continue;
}
//Don't do anything until the object has started ticking
if(obj->getFlag(objStopTicking)) {
if(offset > 0)
addQueue[i - offset] = obj;
continue;
}
//Add object to list
objects.push_back(obj);
obj->originalLock = this;
if(devices.network->isServer && devices.network->hasSyncedClients)
devices.network->sendObject(obj);
++offset;
}
queuedChildren -= offset;
addQueue.resize(addQueue.size() - offset);
addMutex.release();
//Reset ticking
tickIndex = 0;
}
#ifdef PROFILE_PROCESSING
double procStart;
int procType = -1;
int procCount = 0;
#endif
processMessages();
bool isNetServer = devices.network->isServer;
//Tick objects
int i = 0;
unsigned char rnd = (unsigned char)randomi();
while(tickIndex < objects.size()) {
//Caller can limit amount of ticked objects
if(i >= limit)
break;
//Break when a priority lock is detected
if(priorityLocks.get() > 0)
break;
//Tick object
Object* obj = objects[tickIndex];
if(obj->lockGroup != this) {
//Ignore locks that have switched already
obj->originalLock = 0;
objects[tickIndex] = objects.back();
objects.pop_back();
}
else if(!obj->isValid()) {
//Remove invalid objects from the list
obj->originalLock = 0;
obj->drop();
objects[tickIndex] = objects.back();
objects.pop_back();
}
else {
//Only tick if the object wants to be ticked
if((time > obj->nextTick || obj->getFlag(objWakeUp)) && !obj->getFlag(objStopTicking)) {
#ifdef PROFILE_PROCESSING
if(obj->type->id != procType) {
double curTime = devices.driver->getAccurateTime();
if(procType != -1) {
processing::threadProc->measureType(
procType,
curTime - procStart,
procCount);
}
procStart = curTime;
procType = obj->type->id;
procCount = 0;
}
++procCount;
#endif
if(obj->deferredMessages) {
if(auto* messages = obj->fetchMessages()) {
DeferredObjMessage* prev = nullptr;
while(messages) {
messages->prev = prev;
prev = messages;
messages = messages->next;
}
messages = prev;
while(messages) {
auto* msg = messages;
Object* prevObj = activeObject;
activeObject = msg->msg->object;
msg->msg->process();
activeObject = prevObj;
delete msg->msg;
messages = msg->prev;
delete msg;
}
}
}
activeObject = obj;
double delay = obj->think(time - obj->lastTick);
rnd ^= (unsigned char)obj->id;
delay *= 0.85f + ((float)rnd / 255.f) * 0.15f;
obj->lastTick = time;
obj->nextTick = time + delay;
if(isNetServer)
devices.network->sendObjectDelta(obj);
++i;
if(!messages.empty())
processMessages();
//We need to sleep periodically to guarantee progress at high cpu utilization
if(i % 1024 == 0) {
unlockGroup(this);
threads::sleep(1);
lockGroup(this);
}
}
++tickIndex;
}
}
#ifdef PROFILE_PROCESSING
if(procType != -1) {
double curTime = devices.driver->getAccurateTime();
processing::threadProc->measureType(
procType,
curTime - procStart,
procCount);
}
#endif
unlockGroup(this);
activeObject = nullptr;
return tickIndex >= objects.size();
}
LockGroup* getRandomLock() {
assert(!lockGroups.empty());
return lockGroups[randomi(0, (int)lockGroups.size() - 1)];
}
void initLocks(unsigned lockCount) {
assert(tickingLocks == 0);
lockQueueLock.lock();
for(unsigned i = 0; i < lockCount; ++i) {
LockGroup* lock = new LockGroup();
lock->id = i;
lockGroups.push_back(lock);
lockQueue.push_back(lock);
}
lockQueueLock.release();
}
void destroyLocks() {
foreach(it, lockGroups)
delete *it;
lockGroups.clear();
lockQueue.clear();
}
LockGroup* lockObject(Object* obj, bool priority) {
LockGroup* other;
if(!obj)
return 0;
changedGroup:
other = obj->lockGroup;
if(!other)
return 0;
lockGroup(other, priority);
if(obj->lockGroup != other) {
unlockGroup(other);
goto changedGroup;
}
return other;
//TODO: Reimplement this for secondary locks
//if(other == activeLockGroup) {
//++other->goodLocks;
//return 0;
//}
//if(activeLockGroup)
//++other->badLocks;
//else
//++other->goodLocks;
////Chance to switch locks
//if(write && activeLockGroup) {
//if(randomf() < LOCK_SWITCH_CHANCE) {
//++switches;
//++obj->lockGroup->lostObjects;
//++activeLockGroup->gainedObjects;
//if(activeLockGroup != obj->originalLock)
//activeLockGroup->add(obj);
//obj->lockGroup = activeLockGroup;
////Queue a tick if this is the first switch
//if(!switchStage && obj->originalLock == other) {
//switchedLock.lock();
//switchedObjects.push_back(obj);
//switchedLock.release();
//}
//other->mutex.release();
//return 0;
//}
//}
}
void lockError(const char* ch) {
if(scripts::getActiveManager())
scripts::throwException(ch);
else
throw ch;
}
inline void doLock(LockGroup* group, bool priority) {
if(priority)
++group->priorityLocks;
group->mutex.lock();
if(priority)
--group->priorityLocks;
}
void lockGroup(LockGroup* group, bool priority) {
//Check if we already have the group locked
if(activeLockGroup) {
if(group == activeLockGroup) {
group->mutex.lock();
}
else {
#ifdef OBJECT_LOCK_NAGGING
lockError("Cannot lock object with another object already locked.");
return;
#else
//Compatibility. This is a potential deadlock, but silently
//do it anyway if not in nagging mode.
doLock(group, priority);
#endif
}
}
else {
activeLockGroup = group;
doLock(group, priority);
}
}
inline void _unlockGroup(LockGroup* lock) {
if(lock) {
lock->mutex.release();
if(!lock->hasLock()) {
if(lock == activeLockGroup)
activeLockGroup = 0;
}
}
}
void unlockObject(LockGroup* lock) {
_unlockGroup(lock);
}
void unlockGroup(LockGroup* lock) {
_unlockGroup(lock);
}
bool hasQueuedChildren() {
return queuedChildren != 0;
}
bool hasRemainingMessages() {
return remainingMessages != 0;
}
+83
View File
@@ -0,0 +1,83 @@
#pragma once
#include "threads.h"
#include "obj/object.h"
#include <set>
#include <queue>
#define OBJECT_LOCK_NAGGING
struct ObjectMessage;
struct LockGroup {
int id;
threads::Mutex mutex;
threads::atomic_int priorityLocks;
std::vector<Object*> objects;
unsigned tickIndex;
bool hasLock();
bool hasWriteLock();
std::vector<Object*> addQueue;
threads::Mutex addMutex;
std::deque<ObjectMessage*> messages;
std::unordered_map<Object*, std::vector<ObjectMessage*>*> deferredMessages;
threads::Mutex messageLock;
threads::atomic_int badLocks;
threads::atomic_int goodLocks;
threads::atomic_int lostObjects;
threads::atomic_int gainedObjects;
LockGroup();
void addMessage(ObjectMessage* message);
void add(Object* obj);
bool process(double time, int limit = 1000);
void acquireChildren();
void processMessages(int limit = 100);
};
struct ObjectMessage {
Object* object;
ObjectMessage(Object* obj) : object(obj) {}
virtual void process() = 0;
virtual ~ObjectMessage() {}
void* operator new(size_t bytes);
void operator delete(void* p);
};
LockGroup* getRandomLock();
void initLocks(unsigned lockCount);
void destroyLocks();
unsigned getLockCount();
LockGroup* getLock(unsigned index);
void printLockStats();
void acquireRandomChildren();
bool tickRandomLock(double time, int limit = 1000);
void tickRandomMessages(int limit = 100);
//Attempts to process the messages in a particular group, with no guarantee of any success
void tickLockMessages(LockGroup* lock, int limit = 8);
//If a message can't be processed yet (the object has no lock group), it must be queued for later execution
void queueDeferredMessage(ObjectMessage* msg);
LockGroup* getActiveLockGroup();
Object* getActiveObject();
void setActiveObject(Object* obj);
void queueObjectClear(Object* obj);
void delayObjectRelease(Object* obj);
LockGroup* lockObject(Object* obj, bool priority = false);
void unlockObject(LockGroup* lock);
void lockGroup(LockGroup* group, bool priority = false);
void unlockGroup(LockGroup* lock);
bool hasQueuedChildren();
bool hasRemainingMessages();
+263
View File
@@ -0,0 +1,263 @@
#include "obj_group.h"
#include "object.h"
#include "physics/physics_world.h"
#include "main/references.h"
#include "main/logging.h"
#include "util/save_file.h"
#include <map>
threads::ReadWriteMutex groupIDsLock;
threads::atomic_int nextGroupID(1);
std::map<int,ObjectGroup*> groups;
void registerObjectGroup(ObjectGroup* group, int id = -1) {
groupIDsLock.writeLock();
group->grab();
if(id == -1) {
group->id = nextGroupID++;
}
else {
group->id = id;
nextGroupID = id+1;
}
groups[group->id] = group;
groupIDsLock.release();
}
void unregisterObjectGroup(ObjectGroup* group) {
groupIDsLock.writeLock();
groups.erase(group->id);
group->drop();
groupIDsLock.release();
}
ObjectGroup* ObjectGroup::byID(int id) {
ObjectGroup* group = 0;
groupIDsLock.readLock();
auto iter = groups.find(id);
if(iter != groups.end())
group = iter->second;
if(group)
group->grab();
groupIDsLock.release();
return group;
}
ObjectGroup::ObjectGroup(unsigned count, int id) : objectCount(count), origObjectCount(count), objects(new Object*[count]), physItem(new PhysicsItem), owner(0) {
PhysicsGroup* physGroup = new PhysicsGroup;
physGroup->itemCount = count;
physGroup->items = new PhysicsItem[count];
physItem->type = PhysItemType(PIT_Group | PIT_Object);
physItem->group = physGroup;
memset(objects, 0, sizeof(Object*) * count);
registerObjectGroup(this, id);
}
bool ObjectGroup::postLoad() {
//Remove invalid objects
unsigned off = 0;
for(unsigned i = 0; i < objectCount; ++i) {
Object* obj = objects[i];
if(!obj->isValid() || !obj->isInitialized()) {
physItem->group->items[i].object = 0;
++off;
}
else {
if(off != 0)
objects[i-off] = obj;
}
obj->drop();
}
objectCount -= off;
//Check if the group should die
if(objectCount == 0) {
owner = nullptr;
return false;
}
//Check if the owner is valid
if(!owner->isValid() || !owner->isInitialized())
owner = objects[0];
return true;
}
void ObjectGroup::postInit() {
//Update physics item
physItem->bound.reset(objects[0]->physItem->bound);
for(unsigned i = 1; i < objectCount; ++i)
physItem->bound.addBox(objects[i]->physItem->bound);
devices.physics->registerItem(*physItem);
}
ObjectGroup::~ObjectGroup() {
devices.physics->unregisterItem(*physItem);
delete[] physItem->group->items;
delete physItem;
delete[] objects;
}
unsigned ObjectGroup::getObjectCount() const {
return objectCount;
}
unsigned ObjectGroup::getMaxObjectCount() const {
return origObjectCount;
}
Object* ObjectGroup::getObject(unsigned index) {
return objects[index];
}
const Object* ObjectGroup::getObject(unsigned index) const {
return objects[index];
}
void ObjectGroup::setObject(unsigned index, Object* obj) {
objects[index] = obj;
if(index == 0 && owner == 0)
owner = obj;
if(obj)
obj->grab();
}
unsigned ObjectGroup::setNextObject(Object* obj) {
for(unsigned i = 0; i < objectCount; ++i) {
if(objects[i] != 0)
continue;
objects[i] = obj;
if(i == 0 && owner == nullptr)
owner = obj;
return i;
}
return 0;
}
Object* ObjectGroup::getOwner() {
return owner;
}
void ObjectGroup::setOwner(Object* obj) {
owner = obj;
}
vec3d ObjectGroup::getCenter() const {
return physItem->bound.getCenter();
}
PhysicsItem* ObjectGroup::getPhysicsItem() {
return physItem;
}
PhysicsItem* ObjectGroup::getPhysicsItem(unsigned index) const {
return &physItem->group->items[index];
}
bool ObjectGroup::removeOwner() {
Object* newOwner = 0;
double nearest = 9.0e40;
//Move objects down to the lower indices, and find the nearest object to the previous owner to be the new owner
unsigned j = 0;
for(unsigned i = 0; i < objectCount; ++i) {
Object*& obj = objects[i];
if(!obj) {
++j;
}
else if(obj->isValid() && obj != owner) {
double dist = owner->position.distanceToSQ(obj->position);
if(dist < nearest) {
nearest = dist;
newOwner = obj;
}
if(j != i) {
objects[j] = obj;
obj = 0;
}
++j;
}
}
objectCount = j;
if(objectCount != 0) {
if(newOwner)
owner = newOwner;
else
owner = objects[0];
return false;
}
else {
owner = 0;
unregisterObjectGroup(this);
drop();
return true;
}
}
void ObjectGroup::update() {
AABBoxd box;
box.reset(owner->physItem->bound);
unsigned j = 1;
for(unsigned i = 1; i < objectCount; ++i) {
if(!objects[i]) {
++j;
} else if(objects[i]->isValid()) {
box.addBox(objects[i]->physItem->bound);
if(j != i) {
objects[j] = objects[i];
objects[i] = 0;
}
++j;
}
}
objectCount = j;
devices.physics->updateItem(*physItem, box);
}
ObjectGroup::ObjectGroup(SaveFile& file) {
file >> id >> objectCount >> formationFacing;
objects = new Object*[objectCount];
for(unsigned i = 0; i < objectCount; ++i)
file >> objects[i];
owner = objects[(unsigned short)file];
PhysicsGroup* physGroup = new PhysicsGroup;
physGroup->itemCount = objectCount;
physGroup->items = new PhysicsItem[objectCount];
physItem = new PhysicsItem;
physItem->type = PhysItemType(PIT_Group | PIT_Object);
physItem->group = physGroup;
registerObjectGroup(this);
}
void ObjectGroup::save(SaveFile& file) {
file << id;
file << objectCount;
file << formationFacing;
unsigned ownerIndex = 0;
for(unsigned i = 0; i < objectCount; ++i) {
file << objects[i];
if(objects[i] == owner)
ownerIndex = i;
}
file << (unsigned short)ownerIndex;
}
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include "util/refcount.h"
#include "quaternion.h"
class Object;
class SaveFile;
struct PhysicsItem;
class ObjectGroup : public AtomicRefCounted {
PhysicsItem* physItem;
unsigned objectCount, origObjectCount;
Object** objects;
Object* owner;
public:
int id;
quaterniond formationFacing;
ObjectGroup(unsigned count, int id = -1);
bool postLoad();
void postInit();
ObjectGroup(SaveFile& file);
void save(SaveFile& file);
~ObjectGroup();
unsigned getObjectCount() const;
unsigned getMaxObjectCount() const;
Object* getObject(unsigned index);
const Object* getObject(unsigned index) const;
void setObject(unsigned index, Object* obj);
unsigned setNextObject(Object* obj);
Object* getOwner();
void setOwner(Object* obj);
vec3d getCenter() const;
PhysicsItem* getPhysicsItem();
PhysicsItem* getPhysicsItem(unsigned index) const;
//Removes the owner; Returns true if the group is now empty
bool removeOwner();
void update();
//Note: grabs the group before returning it
static ObjectGroup* byID(int id);
};
File diff suppressed because it is too large Load Diff
+325
View File
@@ -0,0 +1,325 @@
#pragma once
#include "vec3.h"
#include "vec2.h"
#include "quaternion.h"
#include "line3d.h"
#include "util/refcount.h"
#include "util/link_container.h"
#include "threads.h"
#include "general_states.h"
#include "util/random.h"
#include <vector>
#include <string>
#ifndef OBJ_TARGETS
#define OBJ_TARGETS 3
#endif
const unsigned ObjectTypeBitOffset = 26;
const unsigned ObjectTypeMask = (unsigned)(0xFF << ObjectTypeBitOffset);
const unsigned ObjectIDMask = 0xFFFFFFFF >> (32 - ObjectTypeBitOffset);
extern unsigned ObjectTypeCount;
class Object;
class ObjectGroup;
struct PhysicsItem;
struct ObjectMessage;
class asIScriptFunction;
class asIScriptObject;
class asITypeInfo;
class SaveFile;
enum ScriptObjectCallback {
SOC_init,
SOC_postInit,
SOC_destroy,
SOC_groupDestroyed,
SOC_tick,
SOC_ownerChange,
SOC_damage,
SOC_repair,
SOC_syncInitial,
SOC_syncDetailed,
SOC_syncDelta,
SOC_recvInitial,
SOC_recvDetailed,
SOC_recvDelta,
SOC_save,
SOC_load,
SOC_postLoad,
SOC_COUNT
};
struct ScriptObjectType {
unsigned id;
std::string name;
std::string script;
mutable threads::atomic_int nextID;
asITypeInfo* scriptType;
asIScriptFunction* functions[SOC_COUNT];
const StateDefinition* states;
std::vector<size_t> componentOffsets;
std::vector<bool> optionalComponents;
size_t blueprintOffset;
scripts::GenericType* refType;
scripts::GenericType* handleType;
ScriptObjectType();
void bind(const char* module, const char* decl);
asIScriptObject* create() const;
};
void prepScriptObjectTypes();
void addObjectStateValueTypes();
void setScriptObjectStates();
void bindScriptObjectTypes();
ScriptObjectType* getScriptObjectType(const std::string& name);
ScriptObjectType* getScriptObjectType(int index);
unsigned getScriptObjectTypeCount();
//Retrieve the object referred to by the given id.
// Grabs the object before returning it, so it needs
// to be dropped afterwards
Object* getObjectByID(unsigned id, bool create = false);
ScriptObjectType* getObjectTypeFromID(unsigned id);
void invalidateUninitializedObjects();
enum ObjectFlag : unsigned {
//Object is not in the process of being deleted
objValid = 0x1,
//Object needs to go to sleep (going invalid)
objStopTicking = 0x2,
//Object has something to do and should ignore timeout delay
objWakeUp = 0x4,
//Object has been allocated as a reference-only object, and does not yet have data
objUninitialized = 0x8,
//Object has been flagged to send a data delta the next time it can
objSendDelta = 0x10,
//Object has been flagged as a focus object, this has various
//effects for interpolation and multiplayer
objFocus = 0x20,
//Object is currently engaged in combat
objEngaged = 0x40,
//Object does not have physics
objNoPhysics = 0x80,
//Object is selected
objSelected = 0x100,
//Object is considered as in combat
objCombat = 0x200,
//Multiplayer clients should be able to get information even out of vision
objMemorable = 0x400,
//Whether it has been given a name
objNamed = 0x800,
//Whether the object should nudge movable objects (enforced by scripts)
objNoCollide = 0x1000,
//Whether the object can not receive damage by being hit by projectiles
objNoDamage = 0x2000,
objQueueDestroy = 0x80000000
};
const unsigned objFlagSaveMask = ~(objSelected);
namespace net {
struct Message;
};
namespace scene {
class Node;
};
class Empire;
struct LockGroup;
class TimedEffect;
class DamageEvent;
struct DeferredObjMessage {
DeferredObjMessage* next, *prev;
ObjectMessage* msg;
};
class Object {
public:
static bool GALAXY_CREATION;
mutable threads::atomic_int references;
heldPointer<Object> targets[OBJ_TARGETS];
//Object tree
unsigned id;
ObjectGroup* group;
//Locking
LockGroup* lockGroup;
LockGroup* originalLock;
unsigned lockHint;
//Messages
DeferredObjMessage* deferredMessages;
void queueDeferredMessage(ObjectMessage* msg);
DeferredObjMessage* fetchMessages();
//Ownership
Empire* owner;
//Vision
bool alwaysVisible;
float sightRange;
float seeableRange;
unsigned char visionTimes[32];
unsigned visibleMask, donatedVision, prevVisibleMask, sightedMask, sightDelay;
//State variables
threads::atomic_int flags;
//Object spatial variables
PhysicsItem* physItem;
vec3d position, velocity, acceleration;
quaterniond rotation;
double radius;
//The last time this Object ran its think()
// - the parent is responsible for updating this
double lastTick;
double nextTick;
//Effects
std::vector<TimedEffect*> effects;
void addTimedEffect(const TimedEffect& eff);
//Generic stats
LinkMap stats;
//Graphics tree
scene::Node* node;
std::string name;
asIScriptObject* script;
const ScriptObjectType* type;
void init();
void postInit();
void setOwner(Empire* newOwner);
bool isLocked() const;
static Object* create(ScriptObjectType* type, LockGroup* lock = 0, int id = 0);
void grab() const;
void drop() const;
inline const ScriptObjectType* GetType() const { return type; }
void setFlag(ObjectFlag flag, bool val);
bool getFlag(ObjectFlag flag) const;
//Like set flag, but only succeeds if this call set the flag to that value
bool setFlagSecure(ObjectFlag flag, bool val);
//Flag access wrappers
bool isValid() const;
bool isInitialized() const;
void wake();
void sleep(double seconds);
//Focus is an object property to indicate
//priority syncing and calculations.
//The focus flag decays after a while and needs to
//be set repeatedly on accessed objects.
bool isFocus() const;
void focus();
bool isVisibleTo(Empire* emp) const;
bool isKnownTo(Empire* emp) const;
unsigned updateVision(Object* target, unsigned depth);
//Damage events
void damage(DamageEvent& evt, double position, const vec2d& direction);
void repair(double amount);
//Targeting
void updateTargets();
template<class T>
bool findTargets(T& cb, int depth) {
for(unsigned i = 0; i < OBJ_TARGETS; ++i) {
Object* targ = targets[i].ptr;
if(targ == this)
continue;
if(cb.result(targ))
return true;
if(depth != 0) {
if(targ->findTargets(cb, depth-1))
return true;
}
}
return false;
}
static const unsigned char RANDOMIZE_TARGETS = 0;
template<class T>
bool findTargets(T& cb, int depth, unsigned char randomizer) {
if(randomizer == RANDOMIZE_TARGETS)
randomizer = (unsigned char)randomi();
else
randomizer ^= (unsigned char)id;
unsigned index = randomizer % OBJ_TARGETS;
for(unsigned i = 0; i < OBJ_TARGETS; ++i, index = (index+1) % OBJ_TARGETS) {
Object* targ = targets[index].ptr;
if(targ == this)
continue;
if(cb.result(targ))
return true;
if(depth != 0) {
if(targ->findTargets(cb, depth-1, randomizer))
return true;
}
}
return false;
}
//Network syncing
void sendInitial(net::Message& msg);
void sendDetailed(net::Message& msg);
bool sendDelta(net::Message& msg);
void recvDetailed(net::Message& msg, double fromTime);
void recvDelta(net::Message& msg, double fromTime);
//Saving & loading
void save(SaveFile& file);
void load(SaveFile& file);
void postLoad();
//Management
Object(ScriptObjectType* Type, LockGroup* group = 0, int id = 0);
~Object();
double think(double seconds);
void flagDestroy() { setFlag(objQueueDestroy, true); setFlag(objWakeUp, true); }
friend class Universe;
void clearScripts();
private:
void destroy(bool fromUniverse = false);
};
Object* recvObjectInitial(net::Message& msg, double fromTime);
void clearObjects();
struct ObjectLock {
LockGroup* group;
bool released;
ObjectLock(Object* Obj, bool priority = false);
void release();
~ObjectLock();
};
+157
View File
@@ -0,0 +1,157 @@
#include "object.h"
#include "obj_group.h"
#include "util/save_file.h"
#include "empire.h"
#include "physics/physics_world.h"
#include "main/references.h"
#include "main/logging.h"
#include "network/message.h"
#include "lock.h"
//TODO: Handle save files with different script type definitions
void Object::save(SaveFile& file) {
file << id;
file << name;
file << (flags & objFlagSaveMask);
file << alwaysVisible << sightRange << visibleMask << sightedMask;
file << position << velocity << acceleration << rotation;
file << radius;
file << seeableRange;
file << lockHint;
file << (group ? group->id : int(0));
file << (owner ? owner->id : INVALID_EMPIRE);
if(!isValid())
return;
auto* func = type->functions[SOC_save];
if(func) {
SaveMessage msg(file);
scripts::Call cl = devices.scripts.server->call(func);
cl.setObject(script);
cl.push((void*)this);
cl.push((void*)&msg);
if(cl.call()) {
char* pData; net::msize_t size;
msg.getAsPacket(pData, size);
file << size;
file.write(pData, size);
}
else {
file << (net::msize_t)0;
}
}
else {
file << (net::msize_t)0;
}
}
void Object::load(SaveFile& file) {
if(!getFlag(objUninitialized))
throw SaveFileError("Object already initialized");
file >> name;
file >> flags;
file >> alwaysVisible >> sightRange >> visibleMask >> sightedMask;
file >> position >> velocity >> acceleration >> rotation;
file >> radius;
if(file < SFV_0001) {
double dummy;
file >> dummy;
}
if(file >= SFV_0017)
file >> seeableRange;
if(file >= SFV_0009) {
file >> lockHint;
if(lockHint > 0)
lockGroup = getLock(lockHint % getLockCount());
}
int groupID = file;
if(groupID != 0 && isValid()) {
group = ObjectGroup::byID(groupID);
if(group) {
for(unsigned i = 0; i < group->getObjectCount(); ++i) {
if(group->getObject(i) == this) {
physItem = group->getPhysicsItem(i);
physItem->bound = AABBoxd::fromCircle(position, radius);
physItem->gridLocation = 0;
physItem->type = PIT_Object;
physItem->object = this;
break;
}
}
if(!physItem) {
error("%s (#%d) missing in group", name.c_str(), id);
group->drop();
group = nullptr;
}
}
else {
error("%s (#%d) missing group", name.c_str(), id);
}
}
owner = Empire::getEmpireByID(file);
if(!isValid())
return;
if(!physItem && !getFlag(objNoPhysics))
physItem = devices.physics->registerItem(AABBoxd::fromCircle(position, radius), this);
void* mixinMem = this + 1;
type->states->prepare(mixinMem);
script = type->create();
auto* loadFunc = type->functions[SOC_load];
SaveMessage scriptTypeData(file);
net::msize_t size = file;
if(size > 0) {
char* buffer = (char*)malloc(size);
file.read(buffer, size);
scriptTypeData.setPacket(buffer, size);
free(buffer);
}
//Either load the saved message, or default initialize
if(loadFunc) {
scripts::Call cl = devices.scripts.server->call(loadFunc);
cl.setObject(script);
cl.push((void*)this);
cl.push((void*)&scriptTypeData);
cl.call();
}
else {
scripts::Call cl = devices.scripts.server->call(type->functions[SOC_init]);
if(cl.valid()) {
cl.setObject(script);
cl.push((void*)this);
cl.call();
}
}
lockGroup->add(this);
setFlag(objUninitialized, false);
}
void Object::postLoad() {
auto* func = type->functions[SOC_postLoad];
if(func && script) {
scripts::Call cl = devices.scripts.server->call(func);
cl.setObject(script);
cl.push((void*)this);
cl.call();
}
}
+176
View File
@@ -0,0 +1,176 @@
#include "obj/universe.h"
#include "empire.h"
#include "compat/misc.h"
#include "util/random.h"
#include "main/references.h"
#include "network/network_manager.h"
#include "physics/physics_world.h"
#include <assert.h>
#include <unordered_set>
void Universe::doQueued() {
threads::Lock qlock(queueLock);
threads::WriteLock lock(childLock);
//Remove queued objects
unsigned offset = 0;
if(!removeQueue.empty()) {
for(unsigned i = 0, cnt = (unsigned)children.size(); i < cnt; ++i) {
Object* child = children[i];
if(removeQueue.find(child) != removeQueue.end()) {
++offset;
removeTargetsTo(child); //TODO: better
child->drop();
}
else if(offset != 0) {
children[i - offset] = children[i];
}
}
if(offset != 0)
children.resize(children.size() - (size_t)offset);
removeQueue.clear();
}
//Add queued objects
foreach(it, addQueue)
children.push_back(*it);
addQueue.clear();
}
void Universe::addChild(Object* obj) {
threads::Lock qlock(queueLock);
auto it = removeQueue.find(obj);
if(it != removeQueue.end())
removeQueue.erase(obj);
else
addQueue.insert(obj);
obj->grab();
}
void Universe::removeChild(Object* obj) {
threads::Lock qlock(queueLock);
auto it = addQueue.find(obj);
if(it != addQueue.end()) {
addQueue.erase(obj);
for(unsigned i = 0; i < OBJ_TARGETS; ++i)
obj->targets[i] = nullptr;
}
else
removeQueue.insert(obj);
}
void Universe::destroyAll() {
foreach(child, children) {
(*child)->destroy(true);
(*child)->drop();
}
children.clear();
}
Object* Universe::getRandomTarget(unsigned randVal) {
//No child lock needed, guaranteed through lock ticking
if(children.empty())
return 0;
if(randVal == 0)
randVal = randomi();
return children[randVal % children.size()];
}
void Universe::setRandomTarget(heldPointer<Object>& targ, unsigned randVal) {
//No child lock needed, guaranteed through lock ticking
if(children.empty())
return;
if(randVal == 0)
randVal = randomi();
Object* child = children[randVal % children.size()];
if(child->isValid()) {
auto& other = child->targets[randVal % OBJ_TARGETS];
if(!other->getFlag(objNoPhysics))
other.swap(targ);
}
}
void Universe::removeTargetsTo(Object* obj) {
//No child lock needed, guaranteed through lock ticking
unsigned remaining = OBJ_TARGETS;
unsigned i = 0;
unsigned cnt = (unsigned)children.size();
for(unsigned t = 0; t < OBJ_TARGETS; ++t)
if(obj->targets[t] == obj)
--remaining;
if(remaining == 0)
goto clearTargets;
for(; i < cnt; ++i) {
Object* child = children[i];
for(unsigned j = 0; j < OBJ_TARGETS; ++j) {
if(child->targets[j] == obj && child != obj) {
for(unsigned t = 0; t < OBJ_TARGETS; ++t) {
if(obj->targets[t] != obj) {
child->targets[j].swap(obj->targets[t]);
break;
}
}
assert(children[i]->targets[j] != obj);
--remaining;
if(remaining == 0)
goto clearTargets;
}
}
}
clearTargets:;
for(unsigned i = 0; i < OBJ_TARGETS; ++i)
obj->targets[i] = 0;
}
const double coneSlope = 0.02;
Object* Universe::getClosestOnLine(const line3dd& line) const {
threads::ReadLock lock(childLock);
Empire* empire = Empire::getPlayerEmpire();
auto lineDir = line.getDirection();
double closest_d = 9e32;
double closest_p = 1.0;
Object* closest_o = 0;
//TODO: This should handle the cone more accurately, possibly in multiple steps?
//Bound should hold the line, and a considerable radius around it to handle the cone
AABBoxd bound(line.start);
bound.addBox( AABBoxd::fromCircle(line.end, 1500.0) );
devices.physics->findInBox(line, [&](const PhysicsItem& item) {
Object* obj = item.object;
if(!obj || !obj->isVisibleTo(empire))
return;
double distOnLine = (obj->position - line.start).dot(lineDir);
if(distOnLine <= 0)
return;
auto p = line.start + (lineDir * distOnLine);
double p_d = p.distanceTo(obj->position);
double c_d = obj->radius + distOnLine * coneSlope;
if(p_d >= c_d)
return;
double pct = p_d / c_d;
auto dist = p.getLength();
if(dist < closest_d && pct < closest_p) {
closest_o = obj;
closest_d = dist;
closest_p = pct;
}
});
return closest_o;
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "obj/object.h"
#include "util/refcount.h"
#include "threads.h"
#include <unordered_set>
class Universe : public AtomicRefCounted {
public:
mutable threads::Mutex queueLock;
std::unordered_set<Object*> addQueue;
std::unordered_set<Object*> removeQueue;
mutable threads::ReadWriteMutex childLock;
std::vector<Object*> children;
void doQueued();
void addChild(Object* obj);
void removeChild(Object* obj);
void destroyAll();
Object* getRandomTarget(unsigned randVal = 0);
void setRandomTarget(heldPointer<Object>& targ, unsigned randVal = 0);
void removeTargetsTo(Object* obj);
Object* getClosestOnLine(const line3dd& line) const;
};