Initial commit

This commit is contained in:
2025-12-17 16:47:48 +00:00
commit 13813f3363
4964 changed files with 1079753 additions and 0 deletions

1365
engine/app/CMakeLists.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,806 @@
#include "DataModel/Accoutrement.hpp"
#include "DataModel/JointInstance.hpp"
#include "DataModel/Workspace.hpp"
#include "DataModel/DataModel.hpp"
#include "DataModel/Attachment.hpp"
#include "Humanoid/Humanoid.hpp"
#include "Players.hpp"
#include "Player.hpp"
#include "Tool/DragUtilities.hpp"
#include "Script/script.hpp"
DYNAMIC_FASTFLAGVARIABLE(AccessoriesAndAttachments, false)
namespace Aya
{
using namespace Aya::Network;
const char* const sAccoutrement = "Accoutrement";
const char* const sHat = "Hat";
const char* const sAccessory = "Accessory";
static const Reflection::PropDescriptor<Accoutrement, CoordinateFrame> prop_AttachmentPoint("AttachmentPoint", category_Appearance,
&Accoutrement::getAttachmentPoint, &Accoutrement::setAttachmentPoint, Reflection::PropertyDescriptor::STANDARD);
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentPos(
"AttachmentPos", category_Appearance, &Accoutrement::getAttachmentPos, &Accoutrement::setAttachmentPos, Reflection::PropertyDescriptor::UI);
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentForward("AttachmentForward", category_Appearance,
&Accoutrement::getAttachmentForward, &Accoutrement::setAttachmentForward, Reflection::PropertyDescriptor::UI);
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentUp(
"AttachmentUp", category_Appearance, &Accoutrement::getAttachmentUp, &Accoutrement::setAttachmentUp, Reflection::PropertyDescriptor::UI);
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentRight(
"AttachmentRight", category_Appearance, &Accoutrement::getAttachmentRight, &Accoutrement::setAttachmentRight, Reflection::PropertyDescriptor::UI);
static const Reflection::PropDescriptor<Accoutrement, int> prop_BackendAccoutrementState("BackendAccoutrementState", category_Appearance,
&Accoutrement::getBackendAccoutrementState, &Accoutrement::setBackendAccoutrementState, Reflection::PropertyDescriptor::REPLICATE_ONLY);
REFLECTION_END();
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// FRONTEND AND BACKEND
Accoutrement::Accoutrement()
: DescribedCreatable<Accoutrement, Instance, sAccoutrement>()
, backendAccoutrementState(NOTHING)
, handleTouched(FLog::TouchedSignal)
{
setName(sAccoutrement);
}
Accoutrement::~Accoutrement()
{
AYAASSERT(!weld);
AYAASSERT(!handleTouched.connected());
AYAASSERT(!characterChildAdded.connected());
AYAASSERT(!characterChildRemoved.connected());
}
void Accoutrement::onCameraNear(float distance)
{
for (size_t i = 0; i < numChildren(); i++)
{
if (CameraSubject* cameraSubject = queryTypedChild<CameraSubject>(i))
{
cameraSubject->onCameraNear(distance);
}
}
}
void Accoutrement::render3dSelect(Adorn* adorn, SelectState selectState)
{
for (size_t i = 0; i < this->numChildren(); ++i)
{
if (IAdornable* iRenderable = dynamic_cast<IAdornable*>(this->getChild(i)))
{
iRenderable->render3dSelect(adorn, selectState);
}
}
}
void Accoutrement::dropAll(ModelInstance* character)
{
Accoutrement::dropAllOthers(character, NULL);
}
void Accoutrement::dropAllOthers(ModelInstance* character, Accoutrement* exception)
{
if (Workspace* workspace = Workspace::findWorkspace(character))
{
while (Accoutrement* acc = character->findFirstChildOfType<Accoutrement>())
{
if (acc != exception)
acc->setParent(workspace); // ok - should drop! check it out
}
}
}
PartInstance* Accoutrement::getHandle()
{
return const_cast<PartInstance*>(getHandleConst());
}
const PartInstance* Accoutrement::getHandleConst() const
{
return Instance::fastDynamicCast<PartInstance>(findConstFirstChildByName("Handle"));
}
Attachment* Accoutrement::findFirstMatchingAttachment(Instance* model, const std::string& originalAttachmentName)
{
if (model != NULL)
{
if (shared_ptr<const Instances> children = model->getChildren2())
{
for (Instances::const_iterator childIterator = children->begin(); childIterator != children->end(); ++childIterator)
{
Instance* child = (*childIterator).get();
Attachment* attachment = Instance::fastDynamicCast<Attachment>(child);
// check to see if child is an attachment and if the attachment fits with our original attachment
if ((attachment != NULL) && (attachment->getName() == originalAttachmentName))
{
return attachment;
}
// Continue recursive search but ignore accourtements/accessories
if (Instance::fastDynamicCast<Accoutrement>(child) == NULL)
{
Attachment* foundAttachment = Accoutrement::findFirstMatchingAttachment(child, originalAttachmentName);
// If the deeper search found one then keep handign it up, otherwise continue searching horizontally
if (foundAttachment != NULL)
{
return foundAttachment;
}
}
}
}
}
return NULL;
}
const CoordinateFrame Accoutrement::getLocation()
{
const PartInstance* handle = getHandleConst();
return handle ? handle->getCoordinateFrame() : CoordinateFrame();
}
//////////////////////////////////////////////////////////////////////////////
//
// REPLICATION - Front and Back end
/*
watch_touch
IN_CHARACTER x
IN_WORKSPACE x -> needs touch or parent already set to proceed
HAS_HANDLE x
NOTHING
*/
void Accoutrement::setBackendAccoutrementStateNoReplicate(int value)
{
backendAccoutrementState = static_cast<AccoutrementState>(value);
}
void Accoutrement::setBackendAccoutrementState(int value)
{
if (value != backendAccoutrementState)
{
setBackendAccoutrementStateNoReplicate(value);
raisePropertyChanged(prop_BackendAccoutrementState);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// BACKEND
void Accoutrement::connectTouchEvent()
{
PartInstance* handle = getHandle();
if (handle)
{
handleTouched = handle->onDemandWrite()->touchedSignal.connect(boost::bind(&Accoutrement::onEvent_HandleTouched, this, _1));
FASTLOG3(FLog::TouchedSignal, "Connecting Accoutrement to touched signal, instance: %p, part: %p, part signal: %p", this, handle,
&(handle->onDemandRead()->touchedSignal));
}
else
{
// AYAASSERT(0); // only call if >= the HAS_HANDLE state
handleTouched.disconnect(); // just to be safe
}
}
void Accoutrement::connectAttachmentAdjustedEvent()
{
attachmentAdjusted.disconnect();
if (PartInstance* handle = getHandle())
{
if (Attachment* attachment = handle->findFirstChildOfType<Attachment>())
{
attachmentAdjusted = attachment->propertyChangedSignal.connect(boost::bind(&Accoutrement::onEvent_AttachmentAdjusted, this, _1));
}
}
}
void Accoutrement::rebuildBackendState()
{
AccoutrementState desiredState = computeDesiredState();
const ServiceProvider* serviceProvider = ServiceProvider::findServiceProvider(this);
AYAASSERT(serviceProvider);
setDesiredState(desiredState, serviceProvider);
}
/*
Backend:
watch_touch
IN_CHARACTER x
IN_WORKSPACE x -> needs touch or parent already set to proceed
HAS_HANDLE x
NOTHING
*/
Accoutrement::AccoutrementState Accoutrement::computeDesiredState()
{
AYAASSERT(Network::Players::backendProcessing(this));
PartInstance* handle = getHandle();
if (!handle)
{
return NOTHING;
}
if (!Workspace::contextInWorkspace(this))
{
return HAS_HANDLE;
}
return computeDesiredState(getParent());
}
Accoutrement::AccoutrementState Accoutrement::computeDesiredState(Instance* testParent)
{
Humanoid* humanoid = Humanoid::modelIsCharacter(testParent);
// Parent is not a character model, we are in the workspace
if (!humanoid)
{
return IN_WORKSPACE;
}
// make sure we are not already wearing a hat
/*if (testParent->findFirstChildOfType<Accoutrement>() != NULL)
{
return IN_WORKSPACE;
}*/
if (humanoid->getTorsoSlow())
{
return EQUIPPED;
}
return NOTHING;
}
void Accoutrement::setDesiredState(AccoutrementState desiredState, const ServiceProvider* serviceProvider)
{
AYAASSERT(Network::Players::backendProcessing(serviceProvider));
if (desiredState > backendAccoutrementState)
{
switch (backendAccoutrementState)
{
case IN_CHARACTER:
upTo_Equipped();
break;
case IN_WORKSPACE:
upTo_InCharacter();
break;
case HAS_HANDLE:
upTo_InWorkspace();
break;
case NOTHING:
upTo_HasHandle();
break;
default:
AYAASSERT(0);
}
setBackendAccoutrementState(static_cast<AccoutrementState>(backendAccoutrementState + 1));
}
else if (desiredState < backendAccoutrementState)
{
switch (backendAccoutrementState)
{
case EQUIPPED:
downFrom_Equipped();
break;
case IN_CHARACTER:
downFrom_InCharacter();
break;
case IN_WORKSPACE:
downFrom_InWorkspace();
break;
case HAS_HANDLE:
downFrom_HasHandle();
break;
default:
AYAASSERT(0);
}
setBackendAccoutrementState(static_cast<AccoutrementState>(backendAccoutrementState - 1));
}
// do again if still not there - recursive
if (desiredState != backendAccoutrementState)
{
setDesiredState(desiredState, serviceProvider);
}
}
void Accoutrement::upTo_HasHandle()
{
connectTouchEvent();
}
void Accoutrement::downFrom_HasHandle()
{
handleTouched.disconnect();
if (DFFlag::AccessoriesAndAttachments)
{
attachmentAdjusted.disconnect();
}
}
void Accoutrement::upTo_InWorkspace()
{
AYAASSERT(ServiceProvider::findServiceProvider(this));
AYAASSERT(Network::Players::backendProcessing(this));
}
void Accoutrement::downFrom_InWorkspace() {}
void Accoutrement::upTo_InCharacter()
{
AYAASSERT(Humanoid::modelIsCharacter(getParent()));
characterChildAdded = getParent()->onDemandWrite()->childAddedSignal.connect(boost::bind(&Accoutrement::onEvent_AddedBackend, this, _1));
characterChildRemoved = getParent()->onDemandWrite()->childRemovedSignal.connect(boost::bind(&Accoutrement::onEvent_RemovedBackend, this, _1));
}
void Accoutrement::downFrom_InCharacter()
{
characterChildAdded.disconnect();
characterChildRemoved.disconnect();
}
void Accoutrement::UnequipThis(shared_ptr<Instance> instance)
{
// if this is an accoutrement that is equipped, unequip
Accoutrement* a = Instance::fastDynamicCast<Accoutrement>(instance.get());
if (!a)
return;
if (a->backendAccoutrementState == EQUIPPED)
{
a->setDesiredState(IN_WORKSPACE, ServiceProvider::findServiceProvider(a));
}
}
// TODO: This code is the nearly the same as Tool::upTo_Equipped. That's evil!
void Accoutrement::upTo_Equipped()
{
// Unequip other hats
/*
Instance *i = dynamic_cast<Instance *>(getParent());
AYAASSERT(i);
if (i->getChildren())
std::for_each(
i->getChildren()->begin(),
i->getChildren()->end(),
boost::bind(&Accoutrement::UnequipThis, _1)
);
*/
// Disconnect listening for touches
handleTouched.disconnect();
// Unjoin tool from other parts if any remaining joints
PartArray parts;
PartInstance::findParts(this, parts);
DragUtilities::unJoinFromOutsiders(parts);
Humanoid* humanoid = Humanoid::modelIsCharacter(getParent());
if (!humanoid)
return;
if (DFFlag::AccessoriesAndAttachments)
{
PartInstance* handle = getHandle();
if (!handle)
return;
// Code for new attachment based system
Attachment* attachment = handle->findFirstChildOfType<Attachment>();
Attachment* matchingAttachment = NULL;
PartInstance* matchingAttachmentPart = NULL;
if (attachment)
{
if ((matchingAttachment = Accoutrement::findFirstMatchingAttachment(getParent(), attachment->getName())))
{
matchingAttachmentPart = Instance::fastDynamicCast<PartInstance>(matchingAttachment->getParent());
}
}
// This infers that both attachments were found
if (matchingAttachmentPart)
{
handle->setCanCollide(false);
buildWeld(handle, matchingAttachmentPart, attachment->getFrameInPart(), matchingAttachment->getFrameInPart(), "AccessoryWeld");
}
else
{
// This is legacy functionality
if (PartInstance* head = humanoid->getHeadSlow())
{
handle->setCanCollide(false);
buildWeld(head, handle, humanoid->getTopOfHead(), attachmentPoint, "HeadWeld");
}
}
// Listen to attachment changed to update weld
connectAttachmentAdjustedEvent();
}
else
{
PartInstance* head = humanoid->getHeadSlow();
if (!head)
return;
PartInstance* handle = getHandle();
if (handle)
{
handle->setCanCollide(false);
buildWeld(head, handle, humanoid->getTopOfHead(), attachmentPoint, "HeadWeld");
}
}
}
void Accoutrement::downFrom_Equipped()
{
ModelInstance* oldCharacter = NULL;
if (weld)
{
if (DFFlag::AccessoriesAndAttachments)
{
PartInstance* handle = getHandle();
PartInstance* oldAttachmentPart = NULL;
if (handle && (weld->getParent() == handle))
{
// If the weld is under the handle, then it is using the new attachment system
oldAttachmentPart = weld->getPart1();
}
else
{
// If the weld is not undle the handle then it is using the legacy system
oldAttachmentPart = weld->getPart0();
}
if (oldAttachmentPart)
{
oldCharacter = Instance::fastDynamicCast<ModelInstance>(oldAttachmentPart->getParent());
}
}
else
{
if (PartInstance* oldArm = weld->getPart0())
{
oldCharacter = Instance::fastDynamicCast<ModelInstance>(oldArm->getParent());
}
}
weld->setParent(NULL);
weld.reset();
}
if (oldCharacter && Humanoid::modelIsCharacter(oldCharacter))
{
CoordinateFrame cf = oldCharacter->getLocation();
Vector3 pos = cf.translation;
pos.y += 4;
pos += cf.lookVector() * 8;
if (PartInstance* handle = getHandle())
{
handle->setCanCollide(true);
handle->moveToPointNoJoin(pos);
handle->setRotationalVelocity(Vector3(1.0f, 1.0f, 1.0f));
}
}
// UGLY hack for when the camera is zooming in on the character, and he drops his hat, so the hat is not transparent.
this->onCameraNear(999.0f);
// Dropping - must start listening again
connectTouchEvent();
if (DFFlag::AccessoriesAndAttachments)
{
// Dropping - Doesn't need to be dynamically adjust weld
attachmentAdjusted.disconnect();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Backend stuff - events
// Used for both the tool and the torso
//
// Character: RightArm, Tool
// Torso: Shoulder?
//
void Accoutrement::onEvent_AddedBackend(shared_ptr<Instance> child)
{
if (child.get() != this) // tool parent changed is handled in ancestor stuff, because this might not be hooked up
{
AYAASSERT(ServiceProvider::findServiceProvider(this));
AYAASSERT(Network::Players::backendProcessing(this));
rebuildBackendState();
}
}
void Accoutrement::onEvent_RemovedBackend(shared_ptr<Instance> child)
{
if (child.get() != this) // tool parent changed is handled in ancestor stuff, because this might not be hooked up
{
AYAASSERT(ServiceProvider::findServiceProvider(this));
AYAASSERT(Network::Players::backendProcessing(this));
if (child.get() == weld.get())
{
AYAASSERT(backendAccoutrementState >= EQUIPPED);
setDesiredState(NOTHING, ServiceProvider::findServiceProvider(this)); // force down, nuke the weld
}
rebuildBackendState();
}
}
void Accoutrement::onChildAdded(Instance* child)
{
if (ServiceProvider::findServiceProvider(this) && Network::Players::backendProcessing(this))
{
rebuildBackendState();
}
}
void Accoutrement::onChildRemoved(Instance* child)
{
if (ServiceProvider::findServiceProvider(this) && Network::Players::backendProcessing(this))
{
rebuildBackendState();
}
}
// This is a backend event
void Accoutrement::onEvent_HandleTouched(shared_ptr<Instance> other)
{
AYAASSERT(Network::Players::backendProcessing(this));
if ((backendAccoutrementState == IN_WORKSPACE) && other)
{
Instance* touchingCharacter = other->getParent();
if (computeDesiredState(touchingCharacter) == EQUIPPED)
{
if (touchingCharacter->findFirstChildOfType<Accoutrement>() == NULL)
{
setParent(touchingCharacter);
}
// AYAASSERT(backendAccoutrementState == EQUIPPED);
}
}
}
void Accoutrement::onEvent_AttachmentAdjusted(const Aya::Reflection::PropertyDescriptor* propertyDescriptor)
{
AYAASSERT(Network::Players::backendProcessing(this));
if (propertyDescriptor == &Aya::Attachment::prop_Frame)
{
updateWeld();
}
}
void Accoutrement::onAncestorChanged(const AncestorChanged& event)
{
const ServiceProvider* oldProvider = ServiceProvider::findServiceProvider(event.oldParent);
const ServiceProvider* newProvider = ServiceProvider::findServiceProvider(event.newParent);
if (oldProvider)
{
if (Network::Players::backendProcessing(oldProvider))
{
setDesiredState(NOTHING, oldProvider);
}
else
{
setBackendAccoutrementStateNoReplicate(NOTHING);
}
}
Super::onAncestorChanged(event);
if (newProvider)
{
if (Network::Players::backendProcessing(newProvider))
{
rebuildBackendState();
}
}
}
void Accoutrement::updateWeld()
{
if (weld != NULL)
{
// all this does not occur client side
AYAASSERT(Network::Players::backendProcessing(this));
Attachment* attachment = NULL;
PartInstance* handle = getHandle();
if (handle && (weld->getParent() == handle))
{
// If the weld is under the handle, then it is using the new attachment system
attachment = handle->findFirstChildOfType<Attachment>();
}
if (attachment)
{
// new attachment system
weld->setC0(attachment->getFrameInPart());
}
else
{
// legacy hat
weld->setC1(attachmentPoint);
}
}
}
void Accoutrement::setAttachmentPoint(const CoordinateFrame& value)
{
if (value != attachmentPoint)
{
attachmentPoint = value;
raisePropertyChanged(prop_AttachmentPoint);
if (DFFlag::AccessoriesAndAttachments)
{
updateWeld();
}
else
{
if (weld != NULL)
{
// all this does not occur client side
AYAASSERT(Network::Players::backendProcessing(this));
weld->setC1(attachmentPoint);
}
}
}
}
// Auxillary UI props
const Vector3 Accoutrement::getAttachmentPos() const
{
return attachmentPoint.translation;
}
const Vector3 Accoutrement::getAttachmentForward() const
{
return attachmentPoint.lookVector();
}
const Vector3 Accoutrement::getAttachmentUp() const
{
return attachmentPoint.upVector();
}
const Vector3 Accoutrement::getAttachmentRight() const
{
return attachmentPoint.rightVector();
}
void Accoutrement::setAttachmentPos(const Vector3& v)
{
setAttachmentPoint(CoordinateFrame(attachmentPoint.rotation, v));
}
void Accoutrement::setAttachmentForward(const Vector3& v)
{
CoordinateFrame c(attachmentPoint);
// Vector3 oldX = c.rightVector();
Vector3 oldY = c.upVector();
Vector3 z = -Math::safeDirection(v);
Vector3 x = oldY.cross(z);
x.unitize();
Vector3 y = z.cross(x);
y.unitize();
c.rotation.setColumn(0, x);
c.rotation.setColumn(1, y);
c.rotation.setColumn(2, z);
setAttachmentPoint(c);
}
void Accoutrement::setAttachmentUp(const Vector3& v)
{
CoordinateFrame c(attachmentPoint);
Vector3 oldX = c.rightVector();
// Vector3 oldZ = -c.lookVector();
Vector3 y = Math::safeDirection(v);
Vector3 z = oldX.cross(y);
z.unitize();
Vector3 x = y.cross(z);
x.unitize();
c.rotation.setColumn(0, x);
c.rotation.setColumn(1, y);
c.rotation.setColumn(2, z);
setAttachmentPoint(c);
}
void Accoutrement::setAttachmentRight(const Vector3& v)
{
CoordinateFrame c(attachmentPoint);
Vector3 oldY = c.upVector();
// Vector3 oldZ = -c.lookVector();
Vector3 x = Math::safeDirection(v);
Vector3 z = x.cross(oldY);
z.unitize();
Vector3 y = z.cross(x);
y.unitize();
c.rotation.setColumn(0, x);
c.rotation.setColumn(1, y);
c.rotation.setColumn(2, z);
setAttachmentPoint(c);
}
Hat::Hat()
: DescribedCreatable<Hat, Accoutrement, sHat>()
{
setName(sHat);
}
Accessory::Accessory()
: DescribedCreatable<Accessory, Accoutrement, sAccessory>()
{
setName(sAccessory);
}
} // namespace Aya

View File

@@ -0,0 +1,198 @@
#pragma once
#include "DataModel/IEquipable.hpp"
#include "DataModel/PartInstance.hpp"
#include "Tree/Instance.hpp"
#include "Base/IAdornable.hpp"
#include "Utility/CameraSubject.hpp"
#include "Utility/Selectable.hpp"
namespace Aya
{
class PartInstance;
class ModelInstance;
class Weld;
class ServiceProvider;
class Workspace;
class Attachment;
namespace Network
{
class Player;
}
extern const char* const sAccoutrement;
extern const char* const sHat;
extern const char* const sAccessory;
class Accoutrement
: public DescribedCreatable<Accoutrement, Instance, sAccoutrement>
, public IEquipable // TODO - move stuff here that's common with Tool
, public IAdornable
, public CameraSubject
{
typedef DescribedCreatable<Accoutrement, Instance, sAccoutrement> Super;
protected:
typedef enum
{
NOTHING,
HAS_HANDLE,
IN_WORKSPACE,
IN_CHARACTER,
EQUIPPED
} AccoutrementState;
// Replicated Properties
AccoutrementState backendAccoutrementState; // backend writes, frontend reads
CoordinateFrame attachmentPoint; // replicates, stores
// "Backend" connections
Aya::signals::scoped_connection_logged handleTouched; // watches for handle touched
Aya::signals::scoped_connection characterChildAdded;
Aya::signals::scoped_connection characterChildRemoved;
Aya::signals::scoped_connection attachmentAdjusted;
// Event handlers - hooked to these signals
void onEvent_AddedBackend(shared_ptr<Instance> child);
void onEvent_RemovedBackend(shared_ptr<Instance> child);
void onEvent_HandleTouched(shared_ptr<Instance> other);
void onEvent_AttachmentAdjusted(const Aya::Reflection::PropertyDescriptor*);
static AccoutrementState characterCanPickUpAccoutrement(Instance* touchingCharacter);
static void UnequipThis(shared_ptr<Instance> instance);
void updateWeld();
///////////////////////////////////////////////////////////////
//
// Backend side
//
AccoutrementState computeDesiredState();
AccoutrementState computeDesiredState(Instance* testParent);
void setDesiredState(AccoutrementState desiredState, const ServiceProvider* serviceProvider);
void setBackendAccoutrementStateNoReplicate(int value);
void rebuildBackendState();
void connectTouchEvent();
void connectAttachmentAdjustedEvent();
// Backend - climbing state
void upTo_Equipped();
void upTo_InCharacter();
void upTo_InWorkspace();
void upTo_HasHandle();
// Backend - dropping state
void downFrom_Equipped();
void downFrom_InCharacter();
void downFrom_InWorkspace();
void downFrom_HasHandle();
///////////////////////////////////////////////////////////////////////
//
// Frontend side
// Instance
/*override*/ void onChildAdded(Instance* child);
/*override*/ void onChildRemoved(Instance* child);
/*override*/ void onAncestorChanged(const AncestorChanged& event);
/*override*/ bool askSetParent(const Instance* instance) const
{
return true;
}
/*override*/ bool askAddChild(const Instance* instance) const
{
return true;
}
////////////////////////////////////////////////////////////////////////
// IHasLocation
/*override*/ const CoordinateFrame getLocation();
////////////////////////////////////////////////////////////////////////
// CameraSubject
//
/*override*/ const CoordinateFrame getRenderLocation()
{
return getLocation();
}
/*override*/ const Vector3 getRenderSize()
{
if (PartInstance* handle = getHandle())
return handle->getPartSizeUi();
return Vector3(0, 0, 0);
}
/*override*/ void onCameraNear(float distance);
// BackpackItem
/*override*/ bool drawSelected() const
{
return (backendAccoutrementState >= EQUIPPED);
}
// IAdornable
/*override*/ void render3dSelect(Adorn* adorn, SelectState selectState);
public:
Accoutrement();
~Accoutrement();
static void dropAll(ModelInstance* character);
static void dropAllOthers(ModelInstance* character, Accoutrement* exception);
PartInstance* getHandle();
const PartInstance* getHandleConst() const;
Attachment* findFirstMatchingAttachment(Instance* model, const std::string& originalAttachment);
//////////////////////////////////////////////////////////
//
// REPLICATION Signals
void setBackendAccoutrementState(int value);
int getBackendAccoutrementState() const
{
return backendAccoutrementState;
}
const CoordinateFrame& getAttachmentPoint() const
{
return attachmentPoint;
}
void setAttachmentPoint(const CoordinateFrame& value);
// Auxillary UI props
const Vector3 getAttachmentPos() const;
const Vector3 getAttachmentForward() const;
const Vector3 getAttachmentUp() const;
const Vector3 getAttachmentRight() const;
void setAttachmentPos(const Vector3& v);
void setAttachmentForward(const Vector3& v);
void setAttachmentUp(const Vector3& v);
void setAttachmentRight(const Vector3& v);
};
class Hat : public DescribedCreatable<Hat, Accoutrement, sHat>
{
public:
Hat();
};
class Accessory : public DescribedCreatable<Accessory, Accoutrement, sAccessory>
{
public:
Accessory();
};
} // namespace Aya

View File

@@ -0,0 +1,60 @@
#pragma once
#include "DataModel/JointInstance.hpp"
#include "DataModel/Workspace.hpp"
#include "Humanoid/Humanoid.hpp"
#include "World/Primitive.hpp"
#include "World/Assembly.hpp"
#include "time.hpp"
DYNAMIC_FASTINT(ActionStationDebounceTime)
namespace Aya
{
template<class Base>
class ActionStation : public Base
{
private:
typedef Base Super;
protected:
Time sleepTime;
Time debounceTime;
bool sleepTimeUp() const
{
return (Time::now<Time::Fast>() - sleepTime).seconds() > 3.0;
}
bool debounceTimeUp() const
{
return (Time::now<Time::Fast>() - debounceTime).seconds() > DFInt::ActionStationDebounceTime;
}
// Instance
// TODO: Ultra mega super hack - setName is setting the internal Primitive::sizeMultiplier
void setName(const std::string& value)
{
Super::setName(value);
this->getPartPrimitive()->setSizeMultiplier(Primitive::SEAT_SIZE);
}
public:
ActionStation()
: sleepTime(Time::now<Time::Fast>() - Time::Interval(4.0))
, debounceTime(Time::now<Time::Fast>())
{
AYAASSERT(this->sleepTimeUp());
AYAASSERT(this->getPartPrimitive());
this->getPartPrimitive()->setSizeMultiplier(Primitive::SEAT_SIZE);
}
virtual ~ActionStation() {}
};
} // namespace Aya

View File

@@ -0,0 +1,257 @@
//
// AdService.cpp
// App
//
// Created by Ben Tkacheff on 4/16/14.
//
//
#include "DataModel/AdService.hpp"
#include "DataModel/UserInputService.hpp"
#include "DataModel/HttpRbxApiService.hpp"
#include "Players.hpp"
#include "Xml/WebParser.hpp"
FASTFLAGVARIABLE(EnableVideoAds, true)
namespace Aya
{
const char* const sAdService = "AdService";
static Reflection::RemoteEventDesc<AdService, void(int, UserInputService::Platform, bool)> event_sendServerRecordImpression(
&AdService::sendServerRecordImpression, "SendServerRecordImpression", "userId", "platform", "wasSuccessful", Security::Roblox,
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::CLIENT_SERVER);
static Reflection::RemoteEventDesc<AdService, void(bool, int, std::string)> event_sendClientAdVerificationResults(
&AdService::sendClientVideoAdVerificationResults, "ClientAdVerificationResults", "canShowAd", "userId", "errorMessage", Security::Roblox,
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AdService, void(int, UserInputService::Platform)> event_serverAdVerification(
&AdService::sendServerVideoAdVerification, "ServerAdVerification", "userId", "platform", Security::Roblox,
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::CLIENT_SERVER);
static Reflection::BoundFuncDesc<AdService, void()> func_showVideoAd(&AdService::showVideoAd, "ShowVideoAd", Security::None);
static Reflection::EventDesc<AdService, void(bool)> event_videoClosed(&AdService::videoAdClosedSignal, "VideoAdClosed", "adShown");
REFLECTION_END();
AdService::AdService()
: showingVideoAd(false)
{
setName(sAdService);
}
bool AdService::canUseService()
{
return FFlag::EnableVideoAds;
}
void AdService::showVideoAd()
{
if (!canUseService())
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, AdService is currently off.");
videoAdClosedSignal(false);
return;
}
if (showingVideoAd)
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd called while currently showing video.");
videoAdClosedSignal(false);
return;
}
if (Aya::UserInputService* inputService = Aya::ServiceProvider::find<Aya::UserInputService>(this))
{
if (!inputService->getTouchEnabled())
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, AdService:ShowVideoAd only works on touch devices currently.");
videoAdClosedSignal(false);
return;
}
}
if (!Network::Players::frontendProcessing(this))
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd must be called from a local script!");
videoAdClosedSignal(false);
return;
}
if (Network::Player* player = Network::Players::findLocalPlayer(this))
{
if (Aya::ServiceProvider::find<Aya::UserInputService>(this))
{
event_serverAdVerification.fireAndReplicateEvent(this, player->getUserID(), UserInputService::getPlatform());
}
else
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd could not find UserInputService, not showing ad.");
videoAdClosedSignal(false);
return;
}
}
else
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd could not find LocalPlayer, not showing ad.");
videoAdClosedSignal(false);
return;
}
}
void AdService::receivedServerShowAdMessage(const bool success, int userId, const std::string& errorMessage)
{
if (!Aya::Network::Players::clientIsPresent(this))
{
return;
}
Network::Player* player = Network::Players::findLocalPlayer(this);
if (!player)
{
return;
}
if (player->getUserID() != userId)
{
return;
}
if (success)
{
showingVideoAd = true;
playVideoAdSignal();
}
else
{
StandardOut::singleton()->printf(
MESSAGE_WARNING, "AdService:ShowVideoAd cannot show video ad, failed verification because %s", errorMessage.c_str());
videoAdClosedSignal(false);
}
}
void AdService::verifyCanPlayVideoAdReceivedResponseNoDMLock(const std::string& response, int userId)
{
shared_ptr<const Reflection::ValueTable> jsonResult(Aya::make_shared<const Reflection::ValueTable>());
bool parseResult = WebParser::parseJSONTable(response, jsonResult);
if (parseResult)
{
Reflection::Variant successVar = jsonResult->at("success");
if (successVar.get<bool>())
{
event_sendClientAdVerificationResults.fireAndReplicateEvent(this, true, userId, "");
}
else
{
if (jsonResult->at("errorMessage").isString())
{
verifyCanPlayVideoAdReceivedError(jsonResult->at("errorMessage").get<std::string>(), userId);
}
else
{
verifyCanPlayVideoAdReceivedError("JSON returned invalid format", userId);
}
}
}
else
{
verifyCanPlayVideoAdReceivedError("Unable to parse JSON response", userId);
}
}
void AdService::verifyCanPlayVideoAdReceivedError(const std::string& error, int userId)
{
Aya::DataModel::LegacyLock dmLock(Aya::DataModel::get(this), Aya::DataModelJob::Write);
event_sendClientAdVerificationResults.fireAndReplicateEvent(this, false, userId, error);
}
void AdService::verifyCanPlayVideoAdReceivedErrorNoDMLock(const std::string& error, int userId)
{
event_sendClientAdVerificationResults.fireAndReplicateEvent(this, false, userId, error);
}
std::string AdService::platformToWebString(const UserInputService::Platform userPlatform)
{
switch (userPlatform)
{
case UserInputService::PLATFORM_IOS:
return "ios";
case UserInputService::PLATFORM_ANDROID:
return "android";
default:
return "unknown";
break;
}
}
void AdService::checkCanPlayVideoAd(int userId, UserInputService::Platform userPlatform)
{
if (!Aya::Network::Players::serverIsPresent(this))
{
return;
}
if (Aya::DataModel* dataModel = Aya::DataModel::get(this))
{
std::string data =
Aya::format("userId=%d&placeId=%d&deviceOSType=%s", userId, dataModel->getPlaceID(), platformToWebString(userPlatform).c_str());
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
{
apiService->postAsync(Aya::format("adimpression/validate-request?%s", data.c_str()), std::string("ServerCanPlayAd"), true,
Aya::PRIORITY_DEFAULT, HttpService::TEXT_PLAIN,
boost::bind(&AdService::verifyCanPlayVideoAdReceivedResponseNoDMLock, this, _1, userId),
boost::bind(&AdService::verifyCanPlayVideoAdReceivedErrorNoDMLock, this, _1, userId));
}
}
}
void AdService::videoAdClosed(bool didPlay)
{
showingVideoAd = false;
videoAdClosedSignal(didPlay);
if (Network::Player* player = Network::Players::findLocalPlayer(this))
{
if (Aya::ServiceProvider::find<Aya::UserInputService>(this))
{
event_sendServerRecordImpression.fireAndReplicateEvent(this, player->getUserID(), UserInputService::getPlatform(), didPlay);
}
}
}
static void ForgetResponse(const std::string& response) {}
void AdService::sendAdImpression(int userId, UserInputService::Platform platform, bool didPlay)
{
if (Aya::DataModel* dataModel = Aya::DataModel::get(this))
{
if (Network::Players::serverIsPresent(dataModel))
{
std::string apiBaseUrl = ServiceProvider::create<ContentProvider>(this)->getApiBaseUrl();
// fire and forget, we don't care about results on the client, at least for now
std::string data = Aya::format("userId=%i&placeId=%d&deviceOSType=%s&wasSuccessful=%s", userId, dataModel->getPlaceID(),
platformToWebString(platform).c_str(), didPlay ? "true" : "false");
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
{
apiService->postAsync(Aya::format("adimpression/record-impression?%s", data.c_str()), std::string("ServerSendAdImpression"), true,
Aya::PRIORITY_DEFAULT, HttpService::TEXT_PLAIN, boost::bind(&ForgetResponse, _1), boost::bind(&ForgetResponse, _1));
}
}
}
}
void AdService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
Super::onServiceProvider(oldProvider, newProvider);
if (newProvider && Network::Players::frontendProcessing(newProvider))
{
sendClientVideoAdVerificationResults.connect(boost::bind(&AdService::receivedServerShowAdMessage, this, _1, _2, _3));
}
}
} // namespace Aya

View File

@@ -0,0 +1,54 @@
//
// AdService.h
//
//
#pragma once
#include "Tree/Instance.hpp"
#include "Tree/Service.hpp"
#include "DataModel/UserInputService.hpp"
namespace Aya
{
extern const char* const sAdService;
class AdService
: public DescribedCreatable<AdService, Instance, sAdService, Reflection::ClassDescriptor::INTERNAL>
, public Service
{
private:
typedef DescribedCreatable<AdService, Instance, sAdService, Reflection::ClassDescriptor::INTERNAL> Super;
bool showingVideoAd;
std::string platformToWebString(const UserInputService::Platform userPlatform);
bool canUseService();
protected:
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
public:
AdService();
Aya::signal<void(bool)> videoAdClosedSignal;
Aya::signal<void()> playVideoAdSignal;
Aya::remote_signal<void(int, UserInputService::Platform)> sendServerVideoAdVerification;
Aya::remote_signal<void(bool, int, std::string)> sendClientVideoAdVerificationResults;
Aya::remote_signal<void(int, UserInputService::Platform, bool)> sendServerRecordImpression;
void showVideoAd();
void videoAdClosed(bool didPlay);
void sendAdImpression(int userId, UserInputService::Platform platform, bool didPlay);
void verifyCanPlayVideoAdReceivedResponseNoDMLock(const std::string& response, int userId);
void verifyCanPlayVideoAdReceivedErrorNoDMLock(const std::string& error, int userId);
void verifyCanPlayVideoAdReceivedError(const std::string& error, int userId);
void checkCanPlayVideoAd(int userId, UserInputService::Platform userPlatform);
void receivedServerShowAdMessage(const bool success, int userId, const std::string& errorMessage);
};
} // namespace Aya

View File

@@ -0,0 +1,49 @@
#include "DataModel/Adornment.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/PVInstance.hpp"
namespace Aya
{
const char* const sPartAdornment = "PartAdornment";
const Reflection::RefPropDescriptor<PartAdornment, PartInstance> prop_partAdornee(
"Adornee", category_Data, &PartAdornment::getAdorneeDangerous, &PartAdornment::setAdornee);
PartAdornment::PartAdornment(const char* name)
: DescribedNonCreatable<PartAdornment, GuiBase3d, sPartAdornment>(name)
{
}
void PartAdornment::setAdornee(PartInstance* value)
{
if (adornee.lock().get() != value)
{
adornee = shared_from(value);
raisePropertyChanged(prop_partAdornee);
}
}
const char* const sPVAdornment = "PVAdornment";
const Reflection::RefPropDescriptor<PVAdornment, PVInstance> prop_pvAdornee(
"Adornee", category_Data, &PVAdornment::getAdorneeDangerous, &PVAdornment::setAdornee);
PVAdornment::PVAdornment(const char* name)
: DescribedNonCreatable<PVAdornment, GuiBase3d, sPVAdornment>(name)
{
}
void PVAdornment::setAdornee(PVInstance* value)
{
if (adornee.lock().get() != value)
{
adornee = shared_from(value);
raisePropertyChanged(prop_pvAdornee);
}
}
} // namespace Aya

View File

@@ -0,0 +1,65 @@
#pragma once
#include "DataModel/GuiBase3d.hpp"
#include "Utility/BrickColor.hpp"
namespace Aya
{
class PartInstance;
class PVInstance;
extern const char* const sPartAdornment;
// A base class for "adornment" of PartInstances (3D objects that adorn Instances)
class PartAdornment : public DescribedNonCreatable<PartAdornment, GuiBase3d, sPartAdornment>
{
public:
PartAdornment(const char* name);
const PartInstance* getAdornee() const
{
return adornee.lock().get();
}
PartInstance* getAdornee()
{
return adornee.lock().get();
}
void setAdornee(PartInstance* value);
PartInstance* getAdorneeDangerous() const
{
return adornee.lock().get();
}
protected:
weak_ptr<PartInstance> adornee;
};
extern const char* const sPVAdornment;
// A base class for "adornment" of PartInstances (3D objects that adorn Instances)
class PVAdornment : public DescribedNonCreatable<PVAdornment, GuiBase3d, sPVAdornment>
{
public:
PVAdornment(const char* name);
const PVInstance* getAdornee() const
{
return adornee.lock().get();
}
PVInstance* getAdornee()
{
return adornee.lock().get();
}
void setAdornee(PVInstance* value);
PVInstance* getAdorneeDangerous() const
{
return adornee.lock().get();
}
protected:
weak_ptr<PVInstance> adornee;
};
} // namespace Aya

View File

@@ -0,0 +1,55 @@
#include "DataModel/AnimatableRootJoint.hpp"
using namespace Aya;
AnimatableRootJoint::AnimatableRootJoint(const shared_ptr<PartInstance>& part)
: IAnimatableJoint()
, part(part)
, isAnimating(false)
{
}
void AnimatableRootJoint::setAnimating(bool value)
{
if (isAnimating != value && value)
{
// reset.
lastCFrame = CoordinateFrame();
}
isAnimating = value;
}
const std::string& AnimatableRootJoint::getParentName()
{
return IAnimatableJoint::sROOT;
}
const std::string& AnimatableRootJoint::getPartName()
{
return part->getName();
}
void AnimatableRootJoint::applyPose(const CachedPose& pose)
{
// nb: we completely ignore blend weights unless they are zero
// nb: we ignore maskWeight and pretend it is 1. (since we don't prevent physics from acting)
CoordinateFrame poseCFrame = pose.getCFrame();
if (pose.weight > 0)
{
// to let physics act on the part, we extract the change from last frame.
// then just apply the change.
// toObjectSpace is this->inverse() * c;
// delta = last^-1 * pose
// newcframe = currentcframe * delta
// newcframe = currentcframe * last^-1 * pose
// so if currentcframe == last (eg: part was anchored), then
// newcframe = pose
CoordinateFrame delta = lastCFrame.toObjectSpace(poseCFrame);
part->setCoordinateFrame(part->getCoordinateFrame() * delta);
}
lastCFrame = poseCFrame;
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "DataModel/PartInstance.hpp"
#include "DataModel/IAnimatableJoint.hpp"
namespace Aya
{
class AnimatableRootJoint : public IAnimatableJoint
{
bool isAnimating;
shared_ptr<PartInstance> part;
CoordinateFrame lastCFrame;
public:
AnimatableRootJoint(const shared_ptr<PartInstance>& part);
PartInstance* getPart() const
{
return part.get();
}
/*override*/ void setAnimating(bool value);
/*override*/ const std::string& getParentName();
/*override*/ const std::string& getPartName();
/*override*/ void applyPose(const CachedPose& pose);
};
} // namespace Aya

View File

@@ -0,0 +1,65 @@
#include "DataModel/Animation.hpp"
#include "DataModel/KeyframeSequenceProvider.hpp"
#include "DataModel/AnimationTrackState.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/JointInstance.hpp"
namespace Aya
{
const char* const sAnimation = "Animation";
const Reflection::PropDescriptor<Animation, AnimationId> prop_AnimationId(
"AnimationId", category_Data, &Animation::getAssetId, &Animation::setAssetId);
// const Reflection::PropDescriptor<Animation, bool> prop_Loop("Loop", category_Data, &Animation::getLoop, &Animation::setLoop);
// const Reflection::EnumPropDescriptor<Animation, Animation::Priority> prop_Priority("Priority", category_Data, &Animation::getPriority,
// &Animation::setPriority);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// FRONTEND AND BACKEND
Animation::Animation()
: DescribedCreatable<Animation, Instance, sAnimation>()
{
setName(sAnimation);
}
bool Animation::isEmbeddedAsset() const
{
return !assetId.isHttp() && !assetId.isAsset();
}
shared_ptr<const KeyframeSequence> Animation::getKeyframeSequence() const
{
return getKeyframeSequence(this);
}
shared_ptr<const KeyframeSequence> Animation::getKeyframeSequence(const Instance* context) const
{
if (assetId.isNull())
{
return shared_ptr<KeyframeSequence>();
}
if (KeyframeSequenceProvider* keyframeSequenceProvider = ServiceProvider::create<KeyframeSequenceProvider>(context))
{
return keyframeSequenceProvider->getKeyframeSequence(assetId, this);
}
return shared_ptr<KeyframeSequence>();
}
void Animation::setAssetId(AnimationId value)
{
if (assetId != value)
{
assetId = value;
raisePropertyChanged(prop_AnimationId);
}
}
} // namespace Aya

View File

@@ -0,0 +1,43 @@
#pragma once
#include "Tree/Instance.hpp"
#include "Utility/AnimationId.hpp"
namespace Aya
{
extern const char* const sAnimation;
class KeyframeSequence;
class Animation : public DescribedCreatable<Animation, Instance, sAnimation>
{
private:
typedef DescribedCreatable<Animation, Instance, sAnimation> Super;
ContentId assetId;
public:
Animation();
shared_ptr<const KeyframeSequence> getKeyframeSequence() const;
shared_ptr<const KeyframeSequence> getKeyframeSequence(const Instance* context) const;
AnimationId getAssetId() const
{
return assetId;
}
void setAssetId(AnimationId value);
bool isEmbeddedAsset() const;
/*override*/ bool askSetParent(const Instance* instance) const
{
return true;
}
/*override*/ int getPersistentDataCost() const
{
return Super::getPersistentDataCost() + Instance::computeStringCost(getAssetId().toString());
}
};
} // namespace Aya

View File

@@ -0,0 +1,79 @@
#include "DataModel/AnimationController.hpp"
#include "DataModel/Workspace.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/Animator.hpp"
#include "World/World.hpp"
#include "Tree/Service.hpp"
#include "Kernel/Constants.hpp"
#include "Players.hpp"
namespace Aya
{
const char* const sAnimationController = "AnimationController";
static Reflection::BoundFuncDesc<AnimationController, shared_ptr<const Reflection::ValueArray>()> desc_GetPlayingAnimationTracks(
&AnimationController::getPlayingAnimationTracks, "GetPlayingAnimationTracks", Security::None);
static Reflection::BoundFuncDesc<AnimationController, shared_ptr<Instance>(shared_ptr<Instance>)> desc_LoadAnimation(
&AnimationController::loadAnimation, "LoadAnimation", "animation", Security::None);
static Reflection::EventDesc<AnimationController, void(shared_ptr<Instance>)> event_AnimationPlayed(
&AnimationController::animationPlayedSignal, "AnimationPlayed", "animationTrack");
REFLECTION_END();
AnimationController::AnimationController()
{
setName("AnimationController");
FASTLOG1(FLog::ISteppedLifetime, "AnimationController created - %p", this);
}
AnimationController::~AnimationController()
{
FASTLOG1(FLog::ISteppedLifetime, "AnimationController destroyed - %p", this);
}
bool AnimationController::askSetParent(const Instance* instance) const
{
return true;
}
void AnimationController::onStepped(const Stepped& event)
{
if (animator)
{
animator->onStepped(event);
}
}
Animator* AnimationController::getAnimator()
{
if (!animator)
{
animator = shared_ptr<Animator>(Creatable<Instance>::create<Aya::Animator>(this));
if (getParent() && getParent()->getClassNameStr() == "Model")
animator->setParent(this);
}
return animator.get();
}
shared_ptr<const Reflection::ValueArray> AnimationController::getPlayingAnimationTracks()
{
return getAnimator()->getPlayingAnimationTracks();
}
shared_ptr<Instance> AnimationController::loadAnimation(shared_ptr<Instance> instance)
{
return getAnimator()->loadAnimation(instance);
}
void AnimationController::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
Super::onServiceProvider(oldProvider, newProvider);
onServiceProviderIStepped(oldProvider, newProvider);
}
} // namespace Aya

View File

@@ -0,0 +1,44 @@
#pragma once
#include "Utility/SteppedInstance.hpp"
#include "Utility/RunStateOwner.hpp"
namespace Aya
{
// class Workspace;
// class Primitive;
// class PartInstance;
class Animator;
extern const char* const sAnimationController;
class AnimationController
: public DescribedCreatable<AnimationController, Instance, sAnimationController>
, public IStepped
{
private:
typedef DescribedCreatable<AnimationController, Instance, sAnimationController> Super;
shared_ptr<Animator> animator;
Animator* getAnimator();
// Instance
/*override*/ bool askSetParent(const Instance* instance) const;
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
// IStepped
/*override*/ void onStepped(const Stepped& event);
public:
AnimationController();
virtual ~AnimationController();
shared_ptr<Instance> loadAnimation(shared_ptr<Instance> animation);
shared_ptr<const Reflection::ValueArray> getPlayingAnimationTracks();
Aya::signal<void(shared_ptr<Instance>)> animationPlayedSignal;
};
} // namespace Aya

View File

@@ -0,0 +1,249 @@
#include "DataModel/AnimationTrack.hpp"
#include "DataModel/Animator.hpp"
#include "DataModel/AnimationTrackState.hpp"
#include "DataModel/Animation.hpp"
#include "DataModel/Workspace.hpp"
namespace Aya
{
namespace Reflection
{
template<>
const Type& Type::getSingleton<shared_ptr<AnimationTrack>>()
{
static TType<shared_ptr<AnimationTrack>> type("AnimationTrack");
return type;
}
} // namespace Reflection
const char* const sAnimationTrack = "AnimationTrack";
static Reflection::BoundFuncDesc<AnimationTrack, void(float, float, float)> desc_Play(
&AnimationTrack::play, "Play", "fadeTime", 0.1f, "weight", 1.0f, "speed", 1.0f, Security::None);
static Reflection::BoundFuncDesc<AnimationTrack, void(float)> desc_Stop(&AnimationTrack::stop, "Stop", "fadeTime", 0.1f, Security::None);
static Reflection::BoundFuncDesc<AnimationTrack, void(float, float)> desc_AdjustWeight(
&AnimationTrack::adjustWeight, "AdjustWeight", "weight", 1.0f, "fadeTime", 0.1f, Security::None);
static Reflection::BoundFuncDesc<AnimationTrack, void(float)> desc_AdjustSpeed(
&AnimationTrack::adjustSpeed, "AdjustSpeed", "speed", 1.0f, Security::None);
static Reflection::BoundFuncDesc<AnimationTrack, double(std::string)> desc_GetTimeOfKeyframe(
&AnimationTrack::getTimeOfKeyframe, "GetTimeOfKeyframe", "keyframeName", Security::None);
static Reflection::PropDescriptor<AnimationTrack, float> prop_Length(
"Length", category_Data, &AnimationTrack::getLength, NULL, Reflection::PropertyDescriptor::SCRIPTING);
static Reflection::PropDescriptor<AnimationTrack, bool> prop_IsPlaying(
"IsPlaying", category_Data, &AnimationTrack::getIsPlaying, NULL, Reflection::PropertyDescriptor::SCRIPTING);
static Reflection::PropDescriptor<AnimationTrack, float> prop_TimePosition(
"TimePosition", category_Data, &AnimationTrack::getTimePosition, &AnimationTrack::setTimePosition, Reflection::PropertyDescriptor::UI);
static Reflection::RefPropDescriptor<AnimationTrack, Animation> prop_Animation(
"Animation", category_Data, &AnimationTrack::getAnimation, NULL, Reflection::PropertyDescriptor::SCRIPTING);
const Reflection::EnumPropDescriptor<AnimationTrack, KeyframeSequence::Priority> prop_Priority(
"Priority", category_Data, &AnimationTrack::getPriority, &AnimationTrack::setPriority);
static Reflection::EventDesc<AnimationTrack, void(std::string)> event_Keyframe(
&AnimationTrack::keyframeReachedSignal, "KeyframeReached", "keyframeName");
static Reflection::EventDesc<AnimationTrack, void()> event_Stopped(&AnimationTrack::stoppedSignal, "Stopped");
REFLECTION_END();
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// FRONTEND AND BACKEND
AnimationTrack::AnimationTrack(shared_ptr<AnimationTrackState> animationTrackState, weak_ptr<Animator> animator, shared_ptr<Animation> anim)
: DescribedNonCreatable<AnimationTrack, Instance, sAnimationTrack>()
, animationTrackState(animationTrackState)
, animator(animator)
, animation(anim)
{
setName(sAnimationTrack);
keyframeReachedConnection = animationTrackState->keyframeReachedSignal.connect(boost::bind(&AnimationTrack::forwardKeyframeReached, this, _1));
stoppedConnection = animationTrackState->stoppedSignal.connect(boost::bind(&AnimationTrack::forwardStopped, this));
lockParent();
}
AnimationTrack::~AnimationTrack()
{
if (keyframeReachedConnection.connected())
keyframeReachedConnection.disconnect();
if (stoppedConnection.connected())
stoppedConnection.disconnect();
}
Animation* AnimationTrack::getAnimation() const
{
return animation.get();
}
float AnimationTrack::getLength() const
{
return animationTrackState->getDuration();
}
bool AnimationTrack::getIsPlaying() const
{
return animationTrackState->getIsPlaying();
}
KeyframeSequence::Priority AnimationTrack::getPriority() const
{
return animationTrackState->getPriority();
}
void AnimationTrack::setPriority(KeyframeSequence::Priority priority)
{
animationTrackState->setPriority(priority);
}
void AnimationTrack::forwardKeyframeReached(std::string keyFrame)
{
keyframeReachedSignal(keyFrame);
}
void AnimationTrack::forwardStopped()
{
stoppedSignal();
}
void AnimationTrack::play(float fadeTime, float weight, float speed)
{
localPlay(fadeTime, weight, speed);
if (shared_ptr<Animator> safeAnimator = animator.lock())
{
shared_ptr<Instance> me = shared_from<Instance>(this);
safeAnimator->replicateAnimationPlay(animation->getAssetId(), fadeTime, weight, speed, me);
}
}
void AnimationTrack::localPlay(float fadeTime, float weight, float speed)
{
if (shared_ptr<Animator> safeAnimator = animator.lock())
{
safeAnimator->reloadAnimation(animationTrackState);
animationTrackState->play(fadeTime, weight, speed);
}
}
void AnimationTrack::stop(float fadeTime)
{
localStop(fadeTime);
if (shared_ptr<Animator> safeAnimator = animator.lock())
{
safeAnimator->replicateAnimationStop(animation->getAssetId(), fadeTime);
}
}
void AnimationTrack::localStop(float fadeTime)
{
animationTrackState->stop(fadeTime);
}
void AnimationTrack::adjustWeight(float weight, float fadeTime)
{
if (shared_ptr<Animator> safeAnimator = animator.lock())
{
safeAnimator->reloadAnimation(animationTrackState);
animationTrackState->adjustWeight(weight, fadeTime);
}
}
void AnimationTrack::adjustSpeed(float speed)
{
double oldSpeed = animationTrackState->getSpeed();
localAdjustSpeed(speed);
if (oldSpeed != animationTrackState->getSpeed())
{
if (shared_ptr<Animator> safeAnimator = animator.lock())
{
safeAnimator->replicateAnimationSpeed(animation->getAssetId(), speed);
}
}
}
void AnimationTrack::localAdjustSpeed(float speed)
{
if (shared_ptr<Animator> safeAnimator = animator.lock())
{
safeAnimator->reloadAnimation(animationTrackState);
animationTrackState->adjustSpeed(speed);
}
}
const std::string AnimationTrack::getAnimationName() const
{
if (animation)
{
return animation->getName();
}
else
{
return "";
}
}
double AnimationTrack::getGameTime() const
{
shared_ptr<Animator> safeAnimator = animator.lock();
if (safeAnimator)
{
return safeAnimator->getGameTime();
}
else
{
return 0.0f;
}
}
double AnimationTrack::getTimePosition() const
{
return animationTrackState->getDurationClampedKeyframeTime(animationTrackState->getKeyframeAtTime(getGameTime()));
}
void AnimationTrack::setTimePosition(double timePosition)
{
double oldTime = getTimePosition();
localSetTimePosition(timePosition);
if (oldTime != getTimePosition())
{
shared_ptr<Animator> safeAnimator = animator.lock();
if (safeAnimator)
{
safeAnimator->replicateAnimationTimePosition(animation->getAssetId(), timePosition);
}
}
}
void AnimationTrack::localSetTimePosition(double timePosition)
{
double clampedKeyframeTime = animationTrackState->getDurationClampedKeyframeTime(timePosition);
animationTrackState->setKeyframeAtTime(getGameTime(), clampedKeyframeTime);
animationTrackState->resetKeyframeReachedDetection(clampedKeyframeTime);
}
double AnimationTrack::getTimeOfKeyframe(std::string keyframeName)
{
const KeyframeSequence* kfs = animationTrackState->getKeyframeSequence();
for (unsigned int index = 0; index < kfs->numChildren(); index++)
{
if (kfs->getChild(index)->getName() == keyframeName)
{
const Keyframe* keyframe = Instance::fastDynamicCast<const Keyframe>(kfs->getChild(index));
AYAASSERT(keyframe);
return keyframe->getTime();
}
}
throw Aya::runtime_error("Could not find a keyframe by that name!");
}
} // namespace Aya

View File

@@ -0,0 +1,65 @@
#pragma once
#include "Tree/Instance.hpp"
#include "DataModel/KeyframeSequence.hpp"
namespace Aya
{
class Animator;
class AnimationTrackState;
class Animation;
extern const char* const sAnimationTrack;
// A controller class that wraps an AnimationTrackState and allows for interaction with Lua
class AnimationTrack : public DescribedNonCreatable<AnimationTrack, Instance, sAnimationTrack>
{
protected:
// Keeps animationTrackState alive (in case we need to restart it)
shared_ptr<AnimationTrackState> animationTrackState;
// Keeps a weak ptr to our animator, so we can restart an animation if it has become stopped
weak_ptr<Animator> animator;
shared_ptr<Animation> animation;
Aya::signals::connection keyframeReachedConnection;
Aya::signals::connection stoppedConnection;
void forwardKeyframeReached(std::string);
void forwardStopped();
double getGameTime() const;
public:
AnimationTrack(shared_ptr<AnimationTrackState> animationTrackState, weak_ptr<Animator> animator, shared_ptr<Animation> anim);
~AnimationTrack();
void play(float fadeTime, float weight, float speed);
void localPlay(float fadeTime, float weight, float speed);
void stop(float fadeTime);
void localStop(float fadeTime);
void adjustWeight(float weight, float fadeTime);
void adjustSpeed(float speed);
void localAdjustSpeed(float speed);
float getLength() const;
bool getIsPlaying() const;
Animation* getAnimation() const;
double getTimePosition() const;
void setTimePosition(double timePosition);
void localSetTimePosition(double timePosition);
double getTimeOfKeyframe(std::string keyframeName);
KeyframeSequence::Priority getPriority() const;
void setPriority(KeyframeSequence::Priority priority);
const std::string getAnimationName() const;
Aya::signal<void(std::string)> keyframeReachedSignal;
Aya::signal<void()> stoppedSignal;
};
} // namespace Aya

View File

@@ -0,0 +1,386 @@
#include "DataModel/AnimationTrackState.hpp"
#include "DataModel/Animation.hpp"
#include "DataModel/Animator.hpp"
#include "DataModel/KeyframeSequence.hpp"
#include "Tree/Service.hpp"
#include "Utility/RunStateOwner.hpp"
#include "DataModel/Animation.hpp"
#include "DataModel/KeyframeSequenceProvider.hpp"
namespace Aya
{
const char* const sAnimationTrackState = "AnimationTrackState";
static const float autoStopFadeTime = 0.3f;
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float, float, float)> event_play(&AnimationTrackState::internalPlaySignal,
"PlayAnimation", "gameTime", "fadeTime", "weight", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY,
Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float)> event_stop(&AnimationTrackState::internalStopSignal, "StopAnimation",
"gameTime", "fadeTime", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float, float)> event_adjustWeight(
&AnimationTrackState::internalAdjustWeightSignal, "AdjustAnimationWeight", "gameTime", "weight", "fadeTime", Security::None,
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float)> event_adjustSpeed(&AnimationTrackState::internalAdjustSpeedSignal,
"AdjustAnimation", "gameTime", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(std::string)> event_Keyframe(&AnimationTrackState::keyframeReachedSignal,
"KeyframeReached", "keyframeName", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void()> event_Stopped(&AnimationTrackState::stoppedSignal, "Stopped", Security::None,
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
REFLECTION_END();
AnimationTrackState::AnimationTrackState(shared_ptr<const KeyframeSequence> keyframeSequence, weak_ptr<const Animator> animator)
: DescribedNonCreatable<AnimationTrackState, Instance, sAnimationTrackState>()
, startTime(0.0f)
, fadeStartTime(0.0f)
, fadeStartWeight(0.0f)
, fadeEndTime(0.0f)
, fadeEndWeight(0.0f)
, speed(1.0f)
, phase(0.0f)
, keyframeSequence(keyframeSequence)
, animator(animator)
, preKeyframe(-1)
, isPlaying(false)
, priority(KeyframeSequence::CORE)
, priorityOverridden(false)
{
setName(sAnimationTrackState);
internalPlaySignal.connect(boost::bind(&AnimationTrackState::onPlay, this, _1, _2, _3, _4));
internalStopSignal.connect(boost::bind(&AnimationTrackState::onStop, this, _1, _2));
internalAdjustWeightSignal.connect(boost::bind(&AnimationTrackState::onAdjustWeight, this, _1, _2, _3));
internalAdjustSpeedSignal.connect(boost::bind(&AnimationTrackState::onAdjustSpeed, this, _1, _2));
lockParent();
}
void AnimationTrackState::setAnimationTrack(shared_ptr<AnimationTrack> animationTrackIn)
{
animationTrack = animationTrackIn;
}
bool AnimationTrackState::isStopped(double time)
{
return (time >= fadeEndTime) && G3D::fuzzyEq(fadeEndWeight, 0);
}
double AnimationTrackState::getGameTime()
{
if (shared_ptr<const Animator> safeAnimator = animator.lock())
{
return safeAnimator->getGameTime();
}
// We've lost our animator, shut.it.down
keyframeSequence.reset();
return 0.0f;
}
double AnimationTrackState::getWeightAtTime(double time)
{
if (time >= fadeEndTime)
{
return fadeEndWeight;
}
else if (time <= fadeStartTime)
{
return fadeStartWeight;
}
else
{
// lerp between two.
double interval = (fadeEndTime - fadeStartTime);
return ((time - fadeStartTime) / interval) * fadeEndWeight + ((fadeEndTime - time) / interval) * fadeStartWeight;
}
}
double AnimationTrackState::getKeyframeAtTime(double time)
{
if (!inReverse())
{
return (time - startTime) * speed + phase;
}
else
{
return (getDuration() - (time - startTime) * -speed) + phase;
}
}
float AnimationTrackState::getDuration()
{
if (const KeyframeSequence* pSequence = getKeyframeSequence())
{
return pSequence->getDuration();
}
return 0.0f;
}
KeyframeSequence::Priority AnimationTrackState::getPriority() const
{
if (priorityOverridden)
{
return priority;
}
else
{
if (const KeyframeSequence* sequence = getKeyframeSequence())
{
return sequence->getPriority();
}
else
{
return KeyframeSequence::CORE;
}
}
}
void AnimationTrackState::setPriority(KeyframeSequence::Priority priorityIn)
{
priorityOverridden = true;
priority = priorityIn;
}
void AnimationTrackState::setKeyframeAtTime(double gameTime, double keyframetime)
{
double delta = keyframetime - getKeyframeAtTime(gameTime);
phase += delta;
}
void AnimationTrackState::onPlay(float gameTime, float fadeTime, float weight, float speed)
{
if (gameTime >= startTime)
{
phase = 0;
startTime = gameTime;
fadeStartTime = startTime;
fadeStartWeight = 0.0;
fadeEndTime = startTime + fadeTime;
fadeEndWeight = weight;
this->speed = speed;
lastKeyframeTime = 0;
preKeyframe = -1;
isPlaying = true;
// automated animation detection does not detect the first keyframe if the animation is running in reverse
if (inReverse())
{
const KeyframeSequence* kfs = getKeyframeSequence();
if (kfs->numChildren() > 0)
{
if (kfs->getChild(kfs->numChildren() - 1))
{
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
}
}
}
}
}
void AnimationTrackState::play(float fadeTime, float weight, float speed)
{
event_play.fireAndReplicateEvent(this, (float)getGameTime(), fadeTime, weight, speed);
}
void AnimationTrackState::onStop(float gameTime, float fadeTime)
{
fadeStartTime = gameTime;
fadeStartWeight = getWeightAtTime(gameTime);
fadeEndTime = gameTime + fadeTime;
fadeEndWeight = 0.0;
event_Stopped.fireAndReplicateEvent(this);
isPlaying = false;
}
void AnimationTrackState::stop(float fadeTime)
{
event_stop.fireAndReplicateEvent(this, (float)getGameTime(), fadeTime);
}
void AnimationTrackState::onAdjustWeight(float gameTime, float weight, float fadeTime)
{
fadeStartTime = gameTime;
fadeStartWeight = getWeightAtTime(gameTime);
fadeEndTime = gameTime + fadeTime;
fadeEndWeight = weight;
}
void AnimationTrackState::onAdjustSpeed(float gameTime, float speed)
{
if (speed != this->speed)
{
// if we reversed directions, we need to reset the preKeyframe
if ((this->speed >= 0 && speed < 0) || (this->speed < 0 && speed >= 0))
{
preKeyframe = -1;
}
// must adjust phase to prevent skipping.
double keyframetime = getKeyframeAtTime(gameTime);
this->speed = speed;
setKeyframeAtTime(gameTime, keyframetime);
}
}
void AnimationTrackState::adjustWeight(float weight, float fadeTime)
{
event_adjustWeight.fireAndReplicateEvent(this, (float)getGameTime(), weight, fadeTime);
}
void AnimationTrackState::adjustSpeed(float speed)
{
event_adjustSpeed.fireAndReplicateEvent(this, (float)getGameTime(), speed);
}
void AnimationTrackState::triggerKeyframeReachedSignal(const shared_ptr<Instance>& child, double minKeyframeTime, double maxKeyframeTime)
{
Keyframe* kf = Instance::fastDynamicCast<Keyframe>(child.get());
if (kf)
{
if (kf->getTime() > minKeyframeTime && kf->getTime() <= maxKeyframeTime)
{
event_Keyframe.fireAndReplicateEvent(this, kf->getName());
}
}
}
bool AnimationTrackState::inReverse()
{
return (speed < 0);
}
double AnimationTrackState::getDurationClampedKeyframeTime(double keyframeTime)
{
double duration = getDuration();
if (duration > 0.0)
{
if (keyframeTime < 0)
{
int durationsOff = (int)(fabs(keyframeTime) / duration) + 63;
keyframeTime = keyframeTime + durationsOff * duration;
}
else if (keyframeTime > duration)
{
int durationsOff = (int)(keyframeTime / duration);
keyframeTime = keyframeTime - durationsOff * duration;
}
}
return keyframeTime;
}
void AnimationTrackState::resetKeyframeReachedDetection(double keyframeTime)
{
lastKeyframeTime = keyframeTime;
preKeyframe = -1;
}
void AnimationTrackState::step(std::vector<PoseAccumulator>& jointposes, double time)
{
double keyframetime = getKeyframeAtTime(time);
float trackweight = (float)getWeightAtTime(time);
const KeyframeSequence* kfs = getKeyframeSequence();
double normKeyframeTime = keyframetime;
float duration = getKeyframeSequence()->getDuration();
kfs->apply(jointposes, lastKeyframeTime, keyframetime, trackweight);
if (!kfs->getLoop()) // play one time. give stop command after trigger of last keyframe.
{
// determine if we are finished with the animation
bool doneWithAnimation;
if (!inReverse())
doneWithAnimation = (lastKeyframeTime < duration && keyframetime >= duration);
else
doneWithAnimation = (lastKeyframeTime > 0 && keyframetime <= 0);
if (duration <= 0)
doneWithAnimation = false;
if (doneWithAnimation)
{
// fire signal for the last frame
if (!inReverse())
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
else
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
// should we use the internal event? or the external one?
// we will play it safe and use the internal one. it is easier to notice a problem with a non-stopped animation than with a
// double fire.
onStop((float)time, autoStopFadeTime);
}
}
else
{
// find out where in the looping animation we are -- even if we're moving backwards
normKeyframeTime = getDurationClampedKeyframeTime(keyframetime);
}
detectKeyframeReached(normKeyframeTime, getDurationClampedKeyframeTime(lastKeyframeTime));
lastKeyframeTime = keyframetime;
}
void AnimationTrackState::detectKeyframeReached(double animationTime, double lastAnimationTime)
{
if (!isPlaying)
return;
const KeyframeSequence* kfs = getKeyframeSequence();
int numKf = kfs->numChildren();
if (numKf == 0)
return;
// did we just loop? if so, fire the keyframes relevant. of course, only if the animation does loop
if (kfs->getLoop())
{
if (!inReverse())
{
if (lastAnimationTime > animationTime)
{
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
}
}
else
{
if (animationTime > lastAnimationTime)
{
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
}
}
}
// now we need to check the midway keyframes
for (int index = 0; index < numKf; index++)
{
const Keyframe* keyframe = Instance::fastDynamicCast<const Keyframe>(kfs->getChild(index));
AYAASSERT(keyframe);
bool passedKeyframe;
if (!inReverse())
passedKeyframe = (lastAnimationTime <= keyframe->getTime() && keyframe->getTime() < animationTime);
else
passedKeyframe = (lastAnimationTime >= keyframe->getTime() && keyframe->getTime() > animationTime);
if (passedKeyframe)
{
if (preKeyframe == index)
continue;
event_Keyframe.fireAndReplicateEvent(this, keyframe->getName());
preKeyframe = index;
break;
}
}
}
} // namespace Aya

View File

@@ -0,0 +1,112 @@
#pragma once
#include "Tree/Instance.hpp"
#include "Utility/ContentId.hpp"
#include "DataModel/KeyframeSequence.hpp"
namespace Aya
{
extern const char* const sAnimationTrackState;
class KeyframeSequence;
class Animator;
class AnimationTrack;
struct PoseAccumulator;
class AnimationTrackState : public DescribedNonCreatable<AnimationTrackState, Instance, sAnimationTrackState>
{
protected:
// Either set directly for solo/debugging or replicated by the AssetId
shared_ptr<const KeyframeSequence> keyframeSequence;
shared_ptr<AnimationTrack> animationTrack;
int preKeyframe;
// Keep a weak ptr to our parent
weak_ptr<const Animator> animator;
double startTime; // reset by the "play" function.
double speed;
double phase; // speed-independent adjustment to keyframetime.
double fadeStartTime;
double fadeStartWeight;
double fadeEndTime;
double fadeEndWeight;
bool isPlaying;
double getGameTime(); // helper
void onPlay(float gameTime, float fadeTime, float weight, float speed);
void onStop(float gameTime, float fadeTime);
void onAdjustWeight(float gameTime, float weight, float fadeTime);
void onAdjustSpeed(float gameTime, float speed);
bool inReverse();
void detectKeyframeReached(double animationTime, double lastAnimationTime);
KeyframeSequence::Priority priority;
bool priorityOverridden;
// helper for step()
void triggerKeyframeReachedSignal(const shared_ptr<Instance>& child, double minKeyframeTime, double maxKeyframeTime);
protected:
double lastKeyframeTime; // keep track of last played frame for keyframe events.
public:
AnimationTrackState(shared_ptr<const KeyframeSequence> keyframeSequence, weak_ptr<const Animator> animator);
~AnimationTrackState() {}
void play(float fadeTime, float weight, float speed);
void stop(float fadeTime);
void adjustWeight(float weight, float fadeTime);
void adjustSpeed(float speed);
// Should be private
Aya::remote_signal<void(float, float, float, float)> internalPlaySignal;
Aya::remote_signal<void(float, float)> internalStopSignal;
Aya::remote_signal<void(float, float, float)> internalAdjustWeightSignal;
Aya::remote_signal<void(float, float)> internalAdjustSpeedSignal;
Aya::remote_signal<void(std::string)> keyframeReachedSignal;
Aya::remote_signal<void()> stoppedSignal;
const KeyframeSequence* getKeyframeSequence() const
{
return keyframeSequence.get();
};
void setKeyframeSequence(shared_ptr<KeyframeSequence>);
double getWeightAtTime(double time);
double getKeyframeAtTime(double time);
void setKeyframeAtTime(double gameTime, double keyframeTime); // adjust phase to get animation on a specific keyframe.
double getSpeed()
{
return speed;
}
float getDuration();
bool isStopped(double time);
bool getIsPlaying() const
{
return isPlaying;
};
double getDurationClampedKeyframeTime(double keyframeTime);
KeyframeSequence::Priority getPriority() const;
void setPriority(KeyframeSequence::Priority priority);
void resetKeyframeReachedDetection(double keyframeTime);
void step(std::vector<PoseAccumulator>& jointposes, double time);
void setAnimationTrack(shared_ptr<AnimationTrack> animationTrackIn);
shared_ptr<AnimationTrack> getAnimationTrack() const
{
return animationTrack;
};
};
} // namespace Aya

View File

@@ -0,0 +1,553 @@
#include "DataModel/Animator.hpp"
#include "DataModel/Animation.hpp"
#include "DataModel/AnimationTrack.hpp"
#include "DataModel/AnimationTrackState.hpp"
#include "DataModel/JointInstance.hpp"
#include "DataModel/Workspace.hpp"
#include "World/SendPhysics.hpp"
#include "World/World.hpp"
#include "World/Primitive.hpp"
#include "World/Assembly.hpp"
#include "Players.hpp"
#include "NetworkOwner.hpp"
#include "Humanoid/Humanoid.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/AnimatableRootJoint.hpp"
#include "DataModel/AnimationController.hpp"
#include "Profiler.hpp"
DYNAMIC_FASTFLAGVARIABLE(ErrorOnFailedToLoadAnim, false)
namespace Aya
{
const char* const sAnimator = "Animator";
// Keep this interface in sync with the proxy one on Humanoid.
// Returns an AnimationTrack
static Reflection::BoundFuncDesc<Animator, shared_ptr<Instance>(shared_ptr<Instance>)> desc_LoadAnimation(
&Animator::loadAnimation, "LoadAnimation", "animation", Security::None);
// animation state replication
static Reflection::RemoteEventDesc<Animator, void(ContentId, float, float, float)> event_OnPlay(&Animator::onPlaySignal, "OnPlay", "animation",
"fadeTime", "weight", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<Animator, void(ContentId, float)> event_OnStop(&Animator::onStopSignal, "OnStop", "animation", "fadeTime",
Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<Animator, void(ContentId, float)> event_OnAdjustSpeed(&Animator::onAdjustSpeedSignal, "OnAdjustSpeed", "animation",
"speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<Animator, void(ContentId, float)> event_OnSetTimePosition(&Animator::onSetTimePositionSignal, "OnSetTimePosition",
"animation", "timePosition", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
REFLECTION_END();
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// FRONTEND AND BACKEND
Animator::Animator()
: DescribedCreatable<Animator, Instance, sAnimator>()
{
setName(sAnimator);
FASTLOG1(FLog::ISteppedLifetime, "Animator created - %p", this);
onPlaySignal.connect(boost::bind(&Animator::onPlay, this, _1, _2, _3, _4));
onStopSignal.connect(boost::bind(&Animator::onStop, this, _1, _2));
onAdjustSpeedSignal.connect(boost::bind(&Animator::onAdjustSpeed, this, _1, _2));
onSetTimePositionSignal.connect(boost::bind(&Animator::onSetTimePosition, this, _1, _2));
}
Animator::Animator(Instance* replicatingContainer)
: DescribedCreatable<Animator, Instance, sAnimator>()
{
shared_ptr<Instance> container = shared_from(replicatingContainer);
setName(sAnimator);
FASTLOG1(FLog::ISteppedLifetime, "Animator created - %p", this);
onPlaySignal.connect(boost::bind(&Animator::onPlay, this, _1, _2, _3, _4));
onStopSignal.connect(boost::bind(&Animator::onStop, this, _1, _2));
onAdjustSpeedSignal.connect(boost::bind(&Animator::onAdjustSpeed, this, _1, _2));
onSetTimePositionSignal.connect(boost::bind(&Animator::onSetTimePosition, this, _1, _2));
}
Animator::~Animator()
{
FASTLOG1(FLog::ISteppedLifetime, "Animator destroyed - %p", this);
}
Instance* Animator::getRootInstance()
{
if (getParent())
{
return getParent()->getParent();
}
return NULL;
}
void Animator::setupClumpChangedListener(Instance* rootInstance)
{
if (PartInstance* p = rootInstance->findFirstDescendantOfType<PartInstance>())
{
if (shared_ptr<PartInstance> rootPart = Instance::fastSharedDynamicCast<PartInstance>(p->getRootPart()))
clumpChangedConnection = rootPart->onDemandWrite()->clumpChangedSignal.connect(boost::bind(&Animator::onEvent_ClumpChanged, this, _1));
}
}
void Animator::reloadAnimation(shared_ptr<AnimationTrackState> animationTrackState)
{
if (animationTrackState->getKeyframeSequence())
{
if (std::find(activeAnimations.begin(), activeAnimations.end(), animationTrackState) == activeAnimations.end())
{
activeAnimations.push_back(animationTrackState);
}
}
}
float Animator::getGameTime() const
{
RunService* runservice = NULL;
runservice = ServiceProvider::find<RunService>(getParent());
if (runservice)
{
return (float)runservice->gameTime();
}
return 0.0f;
}
shared_ptr<Instance> Animator::loadAnimation(shared_ptr<Instance> instance)
{
shared_ptr<Animation> animation = Instance::fastSharedDynamicCast<Animation>(instance);
if (animation)
{
shared_ptr<const KeyframeSequence> kfs = animation->getKeyframeSequence(getParent());
if (!kfs)
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "Object must be in Workspace before loading animation.");
}
shared_ptr<Animator> sharedThis = shared_from(this);
shared_ptr<AnimationTrackState> trackState(Creatable<Instance>::create<AnimationTrackState>(kfs, sharedThis));
shared_ptr<AnimationTrack> track(Creatable<Instance>::create<AnimationTrack>(trackState, sharedThis, animation));
trackState->setAnimationTrack(track);
trackState->setName(instance->getName());
track->setName(instance->getName());
return track;
}
else
{
throw Aya::runtime_error("Argument error: must be an Animation object");
}
}
void Animator::onTrackStepped(
shared_ptr<AnimationTrackState> track, double time, KeyframeSequence::Priority priority, std::vector<PoseAccumulator>* poses)
{
if (track->getPriority() == priority)
{
if (poses->size() == 0) // delay creation
{
poses->reserve(animatableJoints.size());
for (size_t i = 0; i < animatableJoints.size(); ++i)
{
poses->push_back(PoseAccumulator(animatableJoints[i]));
}
}
track->step(*poses, time);
}
}
void Animator::appendAnimatableJointsRec(shared_ptr<Instance> instance, shared_ptr<Instance> exclude)
{
if (instance == exclude)
return;
Motor* joint = Instance::fastDynamicCast<Motor>(instance.get());
if (joint)
{
joint->setIsAnimatedJoint(true);
animatableJoints.push_back(JointPair(instance, joint));
}
}
void resetJointFlag(shared_ptr<Instance> instance)
{
Motor* joint = Instance::fastDynamicCast<Motor>(instance.get());
if (joint)
{
joint->setIsAnimatedJoint(false);
}
}
void Animator::calcAnimatableJoints(Instance* rootInstance, shared_ptr<Instance> exclude)
{
animatableJoints.clear();
if (rootInstance)
{
rootInstance->visitDescendants(boost::bind(&resetJointFlag, _1));
rootInstance->visitDescendants(boost::bind(&Animator::appendAnimatableJointsRec, this, _1, exclude));
}
}
void Animator::onStepped(const Stepped& event)
{
AYAPROFILER_SCOPE("Physics", "Animator::onStepped");
calcAnimatableJoints(getRootInstance());
if (animatableJoints.size() == 0 || activeAnimations.size() == 0)
return;
std::vector<PoseAccumulator> core;
std::vector<PoseAccumulator> idle;
std::vector<PoseAccumulator> movement;
std::vector<PoseAccumulator> action;
float gameTime = getGameTime();
for (auto iter = activeAnimations.begin(); iter != activeAnimations.end();)
{
auto& animTrackState = *iter;
if (!animTrackState->getKeyframeSequence() || animTrackState->isStopped(gameTime))
{
iter = activeAnimations.erase(iter);
}
else
{
++iter;
}
}
// checking to see if the server should take control of this object - NPC humanoid
PartInstance* part = testForServerLockPart.get();
if (part != NULL && activeAnimations.size() > 0 && Time::now<Time::Fast>() > serverLockTimer)
{
Primitive* prim = part->getPartPrimitive();
Assembly* a = prim->getAssembly();
if (a != NULL)
{
prim = a->getAssemblyPrimitive();
part = PartInstance::fromPrimitive(prim);
}
part->resetNetworkOwnerTime(3.0f); // wait
part->setNetworkOwnerAndNotify(Network::NetworkOwner::Server());
serverLockTimer = Time::now<Time::Fast>() + Time::Interval(2.5f);
}
std::for_each(activeAnimations.begin(), activeAnimations.end(),
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::CORE, &core));
std::for_each(activeAnimations.begin(), activeAnimations.end(),
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::IDLE, &idle));
std::for_each(activeAnimations.begin(), activeAnimations.end(),
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::MOVEMENT, &movement));
std::for_each(activeAnimations.begin(), activeAnimations.end(),
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::ACTION, &action));
// fold all poses into idle
if (idle.size() == 0)
idle = core;
else
{
for (size_t i = 0; i < core.size(); ++i)
{
core[i].pose.weight *= idle[i].pose.maskWeight; // here is where we use the blendweight. make the blend is that.
idle[i].pose = CachedPose::blendPoses(core[i].pose, idle[i].pose);
}
}
if (idle.size() == 0)
idle = movement;
else
{
for (size_t i = 0; i < movement.size(); ++i)
{
idle[i].pose.weight *= movement[i].pose.maskWeight; // here is where we use the blendweight. make the blend is that.
idle[i].pose = CachedPose::blendPoses(idle[i].pose, movement[i].pose);
}
}
if (idle.size() == 0)
idle = action;
else
{
for (size_t i = 0; i < action.size(); ++i)
{
idle[i].pose.weight *= action[i].pose.maskWeight; // here is where we use the blendweight. make the blend is that.
idle[i].pose = CachedPose::blendPoses(idle[i].pose, action[i].pose);
}
}
// now, apply poses to joints;
if (!idle.empty())
{
for (auto& poseAccumulator : idle)
{
auto pInst = poseAccumulator.joint.first.lock();
if (pInst)
{
poseAccumulator.joint.second->applyPose(poseAccumulator.pose);
}
}
}
else
{
for (auto& joint : animatableJoints)
{
auto pInst = joint.first.lock();
if (pInst)
{
joint.second->applyPose(CachedPose());
}
}
}
}
/*override*/ void Animator::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
Super::onServiceProvider(oldProvider, newProvider);
onServiceProviderIStepped(oldProvider, newProvider); // hooks up stepped
if (!newProvider)
return;
}
/*override*/ void Animator::verifySetParent(const Instance* instance) const
{
if (instance == NULL || fastDynamicCast<Humanoid>(instance) || fastDynamicCast<AnimationController>(instance))
{
Super::verifySetParent(instance);
}
else
{
throw Aya::runtime_error("Animator has to be placed under Humanoid or AnimationController!");
}
}
/*override*/ bool Animator::askAddChild(const Instance* instance) const
{
return Instance::fastDynamicCast<AnimationTrackState>(instance) != NULL;
}
void Animator::onAncestorChanged(const AncestorChanged& event)
{
Super::onAncestorChanged(event);
}
void Animator::onEvent_DescendantAdded(shared_ptr<Instance> descendant)
{
IAnimatableJoint* joint = dynamic_cast<IAnimatableJoint*>(descendant.get());
if (joint)
{
Instance* figure = getRootInstance();
calcAnimatableJoints(figure);
if (animatableJoints.size() > 0)
setupClumpChangedListener(figure);
}
}
void Animator::onEvent_DescendantRemoving(shared_ptr<Instance> descendant)
{
IAnimatableJoint* joint = dynamic_cast<IAnimatableJoint*>(descendant.get());
if (joint)
{
calcAnimatableJoints(getRootInstance(), descendant);
if (animatableJoints.size() == 0)
clumpChangedConnection.disconnect();
}
}
void Animator::onEvent_ClumpChanged(shared_ptr<Instance> instance)
{
AYAASSERT(animatableJoints.size() > 0);
PartInstance* part = Instance::fastDynamicCast<PartInstance>(instance.get());
if (Primitive* primitive = part->getPartPrimitive())
{
if (Assembly* assembly = primitive->getAssembly())
{
// check for new root assembly part
PartInstance* rootPart = PartInstance::fromAssembly(assembly);
if ((rootPart != part) && rootPart->isDescendantOf(getRootInstance()))
clumpChangedConnection =
rootPart->onDemandWrite()->clumpChangedSignal.connect(boost::bind(&Animator::onEvent_ClumpChanged, this, _1));
assembly->setAnimationControlled(true);
}
}
}
void Animator::onEvent_AncestorModified()
{
descentdantAdded = NULL;
descentdantRemoved = NULL;
ancestorChanged = NULL;
clumpChangedConnection = NULL;
Instance* figure = getRootInstance();
if (figure)
{
descentdantAdded = figure->onDemandWrite()->descendantAddedSignal.connect(boost::bind(&Animator::onEvent_DescendantAdded, this, _1));
descentdantRemoved = figure->onDemandWrite()->descendantRemovingSignal.connect(boost::bind(&Animator::onEvent_DescendantRemoving, this, _1));
ancestorChanged = figure->ancestryChangedSignal.connect(boost::bind(&Animator::onEvent_AncestorModified, this));
}
calcAnimatableJoints(figure);
// listen to assembly change only if we have animatable joints, so we can mark it as animation controlled
if (animatableJoints.size() > 0)
setupClumpChangedListener(figure);
}
void Animator::onPlay(ContentId animation, float fadeTime, float weight, float speed)
{
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
if (node == animationTrackMap.end())
{
passiveLoadAnimation(animation);
node = animationTrackMap.find(animation);
}
if (node != animationTrackMap.end())
{
if (Workspace::showActiveAnimationAsset)
{
StandardOut::singleton()->printf(MESSAGE_INFO, "Play animation: %s", node->first.c_str());
}
node->second->localPlay(fadeTime, weight, speed);
if (Workspace::showActiveAnimationAsset)
{
activeAnimation = node->second->getAnimationName();
activeAnimation.append(" (");
activeAnimation.append(node->first.c_str());
activeAnimation.append(")");
}
}
tellParentAnimationPlayed(node->second);
}
shared_ptr<const Reflection::ValueArray> Animator::getPlayingAnimationTracks()
{
shared_ptr<Reflection::ValueArray> playingAnimationTracks(Aya::make_shared<Reflection::ValueArray>());
std::list<shared_ptr<AnimationTrackState>>::iterator node;
for (node = activeAnimations.begin(); node != activeAnimations.end(); ++node)
{
shared_ptr<Instance> animationTrack(node->get()->getAnimationTrack());
playingAnimationTracks->push_back(animationTrack);
}
return playingAnimationTracks;
}
void Animator::tellParentAnimationPlayed(shared_ptr<Instance> animationTrack)
{
Instance* parent = getParent();
if (parent)
{
AnimationController* animationController = Instance::fastDynamicCast<AnimationController>(parent);
if (animationController)
{
animationController->animationPlayedSignal(animationTrack);
}
else
{
Humanoid* humanoid = Instance::fastDynamicCast<Humanoid>(parent);
if (humanoid)
{
humanoid->animationPlayedSignal(animationTrack);
}
}
}
}
void Animator::onStop(ContentId animation, float fadeTime)
{
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
if (node != animationTrackMap.end())
{
if (Workspace::showActiveAnimationAsset)
{
StandardOut::singleton()->printf(MESSAGE_INFO, "Stop animation: %s", node->first.c_str());
}
node->second->localStop(fadeTime);
}
}
void Animator::onAdjustSpeed(ContentId animation, float speed)
{
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
if (node != animationTrackMap.end())
{
node->second->localAdjustSpeed(speed);
}
}
void Animator::onSetTimePosition(ContentId animation, float timePosition)
{
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
if (node != animationTrackMap.end())
{
node->second->localSetTimePosition(timePosition);
}
}
void Animator::passiveLoadAnimation(ContentId animationId)
{
if (animationTrackMap.find(animationId) == animationTrackMap.end())
{
if (Workspace::showActiveAnimationAsset)
{
StandardOut::singleton()->printf(MESSAGE_INFO, "Add animation: %s", animationId.toString().c_str());
}
shared_ptr<Animation> sharedAnimation(Creatable<Instance>::create<Animation>());
sharedAnimation->setAssetId(animationId);
shared_ptr<Instance> animationTrackInstance = loadAnimation(sharedAnimation);
if (shared_ptr<AnimationTrack> animationTrack = boost::dynamic_pointer_cast<AnimationTrack>(animationTrackInstance))
{
animationTrackMap.insert(std::pair<ContentId, shared_ptr<AnimationTrack>>(animationId, animationTrack));
}
}
}
void Animator::replicateAnimationPlay(ContentId animation, float fadeTime, float weight, float speed, shared_ptr<Instance> track)
{
// replicate the event
event_OnPlay.replicateEvent(this, animation, fadeTime, weight, speed);
// call it locally with the track
tellParentAnimationPlayed(track);
}
void Animator::replicateAnimationStop(ContentId animation, float fadeTime)
{
event_OnStop.replicateEvent(this, animation, fadeTime);
}
void Animator::replicateAnimationSpeed(ContentId animation, float speed)
{
event_OnAdjustSpeed.replicateEvent(this, animation, speed);
}
void Animator::replicateAnimationTimePosition(ContentId animation, float timePosition)
{
event_OnSetTimePosition.replicateEvent(this, animation, timePosition);
}
} // namespace Aya

View File

@@ -0,0 +1,103 @@
#pragma once
#include "Tree/Instance.hpp"
#include "Utility/SteppedInstance.hpp"
#include "DataModel/KeyframeSequence.hpp" //for Animation::Priority, CachedPose
namespace Aya
{
class AnimatableRootJoint;
class AnimationTrackState;
class PartInstance;
class AnimationTrack;
extern const char* const sAnimator;
class Animator
: public DescribedCreatable<Animator, Instance, sAnimator>
, public IStepped
{
private:
typedef DescribedCreatable<Animator, Instance, sAnimator> Super;
Aya::Time serverLockTimer;
Instance* getRootInstance();
void setupClumpChangedListener(Instance* rootInstance);
protected:
std::map<ContentId, shared_ptr<AnimationTrack>> animationTrackMap;
std::string activeAnimation;
typedef std::vector<JointPair> AnimatableJointSet;
AnimatableJointSet animatableJoints;
void calcAnimatableJoints(Instance* rootInstance, shared_ptr<Instance> descendant = shared_ptr<Instance>());
void appendAnimatableJointsRec(shared_ptr<Instance> instance, shared_ptr<Instance> exclude);
scoped_ptr<AnimatableRootJoint> animatableRootJoint;
Aya::signals::scoped_connection descentdantAdded;
Aya::signals::scoped_connection descentdantRemoved;
Aya::signals::scoped_connection ancestorChanged;
Aya::signals::scoped_connection clumpChangedConnection;
void onEvent_DescendantAdded(shared_ptr<Instance> descendant);
void onEvent_DescendantRemoving(shared_ptr<Instance> descendant);
void onEvent_AncestorModified();
void onEvent_ClumpChanged(shared_ptr<Instance> instance);
std::list<shared_ptr<AnimationTrackState>> activeAnimations;
public:
Animator();
Animator(Instance* replicatingContainer); // use this to add Animator behavor to another Instance in the tree, like Humanoid.
virtual ~Animator();
float getGameTime() const;
shared_ptr<Instance> loadAnimation(shared_ptr<Instance> animation);
void reloadAnimation(shared_ptr<AnimationTrackState> animationTrackState);
shared_ptr<PartInstance> testForServerLockPart;
/*implement*/ void onStepped(const Stepped& event);
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
/*override*/ void verifySetParent(const Instance* instance) const;
/*override*/ bool askSetParent(const Instance* instance) const
{
return true;
}
/*override*/ bool askAddChild(const Instance* instance) const;
/*override*/ void onAncestorChanged(const AncestorChanged& event);
Aya::remote_signal<void(ContentId, float, float, float)> onPlaySignal;
Aya::remote_signal<void(ContentId, float)> onStopSignal;
Aya::remote_signal<void(ContentId, float)> onAdjustSpeedSignal;
Aya::remote_signal<void(ContentId, float)> onSetTimePositionSignal;
void onPlay(ContentId animation, float fadeTime, float weight, float speed);
void onStop(ContentId animation, float fadeTime);
void onAdjustSpeed(ContentId animation, float speed);
void onSetTimePosition(ContentId animation, float timePosition);
void passiveLoadAnimation(ContentId animation);
void replicateAnimationPlay(ContentId animation, float fadeTime, float weight, float speed, shared_ptr<Instance> track);
void replicateAnimationStop(ContentId animation, float fadeTime);
void replicateAnimationSpeed(ContentId animation, float speed);
void replicateAnimationTimePosition(ContentId animation, float timePosition);
std::string getActiveAnimation() const
{
return activeAnimation;
}
void tellParentAnimationPlayed(shared_ptr<Instance> animationTrack);
shared_ptr<const Reflection::ValueArray> getPlayingAnimationTracks();
private:
void onTrackStepped(
shared_ptr<AnimationTrackState> trackinst, double time, KeyframeSequence::Priority priority, std::vector<PoseAccumulator>* poses);
};
} // namespace Aya

View File

@@ -0,0 +1,198 @@
#include "DataModel/ArcHandles.hpp"
#include "World/Primitive.hpp"
#include "DataModel/Workspace.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/MouseCommand.hpp"
#include "Players.hpp"
#include "DrawAdorn.hpp"
#include "Utility/HitTest.hpp"
#include "Utility/Faces.hpp"
namespace Aya
{
const char* const sArcHandles = "ArcHandles";
static const Reflection::PropDescriptor<ArcHandles, Axes> prop_Axes("Axes", category_Data, &ArcHandles::getAxes, &ArcHandles::setAxes);
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseEnter(&ArcHandles::mouseEnterSignal, "MouseEnter", "axis",
Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseLeave(&ArcHandles::mouseLeaveSignal, "MouseLeave", "axis",
Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis, float, float)> event_MouseDrag(&ArcHandles::mouseDragSignal, "MouseDrag", "axis",
"relativeAngle", "deltaRadius", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseButton1Down(&ArcHandles::mouseButton1DownSignal, "MouseButton1Down",
"axis", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseButton1Up(&ArcHandles::mouseButton1UpSignal, "MouseButton1Up", "axis",
Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseEnter, "MouseEnter", MouseEnter);
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseLeave, "MouseLeave", MouseLeave);
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseDrag, "MouseDrag", MouseDrag);
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseButton1Down, "MouseButton1Down", MouseButton1Down);
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseButton1Up, "MouseButton1Up", MouseButton1Up);
REFLECTION_END();
ArcHandles::ArcHandles()
: DescribedCreatable<ArcHandles, HandlesBase, sArcHandles>("ArcHandles")
, axes(0x7)
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseEnterSignal, event_MouseEnter, MouseEnter)
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseLeaveSignal, event_MouseLeave, MouseLeave)
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseDragSignal, event_MouseDrag, MouseDrag)
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseButton1DownSignal, event_MouseButton1Down, MouseButton1Down)
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseButton1UpSignal, event_MouseButton1Up, MouseButton1Up)
{
CONNECT_EVENT_REPLICATOR(MouseEnter);
CONNECT_EVENT_REPLICATOR(MouseLeave);
CONNECT_EVENT_REPLICATOR(MouseDrag);
CONNECT_EVENT_REPLICATOR(MouseButton1Down);
CONNECT_EVENT_REPLICATOR(MouseButton1Up);
}
void ArcHandles::setAxes(Axes value)
{
if (axes != value)
{
axes = value;
raisePropertyChanged(prop_Axes);
}
}
int ArcHandles::getHandlesNormalIdMask() const
{
Faces faces;
faces.setNormalId(Aya::NORM_X, axes.getAxisByNormalId(Aya::NORM_X));
faces.setNormalId(Aya::NORM_Y, axes.getAxisByNormalId(Aya::NORM_Y));
faces.setNormalId(Aya::NORM_Z, axes.getAxisByNormalId(Aya::NORM_Z));
faces.setNormalId(Aya::NORM_X_NEG, axes.getAxisByNormalId(Aya::NORM_X));
faces.setNormalId(Aya::NORM_Y_NEG, axes.getAxisByNormalId(Aya::NORM_Y));
faces.setNormalId(Aya::NORM_Z_NEG, axes.getAxisByNormalId(Aya::NORM_Z));
return faces.normalIdMask;
}
void ArcHandles::setServerGuiObject()
{
Super::setServerGuiObject();
// Since we are running the server, we need to be watching for local listeners
eventReplicatorMouseEnter.setListenerMode(!mouseEnterSignal.empty());
eventReplicatorMouseLeave.setListenerMode(!mouseLeaveSignal.empty());
eventReplicatorMouseDrag.setListenerMode(!mouseDragSignal.empty());
eventReplicatorMouseButton1Down.setListenerMode(!mouseButton1DownSignal.empty());
eventReplicatorMouseButton1Up.setListenerMode(!mouseButton1UpSignal.empty());
}
void ArcHandles::onPropertyChanged(const Reflection::PropertyDescriptor& descriptor)
{
Super::onPropertyChanged(descriptor);
eventReplicatorMouseEnter.onPropertyChanged(descriptor);
eventReplicatorMouseLeave.onPropertyChanged(descriptor);
eventReplicatorMouseDrag.onPropertyChanged(descriptor);
eventReplicatorMouseButton1Down.onPropertyChanged(descriptor);
eventReplicatorMouseButton1Up.onPropertyChanged(descriptor);
}
GuiResponse ArcHandles::process(const shared_ptr<InputObject>& event)
{
if (event->isMouseEvent())
{
// ignore mouse if not visible
if (!getVisible())
return GuiResponse::notSunk();
switch (event->getUserInputType())
{
case InputObject::TYPE_MOUSEMOVEMENT:
{
if (mouseDownCaptureInfo)
{
float relangle;
float relradius;
float absangle;
float absradius;
if (getAngleRadiusFromHandle(
event, mouseDownCaptureInfo->hitNormalId, mouseDownCaptureInfo->hitPointWorld, relangle, relradius, absangle, absradius))
{
bool reverse = mouseDownCaptureInfo->hitNormalId >= NORM_X_NEG;
mouseDragSignal(Axes::normalIdToAxis(mouseDownCaptureInfo->hitNormalId), reverse ? -relangle : relangle, relradius);
}
}
Vector3 hitPointWorld;
NormalId hitNormalId;
if (findTargetHandle(event, hitPointWorld, hitNormalId))
{
if (mouseOver != NORM_UNDEFINED && mouseOver != hitNormalId)
{
mouseLeaveSignal(Axes::normalIdToAxis(mouseOver));
}
mouseOver = hitNormalId;
mouseEnterSignal(Axes::normalIdToAxis(hitNormalId));
}
else
{
if (mouseOver != NORM_UNDEFINED)
{
mouseLeaveSignal(Axes::normalIdToAxis(hitNormalId));
mouseOver = NORM_UNDEFINED;
}
}
return GuiResponse::notSunk();
}
case InputObject::TYPE_MOUSEBUTTON1:
{
AYAASSERT(event->isLeftMouseDownEvent() || event->isLeftMouseUpEvent());
if (event->isLeftMouseDownEvent())
{
Vector3 hitPointWorld;
NormalId hitNormalId;
if (findTargetHandle(event, hitPointWorld, hitNormalId))
{
// Capture the MouseDown information for later use
mouseDownCaptureInfo.reset(new MouseDownCaptureInfo(adornee.lock()->getLocation(), hitPointWorld, hitNormalId));
mouseButton1DownSignal(Axes::normalIdToAxis(hitNormalId));
return GuiResponse::sunk();
}
return GuiResponse::notSunk();
}
else if (event->isLeftMouseUpEvent())
{
// Clear our mouse down capture info
mouseDownCaptureInfo.reset();
Vector3 hitPointWorld;
NormalId hitNormalId;
if (findTargetHandle(event, hitPointWorld, hitNormalId))
{
mouseButton1UpSignal(Axes::normalIdToAxis(hitNormalId));
}
return GuiResponse::notSunk();
}
return GuiResponse::notSunk();
}
default:
break;
}
}
return GuiResponse::notSunk();
}
Aya::HandleType ArcHandles::getHandleType() const
{
return HANDLE_ROTATE;
}
} // namespace Aya

View File

@@ -0,0 +1,69 @@
#pragma once
#include "DataModel/HandlesBase.hpp"
#include "DataModel/EventReplicator.hpp"
#include "Base/IAdornable.hpp"
#include "Utility/Axes.hpp"
#include "HandleType.hpp"
namespace Aya
{
extern const char* const sArcHandles;
class ArcHandles : public DescribedCreatable<ArcHandles, HandlesBase, sArcHandles>
{
private:
typedef DescribedCreatable<ArcHandles, HandlesBase, sArcHandles> Super;
public:
ArcHandles();
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseEnterSignal;
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseLeaveSignal;
Aya::remote_signal<void(Aya::Vector3::Axis, float, float)> mouseDragSignal;
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseButton1DownSignal;
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseButton1UpSignal;
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseEnter, void(Aya::Vector3::Axis));
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseLeave, void(Aya::Vector3::Axis));
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseDrag, void(Aya::Vector3::Axis, float, float));
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseButton1Down, void(Aya::Vector3::Axis));
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseButton1Up, void(Aya::Vector3::Axis));
void setAxes(Axes value);
Axes getAxes() const
{
return axes;
}
////////////////////////////////////////////////////////////////////////////////////
//
// Instance
/*override*/ void onPropertyChanged(const Reflection::PropertyDescriptor& descriptor);
////////////////////////////////////////////////////////////////////////////////////
//
// GuiBase
/*override*/ GuiResponse process(const shared_ptr<InputObject>& event);
////////////////////////////////////////////////////////////////////////////////////
//
// HandlesBase
/*override*/ Aya::HandleType getHandleType() const;
protected:
////////////////////////////////////////////////////////////////////////////////////
//
// HandlesBase
/*override*/ int getHandlesNormalIdMask() const;
/*override*/ void setServerGuiObject();
private:
Axes axes;
};
} // namespace Aya

View File

@@ -0,0 +1,517 @@
#include "DataModel/AssetService.hpp"
#include "DataModel/ContentProvider.hpp"
#include "DataModel/MarketplaceService.hpp"
#include "DataModel/HttpRbxApiService.hpp"
#include "Players.hpp"
#include "Utility/StandardOut.hpp"
#include "Utility/LuaWebService.hpp"
#include "Xml/WebParser.hpp"
#include "Utility/Http.hpp"
#include <boost/lexical_cast.hpp>
DYNAMIC_FASTINTVARIABLE(CreatePlacePerMinute, 5)
DYNAMIC_FASTINTVARIABLE(CreatePlacePerPlayerPerMinute, 1)
DYNAMIC_FASTINTVARIABLE(SavePlacePerMinute, 10)
namespace Aya
{
const char* const sAssetService = "AssetService";
static Reflection::BoundFuncDesc<AssetService, void(std::string)> func_SetPlaceAccessUrl(
&AssetService::setPlaceAccessUrl, "SetPlaceAccessUrl", "accessUrl", Security::LocalUser);
static Reflection::BoundFuncDesc<AssetService, void(std::string)> func_SetAssetRevertUrl(
&AssetService::setAssetRevertUrl, "SetAssetRevertUrl", "revertUrl", Security::LocalUser);
static Reflection::BoundFuncDesc<AssetService, void(std::string)> func_setAssetVersionsUrl(
&AssetService::setAssetVersionsUrl, "SetAssetVersionsUrl", "versionsUrl", Security::LocalUser);
static Reflection::BoundYieldFuncDesc<AssetService, bool(int, int)> func_revertAsset(
&AssetService::revertAsset, "RevertAsset", "placeId", "versionNumber", Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, bool(int, AssetService::AccessType, shared_ptr<const Reflection::ValueArray>)>
func_updatePermissions(&AssetService::setPlacePermissions, "SetPlacePermissions", "placeId", "accessType", AssetService::EVERYONE, "inviteList",
shared_ptr<const Reflection::ValueArray>(), Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, shared_ptr<const Reflection::ValueTable>(int)> func_getPlacePermissions(
&AssetService::getPlacePermissions, "GetPlacePermissions", "placeId", Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, shared_ptr<const Reflection::ValueTable>(int, int)> func_getAssetVersions(
&AssetService::getAssetVersions, "GetAssetVersions", "placeId", "pageNum", 1, Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, int(int)> func_getCreatorAssetID(
&AssetService::getCreatorAssetID, "GetCreatorAssetID", "creationID", Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, int(std::string, int, std::string)> func_createPlace(
&AssetService::createPlaceAsync, "CreatePlaceAsync", "placeName", "templatePlaceID", "description", "", Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, int(shared_ptr<Instance>, std::string, int, std::string)> func_createPlaceInPlayerInventory(
&AssetService::createPlaceInPlayerInventoryAsync, "CreatePlaceInPlayerInventoryAsync", "player", "placeName", "templatePlaceID", "description",
"", Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, void()> func_savePlace(&AssetService::savePlaceAsync, "SavePlaceAsync", Security::None);
static Reflection::BoundYieldFuncDesc<AssetService, shared_ptr<Instance>()> func_getGamePlacesAsync(
&AssetService::getGamePlacesAsync, "GetGamePlacesAsync", Security::None);
REFLECTION_END();
namespace Reflection
{
template<>
EnumDesc<AssetService::AccessType>::EnumDesc()
: EnumDescriptor("AccessType")
{
addPair(AssetService::ME, "Me");
addPair(AssetService::FRIENDS, "Friends");
addPair(AssetService::EVERYONE, "Everyone");
addPair(AssetService::INVITEONLY, "InviteOnly");
}
template<>
AssetService::AccessType& Variant::convert<AssetService::AccessType>(void)
{
return genericConvert<AssetService::AccessType>();
}
} // namespace Reflection
template<>
bool StringConverter<AssetService::AccessType>::convertToValue(const std::string& text, AssetService::AccessType& value)
{
return Reflection::EnumDesc<AssetService::AccessType>::singleton().convertToValue(text.c_str(), value);
}
AssetService::AssetService()
: placeAccessUrl("")
, assetRevertUrl("")
, assetVersionsUrl("")
, createPlaceThrottle(&DFInt::CreatePlacePerMinute, &DFInt::CreatePlacePerPlayerPerMinute)
, savePlaceThrottle(&DFInt::SavePlacePerMinute)
{
setName(sAssetService);
}
void AssetService::setPlaceAccessUrl(std::string url)
{
placeAccessUrl = url;
}
void AssetService::setAssetRevertUrl(std::string url)
{
assetRevertUrl = url;
}
void AssetService::setAssetVersionsUrl(std::string url)
{
assetVersionsUrl = url;
}
std::string enumToString(AssetService::AccessType eType)
{
switch (eType)
{
case AssetService::ME:
return std::string("Me");
case AssetService::FRIENDS:
return std::string("Friends");
case AssetService::EVERYONE:
return std::string("Everyone");
case AssetService::INVITEONLY:
return std::string("InviteOnly");
default:
return std::string("Me");
}
}
void AssetService::processServiceResults(std::string* result, const std::exception* httpException,
boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (!httpException)
{
if (!result || result->empty())
{
errorFunction("Invalid response received");
}
shared_ptr<const Reflection::ValueTable> lTable;
std::stringstream resultStream;
resultStream << *result;
if (WebParser::parseJSONTable(*result, lTable))
resumeFunction(lTable);
else
errorFunction("AssetService error occurred");
}
else
{
errorFunction(Aya::format("Http exception occurred %s", httpException->what()));
}
}
static void CreatePlaceSuccessHelper(
weak_ptr<DataModel> dm, const std::string& response, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (shared_ptr<DataModel> sharedDM = dm.lock())
{
if (!response.empty())
{
int newPlaceID = 0;
try
{
newPlaceID = boost::lexical_cast<int>(response.c_str());
}
catch (boost::bad_lexical_cast const& e)
{
errorFunction(Aya::format("Game:CreatePlace response was not a valid placeId because %s", e.what()));
return;
}
if (newPlaceID > 0)
{
resumeFunction(newPlaceID);
}
else
{
errorFunction("Game:CreatePlace response was not a valid placeId because id <= 0");
}
}
else
{
errorFunction("Game:CreatePlace had no valid response");
}
}
}
static void CreatePlaceErrorHelper(weak_ptr<DataModel> dm, const std::string& error, boost::function<void(std::string)> errorFunction)
{
if (shared_ptr<DataModel> sharedDM = dm.lock())
{
if (!error.empty())
{
errorFunction(Aya::format("Game:CreatePlace received and error: %s.", error.c_str()));
}
else
{
errorFunction("Game:CreatePlace had no valid response in error helper.");
}
}
}
bool AssetService::checkCreatePlaceAccess(const std::string& placeName, int templatePlaceId, std::string& message)
{
DataModel* dm = DataModel::get(this);
AYAASSERT(dm);
if (!Network::Players::backendProcessing(this))
{
message = "CreatePlaceAsync can only be called from a server script, aborting create function";
return false;
}
if (placeName.empty())
{
message = "CreatePlaceAsync placeName argument is empty!";
return false;
}
if (templatePlaceId <= 0)
{
message = "CreatePlaceAsync templatePlaceId <= 0, should be a positive value";
return false;
}
if (dm->getPlaceID() <= 0)
{
message = "CreatePlaceAsync called on a Place with id <= 0, place should be opened with Edit button to access CreatePlace";
return false;
}
if (!createPlaceThrottle.checkLimit(Network::Players::getPlayerCount(this)))
{
message = "CreatePlaceAsync requests limit reached";
return false;
}
if (LuaWebService* lws = dm->create<LuaWebService>())
{
if (!lws->isApiAccessEnabled())
{
message = "Studio API access is not enabled. Enable it by going to the Game Settings page.";
return false;
}
}
return true;
}
void AssetService::createPlaceAsync(std::string placeName, int templatePlaceId, std::string desc, boost::function<void(int)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
std::string message;
if (!checkCreatePlaceAccess(placeName, templatePlaceId, message))
{
errorFunction(message);
return;
}
createPlaceAsyncInternal(true, placeName, templatePlaceId, desc, shared_ptr<Instance>(), resumeFunction, errorFunction);
}
void AssetService::createPlaceInPlayerInventoryAsync(shared_ptr<Instance> player, std::string placeName, int templatePlaceId, std::string desc,
boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
std::string message;
if (!checkCreatePlaceAccess(placeName, templatePlaceId, message))
{
errorFunction(message);
return;
}
MarketplaceService* marketPlaceService = ServiceProvider::create<MarketplaceService>(this);
marketPlaceService->launchClientLuaDialog("Do you allow game to create new place in your inventory?", "Yes", "No", player,
boost::bind(&AssetService::createPlaceAsyncInternal, this, _1, placeName, templatePlaceId, desc, _2, resumeFunction, errorFunction));
}
void AssetService::createPlaceAsyncInternal(bool check, std::string placeName, int templatePlaceId, std::string desc,
shared_ptr<Instance> playerInstance, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (!check)
{
StandardOut::singleton()->print(MESSAGE_WARNING, "User didn't allow place creation");
resumeFunction(-1);
return;
}
shared_ptr<Network::Player> player = fastSharedDynamicCast<Network::Player>(playerInstance);
if (player && player->getUserID() <= 0)
{
errorFunction("CreatePlaceAsync requires player to be logged in");
return;
}
if (player)
{
Network::Players* players = ServiceProvider::find<Network::Players>(this);
if (!players || players->getPlayerByID(player->getUserID()) != player)
{
errorFunction("Player no longer valid");
return;
}
}
DataModel* dm = DataModel::get(this);
std::string parameters =
Aya::format("?currentPlaceId=%d&placeName=%s&templatePlaceId=%d", dm->getPlaceID(), Aya::Http::urlEncode(placeName).c_str(), templatePlaceId);
std::string baseUrl = ServiceProvider::create<ContentProvider>(this)->getApiBaseUrl();
std::string playerParameter = "";
if (player)
playerParameter = Aya::format("&playerId=%i", player->getUserID());
try
{
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
{
// now post to the website
Http createHttp((baseUrl + "universes/new-place" + parameters + playerParameter).c_str());
#ifndef _WIN32
createHttp.setAuthDomain(baseUrl);
#endif
std::string postString("CreatePlacePost");
apiService->postAsync(createHttp, postString, Aya::PRIORITY_SERVER_ELEVATED, HttpService::TEXT_PLAIN,
boost::bind(&CreatePlaceSuccessHelper, shared_from(dm), _1, resumeFunction, errorFunction),
boost::bind(&CreatePlaceErrorHelper, shared_from(dm), _1, errorFunction));
}
}
catch (base_exception& e)
{
errorFunction(Aya::format("Game:CreatePlace failed because %s", e.what()));
}
}
void AssetService::savePlaceAsync(boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction)
{
DataModel* dm = DataModel::get(this);
dm->checkFetchExperimentalFeatures();
if (dm->getPlaceID() <= 0)
{
errorFunction("Game:SavePlace placeID is not valid!");
return;
}
if (!savePlaceThrottle.checkLimit())
{
errorFunction("Game:SavePlace requests limit reached");
return;
}
if (Network::Players::frontendProcessing(this))
{
errorFunction("Game:SavePlace can only be called from a server script, aborting save function");
return;
}
else if (Network::Players::backendProcessing(this))
{
if (LuaWebService* lws = dm->create<LuaWebService>())
{
if (!lws->isApiAccessEnabled())
{
errorFunction("Studio API access is not enabled. Enable it by going to the Game Settings page.");
return;
}
}
// construct the url
std::string parameters = "?assetId=" + boost::lexical_cast<std::string>(dm->getPlaceID());
parameters += "&isAppCreation=true";
std::string baseUrl = ServiceProvider::create<ContentProvider>(this)->getBaseUrl();
// save the place
dm->uploadPlace(baseUrl + "ide/publish/UploadExistingAsset" + parameters, SAVE_ALL, resumeFunction, errorFunction);
}
else
{
errorFunction("Game:SavePlace could not determine if client or server");
return;
}
}
void AssetService::getAssetVersions(int assetId, int pageNum, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
Http http(Aya::format(assetVersionsUrl.c_str(), assetId, pageNum));
http.get(boost::bind(&AssetService::processServiceResults, this, _1, _2, resumeFunction, errorFunction));
}
void AssetService::getPlacePermissions(
int placeId, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
Http http(Aya::format(placeAccessUrl.c_str(), placeId));
http.get(boost::bind(&AssetService::processServiceResults, this, _1, _2, resumeFunction, errorFunction));
}
void AssetService::httpPostHelper(std::string* response, std::exception* httpException, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
if (!httpException)
{
if (!response)
resumeFunction(false);
if (*response == "")
resumeFunction(false);
else
resumeFunction(true);
}
else
{
errorFunction(httpException->what());
}
}
void AssetService::revertAsset(
int assetId, int versionNumber, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
Http http(Aya::format(assetRevertUrl.c_str(), assetId, versionNumber));
std::string in;
http.post(
in, Http::kContentTypeDefaultUnspecified, true, boost::bind(&AssetService::httpPostHelper, this, _1, _2, resumeFunction, errorFunction));
}
void AssetService::setPlacePermissions(int placeId, AccessType type, shared_ptr<const Reflection::ValueArray> inviteList,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
std::string url = placeAccessUrl;
url = Aya::format(url.append("/update?").c_str(), placeId);
url = Aya::format(url.append("access=%s").c_str(), enumToString(type).c_str());
if (type == INVITEONLY && inviteList)
{
for (Reflection::ValueArray::const_iterator iter = inviteList->begin(); iter != inviteList->end(); ++iter)
{
std::stringstream convert;
convert << iter->get<std::string>();
url.append("&players=").append(convert.str());
}
}
Http http(url);
std::string in;
http.post(
in, Http::kContentTypeDefaultUnspecified, true, boost::bind(&AssetService::httpPostHelper, this, _1, _2, resumeFunction, errorFunction));
}
void AssetService::getCreatorAssetIDSuccessHelper(
std::string response, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (response.length() == 0)
{
resumeFunction(0);
return;
}
int creatorID = 0;
try
{
creatorID = boost::lexical_cast<int>(response);
}
catch (boost::bad_lexical_cast const& e)
{
errorFunction(Aya::format("AssetService:GetCreatorAssetID response could not convert to number because %s", e.what()));
return;
}
if (creatorID <= 0)
{
errorFunction("AssetService:GetCreatorAssetID response converted but is an invalid assetID");
return;
}
resumeFunction(creatorID);
}
void AssetService::getCreatorAssetIDErrorHelper(std::string error, boost::function<void(std::string)> errorFunction)
{
if (error.length() == 0)
{
errorFunction("AssetService::GetCreatorAssetID did not get a response or an error.");
}
errorFunction(Aya::format("AssetService:GetCreatorAssetID error: %s", error.c_str()));
}
void AssetService::getCreatorAssetID(int creationID, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (creationID <= 0)
{
errorFunction("creationID is not a valid number (should be a positive integer)");
return;
}
std::string baseUrl = Aya::ServiceProvider::create<ContentProvider>(this)->getApiBaseUrl();
std::string parameters = Aya::format("?creationID=%d", creationID);
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
{
apiService->getAsync("GetCreatorAssetID" + parameters, true, Aya::PRIORITY_DEFAULT,
boost::bind(&AssetService::getCreatorAssetIDSuccessHelper, this, _1, resumeFunction, errorFunction),
boost::bind(&AssetService::getCreatorAssetIDErrorHelper, this, _1, errorFunction));
}
}
void AssetService::getGamePlacesAsync(boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
std::string fetchUrl;
ContentProvider* cp = DataModel::get(this)->find<ContentProvider>();
if (cp)
fetchUrl = format("%suniverses/get-universe-places?placeid=%i", cp->getApiBaseUrl().c_str(), DataModel::get(this)->getPlaceID());
shared_ptr<Pages> pagination = Creatable<Instance>::create<StandardPages>(weak_from(DataModel::get(this)), fetchUrl, "Places");
pagination->fetchNextChunk(boost::bind(resumeFunction, pagination), errorFunction);
}
} // namespace Aya

View File

@@ -0,0 +1,68 @@
#pragma once
#include "Tree/Instance.hpp"
#include "Tree/Service.hpp"
#include "RunningAverage.hpp"
#include "DataModel.hpp"
namespace Aya
{
extern const char* const sAssetService;
class AssetService
: public DescribedNonCreatable<AssetService, Instance, sAssetService>
, public Service
{
ThrottlingHelper createPlaceThrottle, savePlaceThrottle;
public:
enum AccessType
{
ME = 0,
FRIENDS = 1,
EVERYONE = 2,
INVITEONLY = 3,
};
AssetService();
void setPlaceAccessUrl(std::string url);
void setAssetRevertUrl(std::string url);
void setAssetVersionsUrl(std::string url);
void revertAsset(int assetId, int versionNumber, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void getAssetVersions(int assetId, int pageNum, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void getPlacePermissions(int placeId, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void setPlacePermissions(int placeId, AccessType type, shared_ptr<const Reflection::ValueArray> inviteList,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void getCreatorAssetID(int creationID, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
void createPlaceAsync(std::string placeName, int templatePlaceId, std::string desc, boost::function<void(int)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void createPlaceInPlayerInventoryAsync(shared_ptr<Instance> player, std::string placeName, int templatePlaceId, std::string desc,
boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
void savePlaceAsync(boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
void getGamePlacesAsync(boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
private:
std::string placeAccessUrl;
std::string assetRevertUrl;
std::string assetVersionsUrl;
void httpPostHelper(std::string* response, std::exception* httpException, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void processServiceResults(std::string* result, const std::exception* httpException,
boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction, boost::function<void(std::string)> errorFunction);
void getCreatorAssetIDSuccessHelper(
std::string response, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
void getCreatorAssetIDErrorHelper(std::string error, boost::function<void(std::string)> errorFunction);
void createPlaceAsyncInternal(bool check, std::string placeName, int templatePlaceId, std::string desc, shared_ptr<Instance> player,
boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
bool checkCreatePlaceAccess(const std::string& placeName, int templatePlaceId, std::string& message);
};
} // namespace Aya

View File

@@ -0,0 +1,432 @@
#if 1 // disable until we are ready for new joint schema
#include "DataModel/Attachment.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/Surface.hpp"
#include "DataModel/Workspace.hpp"
#include "Draw.hpp"
#include "DrawAdorn.hpp"
#include "Base/Adorn.hpp"
#include "World/Primitive.hpp"
#include "Utility/Color.hpp"
// #define ENABLE_AXES_API
namespace Aya
{
using namespace Reflection;
const char* const sAttachment = "Attachment";
REFLECTION_BEGIN()
// Streaming
Reflection::PropDescriptor<Attachment, CoordinateFrame> Attachment::prop_Frame(
"CFrame", category_Data, &Attachment::getFrameInPart, &Attachment::setFrameInPart, PropertyDescriptor::STANDARD);
// UI
static const PropDescriptor<Attachment, Vector3> prop_Position(
"Position", category_Data, &Attachment::getPivotInPart, &Attachment::setPivotInPart, PropertyDescriptor::UI);
static const PropDescriptor<Attachment, Vector3> prop_Rotation(
"Rotation", category_Data, &Attachment::getEulerAnglesInPart, &Attachment::setEulerAnglesInPart, PropertyDescriptor::UI);
static const PropDescriptor<Attachment, Vector3> prop_WorldPosition(
"WorldPosition", "Derived Data", &Attachment::getPivotInWorld, NULL, PropertyDescriptor::UI);
static const PropDescriptor<Attachment, Vector3> prop_WorldRotation(
"WorldRotation", "Derived Data", &Attachment::getEulerAnglesInWorld, NULL, PropertyDescriptor::UI);
#ifdef ENABLE_AXES_API
static const PropDescriptor<Attachment, Vector3> prop_Axis("Axis", "Derived Data", &Attachment::getAxisInPart, NULL, PropertyDescriptor::UI);
static const PropDescriptor<Attachment, Vector3> prop_SecondaryAxis(
"SecondaryAxis", "Derived Data", &Attachment::getSecondaryAxisInPart, NULL, PropertyDescriptor::UI);
static const PropDescriptor<Attachment, Vector3> prop_WorldAxis(
"WorldAxis", "Derived Data", &Attachment::getAxisInWorld, NULL, PropertyDescriptor::UI);
static const PropDescriptor<Attachment, Vector3> prop_WorldSecondaryAxis(
"WorldSecondaryAxis", "Derived Data", &Attachment::getSecondaryAxisInWorld, NULL, PropertyDescriptor::UI);
static BoundFuncDesc<Attachment, void(Vector3, Vector3)> func_setAxes(&Attachment::setAxes, "SetAxes", "axis0", "axis1", Security::None);
static BoundFuncDesc<Attachment, void(Vector3)> func_setAxis(&Attachment::setAxisInPart, "SetAxis", "axis", Security::None);
#endif
#ifdef AYA_ATTACHMENT_LOCKING
const PropDescriptor<Attachment, bool> Attachment::prop_Locked(
"Locked", category_Behavior, &Attachment::getLocked, &Attachment::setLocked, PropertyDescriptor::UI);
#endif
REFLECTION_END()
float Attachment::toolAdornHandleRadius = 0.2f;
float Attachment::toolAdornMajorAxisSize = 0.4f;
float Attachment::toolAdornMajorAxisRadius = 0.07f;
float Attachment::toolAdornMinorAxisSize = 0.3f;
float Attachment::toolAdornMinorAxisRadius = 0.04f;
float Attachment::adornRadius = 0.2f;
Attachment::Attachment()
: visible(false)
, locked(false)
, axisDirectionInPart(1, 0, 0)
, secondaryAxisDirectionInPart(0, 1, 0)
, pivotPositionInPart(1, 0, 0)
{
setName("Attachment");
}
void Attachment::setVisible(bool value)
{
if (value != visible)
{
visible = value;
}
}
Vector3 Attachment::getEulerAnglesInPart() const
{
Matrix3 o = getOrientationInPart();
Vector3 r;
o.toEulerAnglesXYZ(r.x, r.y, r.z);
return Vector3(Math::radiansToDegrees(r.x), Math::radiansToDegrees(r.y), Math::radiansToDegrees(r.z));
}
Vector3 Attachment::getEulerAnglesInWorld() const
{
Matrix3 o = getOrientationInWorld();
Vector3 r;
o.toEulerAnglesXYZ(r.x, r.y, r.z);
return Vector3(Math::radiansToDegrees(r.x), Math::radiansToDegrees(r.y), Math::radiansToDegrees(r.z));
}
void Attachment::setEulerAnglesInPart(const Vector3& v)
{
Matrix3 o = Matrix3::fromEulerAnglesXYZ(Math::degreesToRadians(v.x), Math::degreesToRadians(v.y), Math::degreesToRadians(v.z));
setOrientationInPartInternal(o);
raiseChanged(prop_Frame);
}
void Attachment::setOrientationInPartInternal(const Matrix3& o)
{
setAxisInPartInternal(o.column(0));
setSecondaryAxisInPartInternal(o.column(1));
raiseChanged(prop_Rotation);
raiseChanged(prop_WorldRotation);
}
Matrix3 Attachment::getOrientationInPart() const
{
Matrix3 o;
o.setColumn(0, axisDirectionInPart);
o.setColumn(1, secondaryAxisDirectionInPart);
o.setColumn(2, o.column(0).cross(o.column(1)));
return o;
}
Matrix3 Attachment::getOrientationInWorld() const
{
Matrix3 o;
CoordinateFrame partCF = getParentFrame();
o.setColumn(0, partCF.vectorToWorldSpace(axisDirectionInPart));
o.setColumn(1, partCF.vectorToWorldSpace(secondaryAxisDirectionInPart));
o.setColumn(2, o.column(0).cross(o.column(1)));
return o;
}
void Attachment::setPivotInPart(const Vector3& pivot)
{
pivotPositionInPart = pivot;
raiseChanged(prop_Position);
raiseChanged(prop_WorldPosition);
raiseChanged(prop_Frame);
}
Vector3 Attachment::getPivotInPart() const
{
return pivotPositionInPart;
}
Vector3 Attachment::getPivotInWorld(void) const
{
return getParentFrame().pointToWorldSpace(pivotPositionInPart);
}
void Attachment::setAxisInPartInternal(const Vector3& _axis)
{
if (_axis.magnitude() < 0.0001f)
{
return;
}
axisDirectionInPart = _axis;
axisDirectionInPart.unitize();
#ifdef ENABLE_AXES_API
raiseChanged(prop_Axis);
raiseChanged(prop_WorldAxis);
#endif
// This will orthonormalize the secondary axis to the axis
setSecondaryAxisInPartInternal(secondaryAxisDirectionInPart);
}
void Attachment::setAxisInPart(Vector3 _axis)
{
setAxisInPartInternal(_axis);
raiseChanged(prop_Rotation);
raiseChanged(prop_WorldRotation);
raiseChanged(prop_Frame);
}
Vector3 Attachment::getAxisInPart(void) const
{
return axisDirectionInPart;
}
Vector3 Attachment::getAxisInWorld(void) const
{
return getParentFrame().vectorToWorldSpace(axisDirectionInPart);
}
Vector3 Attachment::getSecondaryAxisInWorld(void) const
{
return getParentFrame().vectorToWorldSpace(secondaryAxisDirectionInPart);
}
Vector3 Attachment::getSecondaryAxisInPart(void) const
{
return secondaryAxisDirectionInPart;
}
void Attachment::setSecondaryAxisInPartInternal(const Vector3& _axis2)
{
if (_axis2.magnitude() < 0.00001f)
{
return;
}
Vector3 axis2 = _axis2;
Vector3 axis;
axis = axisDirectionInPart;
axis2.unitize();
if (fabsf(axis2.dot(axis)) < 0.999f)
{
// The input secondary axis is not completely parallel to the axis, we can project
axis2 = axis2 - axis2.dot(axis) * axis;
}
else
{
// The secondary axis is parallel with the axis, we need to make a choice
if (fabsf(axis.dot(Vector3::unitY())) < sqrtf(2.0f) * 0.5f)
{
// If the axis makes more than 45deg angle with Y axis, project the Y axis on the orthogonal plane of the axis
Vector3 t = Vector3::unitY().cross(axis);
axis2 = axis.cross(t);
}
else
{
if (fabsf(axis.x) + 0.1 > fabsf(axis.z))
{
Vector3 t = Vector3::unitZ().cross(axis);
axis2 = axis.cross(t);
}
else
{
Vector3 t = Vector3::unitX().cross(axis);
axis2 = axis.cross(t);
}
}
}
axis2.unitize();
AYAASSERT(fabsf(axis2.dot(axis)) < 0.00001f);
secondaryAxisDirectionInPart = axis2;
#ifdef ENABLE_AXES_API
raiseChanged(prop_SecondaryAxis);
raiseChanged(prop_WorldSecondaryAxis);
#endif
}
void Attachment::setAxes(Vector3 axis, Vector3 axis2)
{
setAxisInPartInternal(axis);
setSecondaryAxisInPartInternal(axis2);
raiseChanged(prop_Rotation);
raiseChanged(prop_WorldRotation);
raiseChanged(prop_Frame);
}
CoordinateFrame Attachment::getFrameInPart() const
{
CoordinateFrame cf;
cf.translation = getPivotInPart();
cf.rotation = getOrientationInPart();
return cf;
}
void Attachment::setFrameInPart(const CoordinateFrame& frame)
{
setOrientationInPartInternal(frame.rotation);
setPivotInPart(frame.translation);
raiseChanged(prop_Frame);
}
CoordinateFrame Attachment::getFrameInWorld() const
{
CoordinateFrame cf;
cf.translation = getPivotInWorld();
cf.rotation = getOrientationInWorld();
return cf;
}
CoordinateFrame Attachment::getParentFrame() const
{
if (!locked)
{
const PartInstance* part = Instance::fastDynamicCast<const PartInstance>(getParent());
if (part)
{
return part->getCoordinateFrame();
}
}
return CoordinateFrame();
}
void Attachment::setLocked(bool value)
{
if (!locked && value)
{
Vector3 pivotInWorld = getPivotInWorld();
Vector3 axisInWorld = getAxisInWorld();
Vector3 secondaryAxisInWorld = getSecondaryAxisInWorld();
locked = true;
pivotPositionInPart = pivotInWorld;
axisDirectionInPart = axisInWorld;
secondaryAxisDirectionInPart = secondaryAxisInWorld;
#ifdef AYA_ATTACHMENT_LOCKING
raiseChanged(prop_Locked);
#endif
raiseChanged(prop_Position);
raiseChanged(prop_WorldPosition);
raiseChanged(prop_Rotation);
raiseChanged(prop_WorldRotation);
raiseChanged(prop_Frame);
}
if (locked && !value)
{
Vector3 pivotInPart = getPivotInPart();
Vector3 axisInPart = getAxisInPart();
Vector3 secondaryAxisInPart = getSecondaryAxisInPart();
locked = false;
CoordinateFrame partFrame = getParentFrame();
pivotPositionInPart = partFrame.pointToObjectSpace(pivotInPart);
axisDirectionInPart = partFrame.vectorToObjectSpace(axisInPart);
secondaryAxisDirectionInPart = partFrame.vectorToObjectSpace(secondaryAxisInPart);
#ifdef AYA_ATTACHMENT_LOCKING
raiseChanged(prop_Locked);
#endif
raiseChanged(prop_Position);
raiseChanged(prop_WorldPosition);
raiseChanged(prop_Rotation);
raiseChanged(prop_WorldRotation);
raiseChanged(prop_Frame);
}
}
void Attachment::render3dAdorn(Adorn* adorn)
{
if (!visible)
return;
adorn->setMaterial(Adorn::Material_Default);
adorn->setObjectToWorldMatrix(getFrameInWorld());
Sphere sphere;
sphere.radius = adornRadius;
adorn->sphere(sphere, Color3::green());
}
static void renderToolAdornAxes(Adorn* adorn, const CoordinateFrame& cf)
{
{
CoordinateFrame cf1 = cf;
cf1.translation += 0.5f * Attachment::toolAdornMajorAxisSize * cf.rotation.column(0);
DrawAdorn::cylinder(adorn, cf1, 0, Attachment::toolAdornMajorAxisSize, Attachment::toolAdornMajorAxisRadius, Color3::yellow());
}
{
CoordinateFrame cf1 = cf;
cf1.translation += 0.5f * Attachment::toolAdornMinorAxisSize * cf.rotation.column(1);
DrawAdorn::cylinder(adorn, cf1, 1, Attachment::toolAdornMinorAxisSize, Attachment::toolAdornMinorAxisRadius, Color3::red());
}
}
void Attachment::render3dToolAdorn(Adorn* adorn, Attachment::SelectState selectState)
{
float alpha = 1.0f;
static float dimming = 0.7f;
float intensity = dimming;
Color3 color = (selectState & SelectState_Paired) ? Color3::green() : Color3::orange();
adorn->setMaterial(Adorn::Material_SelfLit);
if (selectState & SelectState_Normal)
{
intensity = 1.0f;
}
if (selectState & SelectState_Hovered)
{
intensity = 1.0f;
adorn->setMaterial(Adorn::Material_SelfLitHighlight);
}
if (selectState & SelectState_Hidden)
{
alpha = 0.1f;
}
if ((selectState & SelectState_Normal) || (selectState & SelectState_Hovered))
{
renderToolAdornAxes(adorn, getFrameInWorld());
}
adorn->setObjectToWorldMatrix(getFrameInWorld());
Sphere sphere;
sphere.radius = toolAdornHandleRadius;
adorn->sphere(sphere, Color4(intensity * color, alpha));
}
float Attachment::intersectAdornWithRay(const RbxRay& r)
{
Sphere s;
s.center = getPivotInWorld();
s.radius = toolAdornHandleRadius;
return r.intersectionTime(s);
}
void Attachment::verifySetParent(const Instance* instance) const
{
Super::verifySetParent(instance);
if (instance != NULL && instance->fastDynamicCast<PartInstance>() == NULL && instance->fastDynamicCast<Workspace>() == NULL)
{
throw Aya::runtime_error("Attachments can only be parented to parts or the workspace");
}
}
void Attachment::verifyAddChild(const Instance* newChild) const
{
Super::verifyAddChild(newChild);
/*&& newChild->fastDynamicCast< Constraints::Constraint >() == NULL*/
/*if (newChild != NULL )
{
throw Aya::runtime_error("Attachments can't have children.");
}*/
}
} // namespace Aya
#endif

View File

@@ -0,0 +1,133 @@
#if 1 // disable until we are ready for new joint schema
#pragma once
#include "Tree/Instance.hpp"
#include "Base/IAdornable.hpp"
#include "Utility/NormalId.hpp"
namespace Aya
{
// This is not a streaming/replication safe option, do not enable!
// #define AYA_ATTACHMENT_LOCKING
extern const char* const sAttachment;
class Attachment
: public DescribedCreatable<Attachment, Instance, sAttachment>
, public IAdornable
{
public:
// The adorn looks of the attachment
static float adornRadius;
// The attachment adorn looks under the AttachmentTool
static float toolAdornHandleRadius;
static float toolAdornMajorAxisSize;
static float toolAdornMajorAxisRadius;
static float toolAdornMinorAxisSize;
static float toolAdornMinorAxisRadius;
private:
typedef DescribedCreatable<Attachment, Instance, sAttachment> Super;
// Should we render something in game
bool visible;
bool locked;
Vector3 pivotPositionInPart;
Vector3 axisDirectionInPart;
Vector3 secondaryAxisDirectionInPart;
bool shouldRender3dAdorn() const override
{
return true;
}
void verifySetParent(const Instance* instance) const override;
void verifyAddChild(const Instance* newChild) const override;
public:
static Reflection::PropDescriptor<Attachment, CoordinateFrame> prop_Frame;
Attachment();
~Attachment() {}
// For UI, scripting and streaming
bool getVisible(void) const
{
return visible;
}
void setVisible(bool value);
bool getLocked(void) const
{
return locked;
}
void setLocked(bool value);
// UI and replication
CoordinateFrame getFrameInPart() const;
void setFrameInPart(const CoordinateFrame& frame);
CoordinateFrame getFrameInWorld() const;
// UI and scripting
Vector3 getPivotInPart() const;
void setPivotInPart(const Vector3& pivot);
Vector3 getPivotInWorld(void) const;
Vector3 getEulerAnglesInPart() const;
void setEulerAnglesInPart(const Vector3& v);
Vector3 getEulerAnglesInWorld() const;
Vector3 getAxisInPart(void) const;
Vector3 getAxisInWorld(void) const;
Vector3 getSecondaryAxisInPart(void) const;
Vector3 getSecondaryAxisInWorld(void) const;
CoordinateFrame getParentFrame() const;
// Only for scripting
void setAxes(Vector3 axis, Vector3 secondaryAxis);
// Hidden from API as it has side effects
// Used only by AttachmentTool
void setAxisInPart(Vector3 axis);
virtual float intersectAdornWithRay(const RbxRay& r);
#ifdef AYA_ATTACHMENT_LOCKING
// disabled because not streaming/replication safe
static const Reflection::PropDescriptor<Attachment, bool> prop_Locked;
#endif
// Rendering
enum SelectState
{
SelectState_None = 0,
SelectState_Normal = 1,
SelectState_Hovered = 2,
SelectState_Paired = 4,
SelectState_Hidden = 8
};
void render3dToolAdorn(Adorn* adorn, Attachment::SelectState selectState);
void render3dAdorn(Adorn* adorn) override;
private:
// Hidden from API for having order dependency issues
// This might change the secondary axis in order to keep it orthogonal to the axis
void setAxisInPartInternal(const Vector3& axis);
// This will not change the axis, but will project the secondary axis onto the orthogonormal circle to the axis
void setSecondaryAxisInPartInternal(const Vector3& axis);
void setOrientationInPartInternal(const Matrix3& o);
Matrix3 getOrientationInPart() const;
Matrix3 getOrientationInWorld() const;
};
} // namespace Aya
#endif

View File

@@ -0,0 +1,59 @@
#include "DataModel/Backpack.hpp"
#include "DataModel/Hopper.hpp"
#include "DataModel/Workspace.hpp"
#include "Player.hpp"
#include "Players.hpp"
#include "Script/script.hpp"
namespace Aya
{
const char* const sBackpack = "Backpack"; // rename class ultimately
Backpack::Backpack()
{
setName("Backpack");
}
/* When scripts run in the backpack
BaseScript: 1. In HopperBin if local backpack (runs local)
2. If backend Processing (runs backend)
LocalScript:3. If local backpack (runs local)
*/
bool Backpack::scriptShouldRun(BaseScript* script)
{
AYAASSERT(isAncestorOf(script));
Workspace* workspace = ServiceProvider::find<Workspace>(this);
if (workspace)
{
Aya::Network::Player* localPlayer = Network::Players::findLocalPlayer(this);
bool isLocalBackpack = (this->getParent() == localPlayer);
bool isLocalScript = (script->fastDynamicCast<LocalScript>() != NULL);
if (isLocalBackpack && isLocalScript)
{
script->setLocalPlayer(shared_from(localPlayer));
return true;
}
bool isBackendProcessing = Network::Players::backendProcessing(this);
if (!isLocalScript && isBackendProcessing)
{
return true;
}
}
return false;
}
} // namespace Aya

View File

@@ -0,0 +1,25 @@
#pragma once
#include "DataModel/Hopper.hpp"
#include "Script/IScriptFilter.hpp"
namespace Aya
{
extern const char* const sBackpack;
class Backpack
: public DescribedCreatable<Backpack, Hopper, sBackpack>
, public IScriptFilter
{
private:
// IScriptOwner
/*override*/ bool scriptShouldRun(BaseScript* script);
public:
Backpack();
};
} // namespace Aya

View File

@@ -0,0 +1,450 @@
#include "DataModel/BadgeService.hpp"
#include "DataModel/DebrisService.hpp"
#include "DataModel/Workspace.hpp"
#include "Players.hpp"
#include "Utility/Http.hpp"
#include "Utility/StandardOut.hpp"
namespace Aya
{
const char* const sBadgeService = "BadgeService";
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int, int)> func_UserHasBadge(
&BadgeService::userHasBadge, "UserHasBadge", "userId", "badgeId", Security::None);
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int, int)> func_AwardBadge(
&BadgeService::awardBadge, "AwardBadge", "userId", "badgeId", Security::None);
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int)> func_IsDisabled(&BadgeService::isDisabled, "IsDisabled", "badgeId", Security::None);
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int)> func_IsLegal(&BadgeService::isLegal, "IsLegal", "badgeId", Security::None);
static Reflection::BoundFuncDesc<BadgeService, void(int)> func_SetHasBadgeCooldown(
&BadgeService::setHasBadgeCooldown, "SetHasBadgeCooldown", "seconds", Security::LocalUser);
static Reflection::BoundFuncDesc<BadgeService, void(int)> func_SetPlaceId(&BadgeService::setPlaceId, "SetPlaceId", "placeId", Security::LocalUser);
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetAwardBadgeUrl(
&BadgeService::setAwardBadgeUrl, "SetAwardBadgeUrl", "url", Security::LocalUser);
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetHasBadgeUrl(
&BadgeService::setHasBadgeUrl, "SetHasBadgeUrl", "url", Security::LocalUser);
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetDisabledUrl(
&BadgeService::setIsBadgeDisabledUrl, "SetIsBadgeDisabledUrl", "url", Security::LocalUser);
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetLegalUrl(
&BadgeService::setIsBadgeLegalUrl, "SetIsBadgeLegalUrl", "url", Security::LocalUser);
static Reflection::RemoteEventDesc<BadgeService, void(std::string, int, int)> event_BadgeAwarded(&BadgeService::badgeAwardedSignal, "BadgeAwarded",
"message", "userId", "badgeId", Security::RobloxScript, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::BROADCAST);
REFLECTION_END();
BadgeService::BadgeService()
: placeId(-1)
, cooldownTime(60)
{
setName("BadgeService");
}
void BadgeService::setPlaceId(int placeId)
{
this->placeId = placeId;
}
void BadgeService::setHasBadgeCooldown(int cooldownTime)
{
this->cooldownTime = cooldownTime;
}
void BadgeService::setAwardBadgeUrl(std::string value)
{
awardBadgeUrl = value;
}
void BadgeService::setHasBadgeUrl(std::string value)
{
hasBadgeUrl = value;
}
void BadgeService::setIsBadgeDisabledUrl(std::string value)
{
isBadgeDisabledUrl = value;
}
void BadgeService::setIsBadgeLegalUrl(std::string value)
{
isBadgeLegalUrl = value;
}
void BadgeService::hasBadgeResultHelper(weak_ptr<BadgeService> badgeService, int userId, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
{
safeBadgeService->hasBadgeResult(userId, badgeId, response, err, resumeFunction, errorFunction);
}
}
void BadgeService::hasBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
if (!response)
{
resumeFunction(false);
return;
}
if (*response == "Success")
{
{
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
badgeQueryCache[userId].insert(badgeId);
}
resumeFunction(true);
return;
}
{
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
hotBadges.push_back(HotUserHasBadge(userId, badgeId, cooldownTime));
}
if (*response == "Failure")
{
resumeFunction(false);
}
else
{
resumeFunction(false);
}
}
BadgeService::HotUserHasBadge::HotUserHasBadge(int userId, int badgeId, int cooldownTime)
: userId(userId)
, badgeId(badgeId)
, expiration(Aya::Time::now<Time::Fast>() + Time::Interval(cooldownTime))
{
}
bool BadgeService::HotUserHasBadge::expired() const
{
return Aya::Time::now<Time::Fast>() >= expiration;
}
bool BadgeService::isHasBadgeHot(int userId, int badgeId)
{
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
std::list<HotUserHasBadge>::iterator end = hotBadges.end();
for (std::list<HotUserHasBadge>::iterator iter = hotBadges.begin(); iter != end; ++iter)
{
if (iter->expired())
{
hotBadges.erase(iter, end);
return false;
}
if (iter->userId == userId && iter->badgeId == badgeId)
{
return true;
}
}
return false;
}
void BadgeService::userHasBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (!Workspace::serverIsPresent(this))
{
// Throw a friendly exception to the users that they can't get badges during user games
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, badges can only be queried by the Kiseki game servers");
resumeFunction(false);
return;
}
if (ServiceProvider::find<Network::Players>(this)->getPlayerByID(userId).get() == NULL)
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, player with userId=%d is not in this level at the moment", userId);
resumeFunction(false);
return;
}
{
bool returnResult;
{
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
returnResult = badgeQueryCache[userId].find(badgeId) != badgeQueryCache[userId].end();
}
if (returnResult)
{
// They already have the badge, so don't bother asking the webserver
resumeFunction(true);
return;
}
if (isHasBadgeHot(userId, badgeId))
{
resumeFunction(false);
return;
}
}
// Make the request and send it out
Http http(Aya::format(hasBadgeUrl.c_str(), userId, badgeId));
http.get(boost::bind(&BadgeService::hasBadgeResultHelper, weak_from(this), userId, badgeId, _1, _2, resumeFunction, errorFunction));
}
void BadgeService::isDiabledResultHelper(weak_ptr<BadgeService> badgeService, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
{
safeBadgeService->isDisabledResult(badgeId, response, err, resumeFunction, errorFunction);
}
}
void BadgeService::isDisabledResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
if (!response)
{
{
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
badgeIsDisabledCache[badgeId] = true;
}
resumeFunction(true);
return;
}
if (*response == "1")
{
{
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
badgeIsDisabledCache[badgeId] = true;
}
resumeFunction(true);
}
else if (*response == "0")
{
{
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
badgeIsDisabledCache[badgeId] = false;
}
resumeFunction(false);
}
else
{
// Really bad failure?
resumeFunction(true);
}
}
void BadgeService::isDisabled(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (!Workspace::serverIsPresent(this))
{
// Throw a friendly exception to the users that they can't get badges during user games
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, badges can only be tested if they are disabled on Kiseki game servers");
resumeFunction(true);
return;
}
if (placeId == -1)
{
resumeFunction(true);
return;
}
{
bool hasResult;
bool result;
{
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
// Check if we have already awarded the badge to this user in this game session
hasResult = badgeIsDisabledCache.find(badgeId) != badgeIsDisabledCache.end();
result = hasResult ? badgeIsDisabledCache[badgeId] : false;
}
if (hasResult)
{
// We've already awarded this badge during this game session (or the badge is no good), so you can't get it again.
resumeFunction(result);
return;
}
}
// Make the request and send it out
Http http(Aya::format(isBadgeDisabledUrl.c_str(), badgeId, placeId));
http.get(boost::bind(&BadgeService::isDiabledResultHelper, weak_from(this), badgeId, _1, _2, resumeFunction, errorFunction));
}
void BadgeService::isLegalResultHelper(weak_ptr<BadgeService> badgeService, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
{
safeBadgeService->isLegalResult(badgeId, response, err, resumeFunction, errorFunction);
}
}
void BadgeService::isLegalResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
if (!response)
{
{
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
badgeIsLegalCache[badgeId] = true;
}
resumeFunction(true);
return;
}
if (*response == "1")
{
{
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
badgeIsLegalCache[badgeId] = true;
}
resumeFunction(true);
}
else if (*response == "0")
{
{
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
badgeIsLegalCache[badgeId] = false;
}
resumeFunction(false);
}
else
{
// Really bad failure?
resumeFunction(true);
}
}
void BadgeService::isLegal(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (placeId == -1)
{
// We don't know the placeId, so give up and assume its okay
resumeFunction(true);
return;
}
{
bool hasResult;
bool result;
{
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
// Check if we have already awarded the badge to this user in this game session
hasResult = badgeIsLegalCache.find(badgeId) != badgeIsLegalCache.end();
result = hasResult ? badgeIsLegalCache[badgeId] : false;
}
if (hasResult)
{
// We've already awarded this badge during this game session (or the badge is no good), so you can't get it again.
resumeFunction(result);
return;
}
}
// Make the request and send it out
Http http(Aya::format(isBadgeLegalUrl.c_str(), badgeId, placeId));
http.get(boost::bind(&BadgeService::isLegalResultHelper, weak_from(this), badgeId, _1, _2, resumeFunction, errorFunction));
}
void BadgeService::awardBadgeResultHelper(weak_ptr<BadgeService> badgeService, int userId, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
{
safeBadgeService->awardBadgeResult(userId, badgeId, response, err, resumeFunction, errorFunction);
}
}
static void fireBadgeAwarded(shared_ptr<BadgeService> self, const std::string& response, int userId, int badgeId)
{
event_BadgeAwarded.fireAndReplicateEvent(self.get(), response, userId, badgeId);
}
void BadgeService::awardBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
if (!response)
{
resumeFunction(false);
return;
}
if (*response == "")
{
// We failed in some way to get data back, maybe we should try again? Or just give up?
resumeFunction(false);
}
else if (*response == "0")
{
{
boost::recursive_mutex::scoped_lock lock(badgeAwardSync);
// It returned failure, they must already have the badge. Add them to the cache
badgeAwardCache[userId].insert(badgeId);
}
resumeFunction(false);
}
else
{
{
boost::recursive_mutex::scoped_lock lock(badgeAwardSync);
// Cache that we gave them out this badge, so we won't try again
badgeAwardCache[userId].insert(badgeId);
}
Aya::DataModel* dataModel = (Aya::DataModel*)this->getParent();
if (dataModel)
{
dataModel->submitTask(boost::bind(fireBadgeAwarded, shared_from(this), *response, userId, badgeId), DataModelJob::Write);
}
resumeFunction(true);
}
}
void BadgeService::awardBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (!Workspace::serverIsPresent(this))
{
// Throw a friendly exception to the users that they can't get badges during user games
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, badges can only be awarded by Kiseki game servers");
resumeFunction(false);
return;
}
Network::Players* players = ServiceProvider::find<Network::Players>(this);
AYAASSERT(players);
if (!players->getPlayerByID(userId))
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "The player with userId=%d is not present at the moment", userId);
resumeFunction(false);
return;
}
{
bool alreadyAwarded;
{
boost::recursive_mutex::scoped_lock lock(badgeAwardSync);
// Check if we have already awarded the badge to this user in this game session
alreadyAwarded = (badgeAwardCache[userId].find(badgeId) != badgeAwardCache[userId].end());
}
if (alreadyAwarded)
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "We already gave out badgeId=%d to userId=%d", badgeId, userId);
// We've already awarded this badge during this game session (or the badge is no good), so you can't get it again.
resumeFunction(false);
return;
}
}
// Make the request and send it out
Http http(Aya::format(awardBadgeUrl.c_str(), userId, badgeId, placeId));
// switch get to post to fix exploit in badge service
// http.get(boost::bind(&BadgeService::awardBadgeResultHelper, weak_from(this), userId, badgeId, _1, _2, resumeFunction, errorFunction));
std::string in;
http.post(in, Http::kContentTypeDefaultUnspecified, false,
boost::bind(&BadgeService::awardBadgeResultHelper, weak_from(this), userId, badgeId, _1, _2, resumeFunction, errorFunction));
}
} // namespace Aya

View File

@@ -0,0 +1,87 @@
#pragma once
#include "Tree/Service.hpp"
#include "DataModel/PartInstance.hpp"
namespace Aya
{
extern const char* const sBadgeService;
class BadgeService
: public DescribedCreatable<BadgeService, Instance, sBadgeService, Reflection::ClassDescriptor::INTERNAL>
, public Service
{
private:
std::string awardBadgeUrl;
std::string hasBadgeUrl;
std::string isBadgeDisabledUrl;
std::string isBadgeLegalUrl;
int placeId;
int cooldownTime;
boost::recursive_mutex badgeAwardSync;
std::map<int, std::set<int>> badgeAwardCache;
boost::recursive_mutex badgeQuerySync;
std::map<int, std::set<int>> badgeQueryCache;
boost::recursive_mutex badgeIsDisabledSync;
std::map<int, bool> badgeIsDisabledCache;
boost::recursive_mutex badgeIsLegalSync;
std::map<int, bool> badgeIsLegalCache;
struct HotUserHasBadge
{
int userId;
int badgeId;
Aya::Time expiration;
HotUserHasBadge(int userId, int badgeId, int cooldownTime);
bool expired() const;
};
std::list<HotUserHasBadge> hotBadges;
bool isHasBadgeHot(int userId, int badgeId);
public:
BadgeService();
~BadgeService() {}
Aya::remote_signal<void(std::string, int, int)> badgeAwardedSignal;
void userHasBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void awardBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void isDisabled(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void isLegal(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void setPlaceId(int placeId);
void setHasBadgeCooldown(int seconds);
void setAwardBadgeUrl(std::string);
void setHasBadgeUrl(std::string);
void setIsBadgeDisabledUrl(std::string);
void setIsBadgeLegalUrl(std::string);
private:
static void hasBadgeResultHelper(weak_ptr<BadgeService>, int userId, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void hasBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction);
static void awardBadgeResultHelper(weak_ptr<BadgeService>, int userId, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void awardBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction);
static void isDiabledResultHelper(weak_ptr<BadgeService>, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void isDisabledResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction);
static void isLegalResultHelper(weak_ptr<BadgeService>, int badgeId, std::string* response, std::exception* err,
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void isLegalResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
boost::function<void(std::string)> errorFunction);
};
} // namespace Aya

View File

@@ -0,0 +1,86 @@
#include "DataModel/DataModel.hpp"
#include "DataModel/BaseRenderJob.hpp"
#include "DataModel/HackDefines.hpp"
#include "DataModel/ModelInstance.hpp"
#include "DataModel/Workspace.hpp"
#include "Profiler.hpp"
#include "ImGui.hpp"
LOGGROUP(TaskSchedulerTiming)
namespace Aya
{
BaseRenderJob::BaseRenderJob(double minFps, double maxFps, boost::shared_ptr<DataModel> dataModel)
: Aya::DataModelJob("Render", Aya::DataModelJob::Render, false, dataModel, Aya::Time::Interval(.02))
, isAwake(true)
, minFrameRate(minFps)
, maxFrameRate(maxFps)
{
cyclicExecutive = true;
// We originally intended RenderJob to run after Network, Physics, and so on
// to reduce latency, however this introduced a weird variability into the dt between
// each RenderJob::step. Since makes sure that the first job is always 16 - 17ms apart from
// it's previous run, making RenderJob run first will grant more visual stability at the cost
// of potentially some latency in input.
cyclicPriority = CyclicExecutiveJobPriority_EarlyRendering;
}
void BaseRenderJob::wake()
{
isAwake = true;
if (!isCyclicExecutiveJob())
TaskScheduler::singleton().reschedule(shared_from_this());
}
bool BaseRenderJob::tryJobAgain()
{
if (isCyclicExecutiveJob() && !isAwake)
{
return true;
}
return false;
}
bool BaseRenderJob::isCyclicExecutiveJob()
{
return Aya::TaskScheduler::singleton().isCyclicExecutive() && cyclicExecutive;
}
TaskScheduler::Job::Error BaseRenderJob::error(const Stats& stats)
{
if (!isAwake && (!isCyclicExecutiveJob()))
{
return Job::Error();
}
Job::Error result;
if (isCyclicExecutiveJob())
{
result = computeStandardError(stats, maxFrameRate);
FASTLOG1F(FLog::TaskSchedulerTiming, "Error recalculated, time since last call: %f", (float)stats.timespanSinceLastStep.seconds());
}
else
{
result = computeStandardError(stats, minFrameRate);
}
return result;
}
Time::Interval BaseRenderJob::timeSinceLastRender() const
{
return Time::now<Aya::Time::Fast>() - lastRenderTime;
}
TaskScheduler::StepResult BaseRenderJob::step(const Stats& stats)
{
Profiler::onFrame();
ImGui::onFrame();
return DataModelJob::step(stats);
}
} // namespace Aya

View File

@@ -0,0 +1,41 @@
#pragma once
#include <boost/weak_ptr.hpp>
#include "DataModel/DataModel.hpp"
#include "time.hpp"
#include "TaskScheduler.hpp"
#include "TaskScheduler.Job.hpp"
#include "Utility/IMetric.hpp"
namespace Aya
{
class View;
// This is the base class for all Rendering Jobs. All rendering jobs will inherit from this.
class BaseRenderJob : public DataModelJob
{
protected:
Time lastRenderTime;
volatile bool isAwake;
double minFrameRate;
double maxFrameRate;
public:
BaseRenderJob(double minFrameRate, double maxFrameRate, boost::shared_ptr<DataModel> dataModel);
virtual void wake();
virtual bool tryJobAgain();
virtual bool isCyclicExecutiveJob();
virtual Time::Interval timeSinceLastRender() const;
virtual Job::Error error(const Stats& stats);
// virtual Aya::TaskScheduler::StepResult stepDataModelJob(const Stats&);
virtual TaskScheduler::StepResult step(const Stats& stats);
};
} // namespace Aya

View File

@@ -0,0 +1,200 @@
#include "DataModel/BasicPartInstance.hpp"
#include "World/Primitive.hpp"
DYNAMIC_FASTFLAGVARIABLE(SpheresAllowedCustom, false)
DYNAMIC_FASTFLAG(FormFactorDeprecated)
namespace Aya
{
const char* const sFormFactorPart = "FormFactorPart";
const char* const sBasicPart = "Part";
using namespace Reflection;
const char* category_BasicPart = "Part ";
/*
Unfortunately, we have 3 FormFactor properties. The reason for this
is legacy content. For a while "FormFactor" was the STREAMING version
and "formFactor" was the scriptable version. To support legacy files
we override readProperty so that old files with FormFactor use the raw
XML version of set.
*/
static const EnumPropDescriptor<FormFactorPart, FormFactorPart::FormFactor> prop_formFactorUi(
"FormFactor", category_BasicPart, &FormFactorPart::getFormFactor, &FormFactorPart::setFormFactorUi, PropertyDescriptor::LEGACY_SCRIPTING);
static const EnumPropDescriptor<FormFactorPart, FormFactorPart::FormFactor> prop_FormFactorLegacy(
"formFactor", category_BasicPart, &FormFactorPart::getFormFactor, &FormFactorPart::setFormFactorUi, PropertyDescriptor::LEGACY_SCRIPTING);
static const EnumPropDescriptor<FormFactorPart, FormFactorPart::FormFactor> prop_FormFactorData(
"formFactorRaw", category_BasicPart, &FormFactorPart::getFormFactor, &FormFactorPart::setFormFactorXml, PropertyDescriptor::STREAMING);
FormFactorPart::FormFactorPart() {}
FormFactorPart::~FormFactorPart() {}
void FormFactorPart::readProperty(const XmlElement* propertyElement, IReferenceBinder& binder)
{
std::string name;
if (propertyElement->findAttributeValue(name_name, name))
{
// Redirect legacy FormFactor to formFactorRaw
if (name == "FormFactor")
{
prop_FormFactorData.readValue(this, propertyElement, binder);
return;
}
}
Super::readProperty(propertyElement, binder);
}
void FormFactorPart::setFormFactorXml(FormFactor value)
{
if (value != formFactor)
{
formFactor = value;
raisePropertyChanged(prop_FormFactorData);
raisePropertyChanged(prop_formFactorUi);
}
}
void FormFactorPart::setFormFactorUi(FormFactor value)
{
validateFormFactor(value);
if (value != formFactor)
{
destroyJoints();
setFormFactorXml(value);
if (value != CUSTOM)
{
// Snap to a valid size
Vector3 uiSize = getPartSizeUi();
uiSize.y = std::max(1.0f, (float)Math::iRound(uiSize.y)); // trunc
setPartSizeUnjoined(uiSize);
}
join();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const EnumPropDescriptor<BasicPartInstance, BasicPartInstance::LegacyPartType> prop_shapXml(
"shap", category_BasicPart, NULL, &BasicPartInstance::setLegacyPartTypeXml, PropertyDescriptor::LEGACY); // Used to prepare for TA14264
const EnumPropDescriptor<BasicPartInstance, BasicPartInstance::LegacyPartType> BasicPartInstance::prop_shapeXml(
"shape", category_BasicPart, &BasicPartInstance::getLegacyPartType, &BasicPartInstance::setLegacyPartTypeXml, PropertyDescriptor::STREAMING);
static const EnumPropDescriptor<BasicPartInstance, BasicPartInstance::LegacyPartType> prop_shapeUi(
"Shape", category_BasicPart, &BasicPartInstance::getLegacyPartType, &BasicPartInstance::setLegacyPartTypeUi, PropertyDescriptor::UI);
BasicPartInstance::BasicPartInstance() {}
BasicPartInstance::~BasicPartInstance() {}
void BasicPartInstance::validateFormFactor(FormFactor& formFactor)
{
if (DFFlag::SpheresAllowedCustom)
{
if (!hasThreeDimensionalSize() && formFactor != PartInstance::CUSTOM)
{
formFactor = PartInstance::SYMETRIC;
}
}
else if (!hasThreeDimensionalSize())
{
formFactor = PartInstance::SYMETRIC;
}
}
PartType BasicPartInstance::getPartType() const
{
switch (legacyPartType)
{
case BLOCK_LEGACY_PART:
return BLOCK_PART;
case CYLINDER_LEGACY_PART:
return CYLINDER_PART;
case BALL_LEGACY_PART:
return BALL_PART;
default:
AYAASSERT(0);
return BLOCK_PART;
}
}
void BasicPartInstance::setLegacyPartTypeXml(LegacyPartType _type)
{
if (legacyPartType != _type)
{
legacyPartType = _type;
getPartPrimitive()->setGeometryType(legacyPartType == BLOCK_LEGACY_PART ? Geometry::GEOMETRY_BLOCK
: legacyPartType == CYLINDER_LEGACY_PART ? Geometry::GEOMETRY_CYLINDER
: Geometry::GEOMETRY_BALL);
if (!DFFlag::FormFactorDeprecated)
{
if (DFFlag::SpheresAllowedCustom)
{
if (!hasThreeDimensionalSize() && getFormFactor() != PartInstance::CUSTOM)
{
setFormFactorXml(PartInstance::SYMETRIC);
}
}
else if (!hasThreeDimensionalSize())
{
setFormFactorXml(PartInstance::SYMETRIC);
}
}
raisePropertyChanged(prop_shapeXml);
raisePropertyChanged(prop_shapeUi);
shouldRenderSetDirty();
}
}
void BasicPartInstance::setLegacyPartTypeUi(LegacyPartType _type)
{
if (!hasThreeDimensionalSize())
{
AYAASSERT(formFactor == PartInstance::SYMETRIC);
}
if (legacyPartType != _type)
{
destroyJoints();
// 1. Set the new type / Make form factor symetric for balls, cylinders (form factor may change)
setLegacyPartTypeXml(_type);
// 2. Make the dimensions uniform for balls, cyclinders (size may change)
if (!hasThreeDimensionalSize())
{
setPartSizeUnjoined(getPartSizeUi());
}
safeMove();
join();
}
}
bool BasicPartInstance::hasThreeDimensionalSize()
{
return legacyPartType != BALL_LEGACY_PART;
}
} // namespace Aya

View File

@@ -0,0 +1,66 @@
#pragma once
#include "DataModel/PartInstance.hpp"
namespace Aya
{
extern const char* const sFormFactorPart;
class FormFactorPart : public DescribedNonCreatable<FormFactorPart, PartInstance, sFormFactorPart>
{
typedef DescribedNonCreatable<FormFactorPart, PartInstance, sFormFactorPart> Super;
public:
/*override*/ virtual FormFactor getFormFactor() const
{
return formFactor;
}
void setFormFactorUi(FormFactor value);
void setFormFactorXml(FormFactor value);
FormFactorPart();
virtual ~FormFactorPart();
/* override */ void readProperty(const XmlElement* propertyElement, IReferenceBinder& binder);
protected:
virtual void validateFormFactor(FormFactor& value) {}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern const char* const sBasicPart;
class BasicPartInstance : public DescribedCreatable<BasicPartInstance, FormFactorPart, sBasicPart>
{
private:
/*override*/ void validateFormFactor(FormFactor& value);
public:
static const Reflection::EnumPropDescriptor<BasicPartInstance, LegacyPartType> prop_shapeXml;
BasicPartInstance();
virtual ~BasicPartInstance();
/*override*/ virtual bool hasThreeDimensionalSize();
void setLegacyPartTypeUi(LegacyPartType _type);
void setLegacyPartTypeXml(LegacyPartType _type);
/*override*/ virtual PartType getPartType() const;
LegacyPartType getLegacyPartType() const
{
return legacyPartType;
}
};
} // namespace Aya

View File

@@ -0,0 +1,56 @@
#include "DataModel/BevelMesh.hpp"
using namespace Aya;
const char* const Aya::sBevelMesh = "BevelMesh";
static Reflection::PropDescriptor<BevelMesh, float> desc_bevel(
"Bevel", category_Data, &BevelMesh::getBevel, &BevelMesh::setBevel, Aya::Reflection::PropertyDescriptor::STREAMING);
static Reflection::PropDescriptor<BevelMesh, float> desc_roundness(
"Bevel Roundness", category_Data, &BevelMesh::getRoundness, &BevelMesh::setRoundness, Aya::Reflection::PropertyDescriptor::STREAMING);
static Reflection::PropDescriptor<BevelMesh, float> desc_bulge(
"Bulge", category_Data, &BevelMesh::getBulge, &BevelMesh::setBulge, Aya::Reflection::PropertyDescriptor::STREAMING);
REFLECTION_END();
BevelMesh::BevelMesh()
: bevel(0.0)
, roundness(0.0)
, bulge(0.0)
{
}
const float BevelMesh::getBevel() const
{
return bevel;
}
void BevelMesh::setBevel(const float bev)
{
bevel = bev;
raisePropertyChanged(desc_bevel);
}
const float BevelMesh::getRoundness() const
{
return roundness;
}
void BevelMesh::setRoundness(const float rnd)
{
roundness = rnd;
raisePropertyChanged(desc_roundness);
}
const float BevelMesh::getBulge() const
{
return bulge;
}
void BevelMesh::setBulge(const float blg)
{
bulge = blg;
raisePropertyChanged(desc_bulge);
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "DataModelMesh.hpp"
namespace Aya
{
extern const char* const sBevelMesh;
class BevelMesh : public DescribedNonCreatable<BevelMesh, DataModelMesh, sBevelMesh>
{
protected:
float bevel;
float roundness;
float bulge; // TODO : note, quick hack putting it in here.
public:
BevelMesh();
const float getRoundness() const;
void setRoundness(const float roundness);
const float getBevel() const;
void setBevel(const float bevel);
const float getBulge() const;
void setBulge(const float bulge);
};
} // namespace Aya

View File

@@ -0,0 +1,414 @@
#include "DataModel/BillboardGui.hpp"
#include "DataModel/DataModel.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/Filters.hpp"
#include "World/ContactManager.hpp"
#include "World/World.hpp"
#include "Base/ViewportBillboarder.hpp"
#include "Base/AdornBillboarder.hpp"
#include "Base/AdornBillboarder2D.hpp"
#include "Base/AdornSurface.hpp"
#include "Player.hpp"
#include "Players.hpp"
FASTFLAGVARIABLE(BillboardGuiVR, true)
namespace Aya
{
const char* const sAdornmentGui = "BillboardGui";
const Reflection::RefPropDescriptor<BillboardGui, Instance> prop_adornee(
"Adornee", category_Data, &BillboardGui::getAdorneeDangerous, &BillboardGui::setAdornee);
Reflection::PropDescriptor<BillboardGui, Vector3> prop_studsOffset(
"StudsOffset", category_Data, &BillboardGui::getStudsOffset, &BillboardGui::setStudsOffset);
Reflection::PropDescriptor<BillboardGui, Vector3> prop_extentsOffset(
"ExtentsOffset", category_Data, &BillboardGui::getExtentsOffset, &BillboardGui::setExtentsOffset);
Reflection::PropDescriptor<BillboardGui, Vector2> prop_sizeOffset(
"SizeOffset", category_Data, &BillboardGui::getSizeOffset, &BillboardGui::setSizeOffset);
Reflection::PropDescriptor<BillboardGui, UDim2> prop_Size("Size", category_Data, &BillboardGui::getSize, &BillboardGui::setSize);
Reflection::PropDescriptor<BillboardGui, bool> prop_Enabled("Enabled", category_Data, &BillboardGui::getEnabled, &BillboardGui::setEnabled);
Reflection::PropDescriptor<BillboardGui, bool> prop_Active("Active", category_Data, &BillboardGui::getActive, &BillboardGui::setActive);
Reflection::PropDescriptor<BillboardGui, bool> prop_AlwaysOnTop(
"AlwaysOnTop", category_Data, &BillboardGui::getAlwaysOnTop, &BillboardGui::setAlwaysOnTop);
const Reflection::RefPropDescriptor<BillboardGui, Instance> prop_LocalPlayerVisible(
"PlayerToHideFrom", category_Data, &BillboardGui::getPlayerToHideFrom, &BillboardGui::setPlayerToHideFrom);
static bool getBillboardFrame(const Camera* camera, const CoordinateFrame& cameraCFrame, const Vector3& partSize, const CoordinateFrame& partCFrame,
const Vector3& partExtentRelativeOffset, const Vector3& partStudsOffset, const Vector2& billboardSizeRelativeOffset, const UDim2& billboardSize,
CoordinateFrame& result, Rect2D& viewport)
{
Extents localExtents = Extents::fromCenterCorner(Vector3(), partSize / 2);
Extents cameraSpaceExtents = localExtents.toWorldSpace(cameraCFrame.inverse() * partCFrame);
Vector3 relativeOffsetInCameraSpace = (partExtentRelativeOffset + Vector3::one()) * 0.5f * cameraSpaceExtents.size() + cameraSpaceExtents.min();
Vector3 billboardCenterInCameraSpace = relativeOffsetInCameraSpace + partStudsOffset;
Vector3 billboardCenterInWorldSpace = cameraCFrame.pointToWorldSpace(billboardCenterInCameraSpace);
float pixelsPerStud = 1;
{
Vector3 billboardCenterInCameraSpace = camera->project(billboardCenterInWorldSpace);
if (billboardCenterInCameraSpace.z <= 0 || billboardCenterInCameraSpace.z > 1000)
return false;
pixelsPerStud = billboardCenterInCameraSpace.z;
}
float studsPerPixel = 1.f / pixelsPerStud; // undo the perspective effect of finding extents in screenspace.
// -1 : to UI coordinates (0,0 upper left, )
Vector2 billboardSizeInStuds = (billboardSize * Vector2(pixelsPerStud, pixelsPerStud)) * studsPerPixel;
viewport = Rect2D(billboardSizeInStuds * pixelsPerStud);
Vector2 UIToCameraSpaceScaler(billboardSizeInStuds / viewport.wh());
billboardCenterInCameraSpace += Vector3(billboardSizeRelativeOffset * billboardSizeInStuds, 0);
CoordinateFrame desiredModelView;
desiredModelView.translation = billboardCenterInCameraSpace - Vector3(billboardSizeInStuds.x * 0.5f, billboardSizeInStuds.y * -0.5f, 0);
desiredModelView.rotation.set(UIToCameraSpaceScaler.x, 0, 0, 0, UIToCameraSpaceScaler.y, 0, 0, 0, 1);
result = cameraCFrame * desiredModelView;
return true;
}
// This is Moeller<65>Trumbore ray-triangle intersection algorithm, adapted for quads
// The only difference with quads is the barycentric coordinate boundary check.
static bool rayQuad(
const Vector3& raySource, const Vector3& rayDir, const Vector3& v0, const Vector3& v1, const Vector3& v2, float& t, float& u, float& v)
{
Vector3 edge1 = v1 - v0;
Vector3 edge2 = v2 - v0;
Vector3 P = rayDir.cross(edge2);
float det = edge1.dot(P);
if (det <= 0)
return false;
Vector3 T = raySource - v0;
Vector3 Q = T.cross(edge1);
t = edge2.dot(Q) / det;
u = T.dot(P) / det;
v = rayDir.dot(Q) / det;
return t >= 0 && u >= 0 && u <= 1 && v >= 0 && v <= 1;
}
static bool getBillboardHit(Workspace* workspace, const Vector2& pos, const RbxRay& ray, const Vector2& viewport,
const CoordinateFrame& projectionFrame, bool alwaysOnTop, Vector2& result)
{
Vector3 v0 = projectionFrame.pointToWorldSpace(Vector3(0, 0, 0));
Vector3 v1 = projectionFrame.pointToWorldSpace(Vector3(0, -viewport.y, 0));
Vector3 v2 = projectionFrame.pointToWorldSpace(Vector3(viewport.x, 0, 0));
float t, u, v;
if (!rayQuad(ray.origin(), ray.direction(), v0, v1, v2, t, u, v))
return false;
result.x = v * viewport.x;
result.y = u * viewport.y;
if (alwaysOnTop)
return true;
RbxRay searchRay = RbxRay::fromOriginAndDirection(ray.origin(), ray.direction() * 2048);
FilterInvisibleNonColliding filter; // invisible & non-colliding parts don't block mouse-clicks
Vector3 hitPoint;
if (workspace->getWorld()->getContactManager()->getHit(searchRay, NULL, &filter, hitPoint) == NULL)
return true;
float wt = ray.direction().dot(hitPoint - ray.origin());
return (t < wt);
}
BillboardGui::BillboardGui()
: DescribedCreatable<BillboardGui, GuiLayerCollector, sAdornmentGui>("BillboardGui")
, IStepped(StepType_Render)
, alwaysOnTop(false)
, enabled(true)
, active(false)
, visibleAndValid(false)
{
if (!FFlag::BillboardGuiVR)
viewportBillboarder.reset(new ViewportBillboarder());
}
void BillboardGui::setRenderFunction(boost::function<void(BillboardGui*, Adorn*)> func)
{
adornFunc = func;
}
bool BillboardGui::askSetParent(const Instance* instance) const
{
return (Instance::fastDynamicCast<GuiBase2d>(instance) == NULL);
}
void BillboardGui::onStepped(const Stepped& event)
{
if (enabled)
{
shared_ptr<Instance> part = getPart();
Workspace* workspace = ServiceProvider::find<Workspace>(part.get());
if (part && workspace)
{
CoordinateFrame adornCFrame;
Vector3 adornSize;
calcAdornPlacement(part.get(), adornCFrame, adornSize);
{
const Camera* camera = workspace->getConstCamera();
CoordinateFrame cameraCFrame = camera->getRenderingCoordinateFrame();
UserInputService* uis = ServiceProvider::find<UserInputService>(workspace);
{
visibleAndValid = getBillboardFrame(camera, cameraCFrame, adornSize, adornCFrame, partExtentRelativeOffset, partStudsOffset,
billboardSizeRelativeOffset, billboardSize, projectionFrame, viewport);
}
if (visibleAndValid)
handleResize(viewport, false);
}
}
}
}
bool BillboardGui::shouldRender3dSortedAdorn() const
{
const shared_ptr<Instance> part(getPart());
if (enabled && part)
// only render if part is still in DataModel
return DataModel::get(part.get()) ? true : false;
else
return false;
}
boost::shared_ptr<Instance> BillboardGui::getPart() const
{
boost::shared_ptr<Instance> part = adornee.lock();
if (!part)
{
// if no adornee specified, use parent if it is a partInstance.
// allows for easy insertion of billboard guis in the world that replicate and show for everyone.
part = shared_from(const_cast<Instance*>(getParent()));
}
return part;
}
Vector3 BillboardGui::render3dSortedPosition() const
{
boost::shared_ptr<Instance> part = getPart();
CoordinateFrame cframe;
Vector3 size;
calcAdornPlacement(part.get(), cframe, size);
return cframe.translation;
}
void BillboardGui::render3dSortedAdorn(Adorn* adorn)
{
{
if (!enabled || !visibleAndValid)
return;
boost::shared_ptr<Instance> part = getPart();
Workspace* workspace = ServiceProvider::find<Workspace>(part.get());
if (!part || !workspace)
return;
if (Aya::Network::Player* player = Network::Players::findLocalPlayer(DataModel::get(part.get())))
if (shared_ptr<Instance> noPlayerRender = playerToHideFrom.lock())
if (player == fastDynamicCast<Aya::Network::Player>(noPlayerRender.get()))
return;
{
Vector3 screenOffset = workspace->getConstCamera()->project(projectionFrame.translation);
AdornBillboarder2D adornView(adorn, viewport, Math::roundVector2(screenOffset.xy()));
Super::render2d(&adornView);
}
}
}
GuiResponse BillboardGui::process(const shared_ptr<InputObject>& event)
{
{
if (!enabled || !active || !visibleAndValid)
return GuiResponse::notSunk();
if (event->isMouseEvent())
{
if (Workspace* workspace = ServiceProvider::find<Workspace>(this))
{
Vector2 pos = event->get2DPosition();
RbxRay ray = workspace->getCamera()->worldRay(pos.x, pos.y);
Vector2 hit;
if (getBillboardHit(workspace, pos, ray, viewport.wh(), projectionFrame, alwaysOnTop, hit))
{
shared_ptr<InputObject> transformedEvent = Aya::Creatable<Instance>::create<InputObject>(*event);
transformedEvent->setPosition(Vector3(Math::roundVector2(hit), 0));
return Super::process(transformedEvent);
}
}
}
return GuiResponse::notSunk();
}
}
bool BillboardGui::canProcessMeAndDescendants() const
{
return false;
}
void BillboardGui::setPlayerToHideFrom(Instance* value)
{
if (value && !fastDynamicCast<Aya::Network::Player>(value))
throw std::runtime_error("HideFromPlayer can only be of type Player");
if (playerToHideFrom.lock().get() != value)
{
playerToHideFrom = shared_from(value);
raisePropertyChanged(prop_LocalPlayerVisible);
shouldRenderSetDirty();
}
}
void BillboardGui::setEnabled(bool value)
{
if (enabled != value)
{
enabled = value;
raisePropertyChanged(prop_Enabled);
shouldRenderSetDirty();
}
}
void BillboardGui::setActive(bool value)
{
if (active != value)
{
active = value;
raisePropertyChanged(prop_Active);
}
}
void BillboardGui::setAdornee(Instance* value)
{
if (adornee.lock().get() != value)
{
adornee = shared_from(value);
raisePropertyChanged(prop_adornee);
shouldRenderSetDirty();
}
}
void BillboardGui::onAncestorChanged(const AncestorChanged& event)
{
Super::onAncestorChanged(event);
shouldRenderSetDirty();
}
const Vector3& BillboardGui::getStudsOffset() const
{
return partStudsOffset;
}
void BillboardGui::setStudsOffset(const Vector3& value)
{
if (partStudsOffset != value)
{
partStudsOffset = value;
raisePropertyChanged(prop_studsOffset);
}
}
const Vector3& BillboardGui::getExtentsOffset() const
{
return partExtentRelativeOffset;
}
void BillboardGui::setExtentsOffset(const Vector3& value)
{
if (partExtentRelativeOffset != value)
{
partExtentRelativeOffset = value;
raisePropertyChanged(prop_extentsOffset);
}
}
const Vector2& BillboardGui::getSizeOffset() const
{
return billboardSizeRelativeOffset;
}
void BillboardGui::setSizeOffset(const Vector2& value)
{
if (billboardSizeRelativeOffset != value)
{
billboardSizeRelativeOffset = value;
raisePropertyChanged(prop_sizeOffset);
}
}
UDim2 BillboardGui::getSize() const
{
return billboardSize;
}
void BillboardGui::setSize(UDim2 value)
{
if (billboardSize != value)
{
billboardSize = value;
raisePropertyChanged(prop_Size);
}
}
bool BillboardGui::getAlwaysOnTop() const
{
return alwaysOnTop;
}
void BillboardGui::setAlwaysOnTop(bool value)
{
if (alwaysOnTop != value)
{
alwaysOnTop = value;
raisePropertyChanged(prop_AlwaysOnTop);
}
}
void BillboardGui::calcAdornPlacement(Instance* part, CoordinateFrame& cframe, Vector3& size) const
{
if (PartInstance* thePart = fastDynamicCast<PartInstance>(part))
{
cframe = thePart->calcRenderingCoordinateFrame();
size = thePart->getPartSizeXml();
}
else if (ModelInstance* theModel = fastDynamicCast<ModelInstance>(part))
{
cframe = theModel->calculateModelCFrame();
size = theModel->calculateModelSize();
}
else
{
cframe = CoordinateFrame();
size = Vector3(0, 0, 0);
}
}
} // namespace Aya

View File

@@ -0,0 +1,145 @@
#pragma once
#include "DataModel/GuiBase2d.hpp"
#include "Utility/UDim.hpp"
#include "Utility/SteppedInstance.hpp"
#include "DataModel/GuiLayerCollector.hpp"
#include "DataModel/ModelInstance.hpp"
#include "DataModel/PartInstance.hpp"
namespace Aya
{
class PartInstance;
class ViewportBillboarder;
extern const char* const sAdornmentGui;
// A core window on which other windows are created
// Ugh: this component is also a "PartAdornment", but we can't easily derive from both Adornment and GuiBase2d, resolve later.
class BillboardGui
: public DescribedCreatable<BillboardGui, GuiLayerCollector, sAdornmentGui>
, public IStepped
{
private:
typedef DescribedCreatable<BillboardGui, GuiLayerCollector, sAdornmentGui> Super;
std::auto_ptr<ViewportBillboarder> viewportBillboarder;
public:
BillboardGui();
/////////////////////////////////////////////////////////////
// Instance
//
/*override*/ bool askSetParent(const Instance* instance) const;
/*override*/ bool processMeAndDescendants() const
{
return false;
}
const Instance* getAdornee() const
{
return adornee.lock().get();
}
Instance* getAdornee()
{
return adornee.lock().get();
}
void setAdornee(Instance* value);
Instance* getAdorneeDangerous() const
{
return adornee.lock().get();
}
const Vector3& getStudsOffset() const;
void setStudsOffset(const Vector3& value);
const Vector3& getExtentsOffset() const;
void setExtentsOffset(const Vector3& value);
const Vector2& getSizeOffset() const;
void setSizeOffset(const Vector2& value);
UDim2 getSize() const;
void setSize(UDim2 value);
bool getAlwaysOnTop() const;
void setAlwaysOnTop(bool value);
bool getActive() const
{
return active;
}
void setActive(bool value);
bool getEnabled() const
{
return enabled;
}
void setEnabled(bool value);
void setRenderFunction(boost::function<void(BillboardGui*, Adorn*)> func);
Instance* getPlayerToHideFrom() const
{
return playerToHideFrom.lock().get();
}
void setPlayerToHideFrom(Instance* value);
private:
boost::shared_ptr<Instance> getPart() const;
boost::function<void(BillboardGui*, Adorn*)> adornFunc;
// reflected state
Vector3 partExtentRelativeOffset;
Vector3 partStudsOffset;
Vector2 billboardSizeRelativeOffset;
UDim2 billboardSize;
bool alwaysOnTop;
bool enabled;
bool active;
// dynamic state
bool visibleAndValid;
CoordinateFrame projectionFrame;
Rect2D viewport;
// reflected state
boost::weak_ptr<Instance> playerToHideFrom;
boost::weak_ptr<Instance> adornee;
////////////////////////////////////////////////////////////////////////////////////
//
// Instance
/*override*/ void onAncestorChanged(const AncestorChanged& event);
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
Super::onServiceProvider(oldProvider, newProvider);
onServiceProviderIStepped(oldProvider, newProvider);
}
////////////////////////////////////////////////////////////////////////////////////
//
// IAdornable
/*override*/ bool shouldRender3dSortedAdorn() const;
/*override*/ void render3dSortedAdorn(Adorn* adorn);
/*override*/ Vector3 render3dSortedPosition() const;
/*override*/ bool isVisible(const Rect2D& rect) const
{
return true;
}
////////////////////////////////////////////////////////////////////////////////////
//
// GuiTarget
/*override*/ bool canProcessMeAndDescendants() const;
/*override*/ GuiResponse process(const shared_ptr<InputObject>& event);
// IStepped
virtual void onStepped(const Stepped& event);
void calcAdornPlacement(Instance* part, CoordinateFrame& cframe, Vector3& size) const;
};
} // namespace Aya

View File

@@ -0,0 +1,90 @@
#include "DataModel/Bindable.hpp"
#include "FastLog.hpp"
namespace Aya
{
const char* const sBindableFunction = "BindableFunction";
static Reflection::BoundYieldFuncDesc<BindableFunction, shared_ptr<const Reflection::Tuple>(shared_ptr<const Reflection::Tuple>)> func_Invoke(
&BindableFunction::invoke, "Invoke", "arguments", Security::None);
static Reflection::BoundAsyncCallbackDesc<BindableFunction, shared_ptr<const Reflection::Tuple>(shared_ptr<const Reflection::Tuple>)>
callback_OnInvoke("OnInvoke", &BindableFunction::onInvoke, "arguments", &BindableFunction::processQueue);
#if 0
const char* const sPropertyInstance = "Property";
static Reflection::PropDescriptor<PropertyInstance, Reflection::Variant> prop_Value("Value", category_Data, &PropertyInstance::getValue, &PropertyInstance::setValue, Reflection::PropertyDescriptor::SCRIPTING);
static Reflection::BoundCallbackDesc<Reflection::Variant()> callback_GetValue("OnGetValue", &PropertyInstance::getCallback);
static Reflection::BoundCallbackDesc<void(Reflection::Variant)> callback_SetValue("OnSetValue", &PropertyInstance::setCallback, "value");
static Reflection::EventDesc<PropertyInstance, void(Reflection::Variant)> event_ValueChanged(&PropertyInstance::valueChanged, "ValueChanged", "value");
static Reflection::BoundFuncDesc<PropertyInstance, void()> func_FireValueChanged(&PropertyInstance::fireValueChanged, "FireValueChanged", Security::None);
#endif
const char* const sBindableEvent = "BindableEvent";
static Reflection::BoundFuncDesc<BindableEvent, void(shared_ptr<const Reflection::Tuple>)> func_Fire(
&BindableEvent::fire, "Fire", "arguments", Security::None);
static Reflection::EventDesc<BindableEvent, void(shared_ptr<const Reflection::Tuple>)> event_Event(&BindableEvent::event, "Event", "arguments");
REFLECTION_END();
void BindableFunction::processQueue(const OnInvokeCallback& oldValue)
{
if (onInvoke.empty())
return;
while (!queue.empty())
{
// Copy it to the stack to avoid re-entrancy bugs
const Invocation invocation = queue.front();
queue.pop();
onInvoke(invocation.arguments, invocation.resumeFunction, invocation.errorFunction);
}
}
void BindableFunction::invoke(shared_ptr<const Reflection::Tuple> arguments,
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (onInvoke.empty())
{
Invocation invocation = {arguments, resumeFunction, errorFunction};
queue.push(invocation);
return;
}
onInvoke(arguments, resumeFunction, errorFunction);
}
bool BindableFunction::askSetParent(const Instance* instance) const
{
return true;
}
void BindableEvent::fire(shared_ptr<const Reflection::Tuple> arguments)
{
event(arguments);
}
bool BindableEvent::askSetParent(const Instance* instance) const
{
return true;
}
#if 0
void PropertyInstance::fireValueChanged()
{
valueChanged(getCallback());
raisePropertyChanged(prop_Value);
}
Reflection::Variant PropertyInstance::getValue()
{
return getCallback();
}
void PropertyInstance::setValue(const Reflection::Variant& value)
{
setCallback(value);
}
#endif
} // namespace Aya

View File

@@ -0,0 +1,76 @@
#pragma once
#include "Tree/Instance.hpp"
namespace Aya
{
// Summary: A simple class that exposes a configurable function.
// Usage: It allows scripts to expose their functions
extern const char* const sBindableFunction;
class BindableFunction : public DescribedCreatable<BindableFunction, Instance, sBindableFunction>
{
struct Invocation
{
shared_ptr<const Reflection::Tuple> arguments;
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction;
boost::function<void(std::string)> errorFunction;
};
typedef std::queue<Invocation> Queue;
Queue queue;
public:
BindableFunction()
: DescribedCreatable<BindableFunction, Instance, sBindableFunction>("Function")
{
}
void invoke(shared_ptr<const Reflection::Tuple> arguments, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction,
boost::function<void(std::string)> errorFunction);
typedef boost::function<void(
shared_ptr<const Reflection::Tuple>, boost::function<void(shared_ptr<const Reflection::Tuple>)>, boost::function<void(std::string)>)>
OnInvokeCallback;
OnInvokeCallback onInvoke;
void processQueue(const OnInvokeCallback& oldValue);
/*override*/ bool askSetParent(const Instance* instance) const;
};
#if 0
// Summary: A simple class that exposes a property
// Usage: It allows scripts to expose their properties
extern const char* const sPropertyInstance;
class PropertyInstance : public DescribedCreatable<PropertyInstance, Instance, sPropertyInstance>
{
public:
PropertyInstance():DescribedCreatable<PropertyInstance, Instance, sPropertyInstance>("Property") {}
Aya::signal<void(Reflection::Variant)> valueChanged;
boost::function<Reflection::Variant()> getCallback;
boost::function<void(Reflection::Variant)> setCallback;
Reflection::Variant getValue();
void setValue(const Reflection::Variant& value);
void fireValueChanged();
};
#endif
// Summary: A simple class that exposes a fire-able event.
// Usage: It allows scripts to expose their events
extern const char* const sBindableEvent;
class BindableEvent : public DescribedCreatable<BindableEvent, Instance, sBindableEvent>
{
public:
BindableEvent()
: DescribedCreatable<BindableEvent, Instance, sBindableEvent>("Event")
{
}
Aya::signal<void(shared_ptr<const Reflection::Tuple>)> event;
void fire(shared_ptr<const Reflection::Tuple> arguments);
/*override*/ bool askSetParent(const Instance* instance) const;
};
} // namespace Aya

View File

@@ -0,0 +1,8 @@
#include "DataModel/BlockMesh.hpp"
using namespace Aya;
const char* const Aya::sBlockMesh = "BlockMesh";

View File

@@ -0,0 +1,15 @@
#pragma once
#include "BevelMesh.hpp"
namespace Aya
{
extern const char* const sBlockMesh;
class BlockMesh : public DescribedCreatable<BlockMesh, BevelMesh, sBlockMesh>
{
public:
BlockMesh() {}
};
} // namespace Aya

View File

@@ -0,0 +1,53 @@
#include "BloomEffect.hpp"
namespace Aya
{
const char* const sBloomEffect = "BloomEffect";
static Reflection::PropDescriptor<BloomEffect, float> prop_brightness("Intensity", "State", &BloomEffect::getIntensity, &BloomEffect::setIntensity, Reflection::PropertyDescriptor::STANDARD);
static Reflection::PropDescriptor<BloomEffect, float> prop_contrast("Size", "State", &BloomEffect::getSize, &BloomEffect::setSize, Reflection::PropertyDescriptor::STANDARD);
static Reflection::PropDescriptor<BloomEffect, float> prop_saturation("Threshold", "State", &BloomEffect::getThreshold, &BloomEffect::setThreshold, Reflection::PropertyDescriptor::STANDARD);
BloomEffect::BloomEffect()
: m_intensity(1)
, m_size(24)
, m_threshold(2)
{
setName(sBloomEffect);
}
BloomEffect::~BloomEffect()
{
//
}
void BloomEffect::setIntensity(float intensity)
{
if (m_intensity == intensity)
return;
m_intensity = intensity;
raisePropertyChanged(prop_brightness);
}
void BloomEffect::setSize(float size)
{
if (m_size == size)
return;
m_size = size;
raisePropertyChanged(prop_contrast);
}
void BloomEffect::setThreshold(float threshold)
{
if (m_threshold == threshold)
return;
m_threshold = threshold;
raisePropertyChanged(prop_saturation);
}
} // namespace Aya

View File

@@ -0,0 +1,29 @@
#pragma once
#include "PostEffect.hpp"
namespace Aya
{
extern const char* const sBloomEffect;
class BloomEffect : public DescribedCreatable<BloomEffect, PostEffect, sBloomEffect>
{
public:
BloomEffect();
~BloomEffect();
void setIntensity(float intensity);
void setSize(float size);
void setThreshold(float threshold);
float getIntensity() const { return m_intensity; }
float getSize() const { return m_size; }
float getThreshold() const { return m_threshold; }
private:
float m_intensity;
float m_size;
float m_threshold;
};
} // namespace Aya

View File

@@ -0,0 +1,30 @@
#include "BlurEffect.hpp"
namespace Aya
{
const char* const sBlurEffect = "BlurEffect";
static Reflection::PropDescriptor<BlurEffect, float> prop_size("Size", "State", &BlurEffect::getSize, &BlurEffect::setSize, Reflection::PropertyDescriptor::STANDARD);
BlurEffect::BlurEffect()
: m_size(4.0f)
{
setName(sBlurEffect);
}
BlurEffect::~BlurEffect()
{
//
}
void BlurEffect::setSize(float size)
{
if (m_size == size)
return;
m_size = size;
raisePropertyChanged(prop_size);
}
} // namespace Aya

View File

@@ -0,0 +1,22 @@
#pragma once
#include "PostEffect.hpp"
namespace Aya
{
extern const char* const sBlurEffect;
class BlurEffect : public DescribedCreatable<BlurEffect, PostEffect, sBlurEffect>
{
public:
BlurEffect();
~BlurEffect();
void setSize(float size);
float getSize() const { return m_size; }
private:
float m_size = 4.0f;
};
} // namespace Aya

View File

@@ -0,0 +1,242 @@
#include "DataModel/CSGDictionaryService.hpp"
#include "DataModel/PartOperation.hpp"
#include "DataModel/Value.hpp"
#include "DataModel/DataModel.hpp"
#include "DataModel/NonReplicatedCSGDictionaryService.hpp"
FASTFLAG(StudioCSGAssets)
FASTFLAG(CSGLoadFromCDN)
FASTFLAG(IgnoreBlankDataOnStore)
namespace
{
const std::string localKeyTag("CSGK");
const size_t minKeySize = 4;
} // namespace
namespace Aya
{
const char* const sCSGDictionaryService = "CSGDictionaryService";
CSGDictionaryService::CSGDictionaryService()
{
setName("CSGDictionaryService");
}
void CSGDictionaryService::reparentAllChildData()
{
if (numChildren() <= 0)
return;
std::vector<shared_ptr<Aya::Instance>> children;
children.insert(children.begin(), getChildren()->begin(), getChildren()->end());
for (std::vector<shared_ptr<Aya::Instance>>::const_iterator iter = children.begin(); iter != children.end(); ++iter)
reparentChildData(*iter);
}
void CSGDictionaryService::reparentChildData(shared_ptr<Aya::Instance> childInstance)
{
if (!isChildData(childInstance))
return;
NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create<NonReplicatedCSGDictionaryService>(DataModel::get(this));
childInstance->setParent(nrDictionaryService);
if (shared_ptr<Aya::BinaryStringValue> bStrValue = Aya::Instance::fastSharedDynamicCast<Aya::BinaryStringValue>(childInstance))
{
std::string key = createHashKey(bStrValue->getValue().value());
instanceMap.erase(key);
}
}
void CSGDictionaryService::storeData(PartOperation& partOperation, bool forceIncrement)
{
BinaryString tmpString;
tmpString = partOperation.getMeshData();
if (FFlag::IgnoreBlankDataOnStore && tmpString.value().size() > 0)
{
storeStringData(tmpString, forceIncrement, "MeshData");
partOperation.setMeshData(tmpString);
}
tmpString = partOperation.getPhysicsData();
if (FFlag::IgnoreBlankDataOnStore && tmpString.value().size() > 0)
{
storeStringData(tmpString, forceIncrement, "PhysicsData");
}
partOperation.setPhysicsData(tmpString);
}
void CSGDictionaryService::retrieveData(PartOperation& partOperation)
{
BinaryString tmpString;
tmpString = partOperation.getMeshData();
retrieveStringData(tmpString);
partOperation.setMeshData(tmpString);
tmpString = partOperation.getPhysicsData();
retrieveStringData(tmpString);
partOperation.setPhysicsData(tmpString);
}
void CSGDictionaryService::storeAllDescendants(shared_ptr<Instance> instance)
{
if (instance->getChildren())
for (Aya::Instances::const_iterator iter = instance->getChildren()->begin(); iter != instance->getChildren()->end(); ++iter)
storeAllDescendants(*iter);
if (shared_ptr<PartOperation> childOperation = Aya::Instance::fastSharedDynamicCast<PartOperation>(instance))
storeData(*childOperation);
}
void CSGDictionaryService::retrieveAllDescendants(shared_ptr<Instance> instance)
{
if (instance->getChildren())
for (Aya::Instances::const_iterator iter = instance->getChildren()->begin(); iter != instance->getChildren()->end(); ++iter)
retrieveAllDescendants(*iter);
if (shared_ptr<PartOperation> childOperation = Aya::Instance::fastSharedDynamicCast<PartOperation>(instance))
retrieveData(*childOperation);
}
void CSGDictionaryService::refreshRefCountUnderInstance(Aya::Instance* instance)
{
if (Aya::PartOperation* partOperation = Aya::Instance::fastDynamicCast<Aya::PartOperation>(instance))
storeData(*partOperation, true);
if (instance->getChildren())
for (Aya::Instances::const_iterator iter = instance->getChildren()->begin(); iter != instance->getChildren()->end(); ++iter)
refreshRefCountUnderInstance(iter->get());
}
boost::shared_ptr<CSGMesh> CSGDictionaryService::insertMesh(const std::string key, const Aya::BinaryString& meshData)
{
shared_ptr<CSGMesh> mesh = shared_ptr<CSGMesh>(CSGMeshFactory::singleton()->createMesh());
mesh->fromBinaryString(meshData.value());
cachedMeshMap.insert(std::make_pair(key, mesh));
return cachedMeshMap.at(key);
}
void CSGDictionaryService::insertMesh(PartOperation& partOperation)
{
const BinaryString& keyIn = partOperation.getMeshData();
if (keyIn.value().size() > minKeySize)
{
const std::string key = getLocalKeyHash(keyIn);
if (strncmp(keyIn.value().c_str(), localKeyTag.c_str(), minKeySize) == 0)
{
if (!cachedBREPMeshMap.count(key))
{
shared_ptr<CSGMesh> mesh = partOperation.getMesh();
cachedBREPMeshMap.insert(std::make_pair(key, mesh));
if (cachedMeshMap.count(key))
cachedMeshMap.erase(key);
}
}
}
}
boost::shared_ptr<CSGMesh> CSGDictionaryService::getMesh(PartOperation& partOperation)
{
const BinaryString& keyIn = partOperation.getMeshData();
if (keyIn.value().size() > minKeySize)
{
std::string key = getLocalKeyHash(keyIn);
if (strncmp(keyIn.value().c_str(), localKeyTag.c_str(), minKeySize) == 0)
{
if (cachedBREPMeshMap.count(key))
{
return cachedBREPMeshMap.at(key);
}
else if (cachedMeshMap.count(key))
{
return cachedMeshMap.at(key);
}
else if (instanceMap.count(key))
{
if (shared_ptr<Aya::BinaryStringValue> meshData = instanceMap.at(key).ref.lock())
return insertMesh(key, meshData->getValue());
}
}
}
return boost::shared_ptr<CSGMesh>();
}
void CSGDictionaryService::retrieveMeshData(PartOperation& partOperation)
{
BinaryString tmpString = partOperation.getMeshData();
retrieveStringData(tmpString);
partOperation.setMeshData(tmpString);
}
void CSGDictionaryService::storePhysicsData(PartOperation& partOperation, bool forceIncrement)
{
BinaryString tmpString = partOperation.getPhysicsData();
storeStringData(tmpString, forceIncrement, "PhysicsData");
partOperation.setPhysicsData(tmpString);
}
void CSGDictionaryService::retrievePhysicsData(PartOperation& partOperation)
{
BinaryString tmpString = partOperation.getPhysicsData();
retrieveStringData(tmpString);
partOperation.setPhysicsData(tmpString);
}
void CSGDictionaryService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
FlyweightService::onServiceProvider(oldProvider, newProvider);
if (FFlag::CSGLoadFromCDN && !oldProvider && newProvider)
{
DataModel* dataModel = DataModel::get(this);
if (!dataModel)
return;
dataModel->workspaceLoadedSignal.connect(boost::bind(&CSGDictionaryService::onWorkspaceLoaded, shared_from(this)));
}
}
void findAndStripMeshData(std::set<BinaryString>* keys, shared_ptr<Instance> descendant)
{
if (shared_ptr<PartOperation> partOperation = Instance::fastSharedDynamicCast<PartOperation>(descendant))
{
if (partOperation->hasAsset() && partOperation->getMeshData().value().size() > 0)
{
keys->insert(partOperation->getMeshData());
partOperation->setMeshData(BinaryString());
}
}
}
void CSGDictionaryService::onWorkspaceLoaded()
{
std::set<BinaryString> keys;
DataModel* dataModel = DataModel::get(this);
if (!dataModel)
return;
dataModel->visitDescendants(boost::bind(&findAndStripMeshData, &keys, _1));
for (std::set<BinaryString>::iterator iter = keys.begin(); iter != keys.end(); ++iter)
{
removeStringData((*iter));
}
}
} // namespace Aya

View File

@@ -0,0 +1,64 @@
#pragma once
#include "Utility/BinaryString.hpp"
#include "Tree/Instance.hpp"
#include "Tree/Service.hpp"
#include "DataModel/FlyweightService.hpp"
#include <boost/unordered_map.hpp>
#include "Value.hpp"
namespace Aya
{
class PartOperation;
class CSGMesh;
extern const char* const sCSGDictionaryService;
class CSGDictionaryService
: public DescribedCreatable<CSGDictionaryService, FlyweightService, sCSGDictionaryService, Reflection::ClassDescriptor::PERSISTENT,
Security::Roblox>
{
protected:
typedef DescribedCreatable<CSGDictionaryService, FlyweightService, sCSGDictionaryService, Reflection::ClassDescriptor::PERSISTENT,
Security::Roblox>
Super;
typedef boost::unordered_map<std::string, boost::shared_ptr<CSGMesh>> CSGMeshMap;
CSGMeshMap cachedBREPMeshMap;
CSGMeshMap cachedMeshMap;
void reparentChildData(shared_ptr<Aya::Instance> sharedInstance);
virtual void refreshRefCountUnderInstance(Aya::Instance* instance);
boost::shared_ptr<CSGMesh> insertMesh(const std::string key, const Aya::BinaryString& meshData);
boost::shared_ptr<CSGMesh> insertCachedMesh(const std::string key, const Aya::BinaryString& meshData);
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
public:
CSGDictionaryService();
void storeData(PartOperation& partOperation, bool forceIncrement = false);
void retrieveData(PartOperation& partOperation);
void storeAllDescendants(shared_ptr<Aya::Instance> instance);
void retrieveAllDescendants(shared_ptr<Aya::Instance> instance);
void reparentAllChildData();
void insertMesh(PartOperation& partOperation);
boost::shared_ptr<CSGMesh> getMesh(PartOperation& partOperation);
boost::shared_ptr<CSGMesh> getCachedMesh(PartOperation& partOperation);
void retrieveMeshData(PartOperation& partOperation);
void storePhysicsData(PartOperation& partOperation, bool forceIncrement = false);
void retrievePhysicsData(PartOperation& partOperation);
void onWorkspaceLoaded();
};
} // namespace Aya

View File

@@ -0,0 +1,348 @@
#include "DataModel/CSGMesh.hpp"
#include "World/TriangleMesh.hpp"
#include "Utility/Lcmrand.hpp"
#include <algorithm>
#include <fstream>
#include <streambuf>
#include <sstream>
#include "Utility/MD5Hasher.hpp"
FASTFLAGVARIABLE(FixGlowingCSG, true)
using namespace G3D;
#ifndef CSG_KERNEL_OLD
namespace Aya
{
namespace
{
CSGMeshFactory* csgMeshFactory = 0;
}
void CSGMeshFactory::set(CSGMeshFactory* factory)
{
csgMeshFactory = factory;
}
CSGMeshFactory* CSGMeshFactory::singleton()
{
if (csgMeshFactory)
return csgMeshFactory;
static CSGMeshFactory* meshFactory = new CSGMeshFactory;
return meshFactory;
}
CSGMesh* CSGMeshFactory::createMesh()
{
return new CSGMesh;
}
CSGMesh::CSGMesh()
: version(2)
, brepVersion(1)
, badMesh(false)
{
}
CSGMesh::~CSGMesh() {}
void CSGMesh::clearMesh()
{
vertices.clear();
indices.clear();
}
//////////////////////////////////////////////////////////////////////
// The below code is to provide a first line of defense against expert users
// injecting random data or their own data into the geometry stream.
// It is not expected to completely prevent attempts at injection but
// it will slow down the process so that we can move the generation of
// the mesh to a service on a server.
///////////////////////////////////////////////////////////////////////
namespace
{
void generateRandomString(char* s, const size_t len)
{
for (size_t i = 0; i < len; ++i)
{
s[i] = (char)rand();
}
}
} // namespace
const size_t saltSize = 16;
const size_t hashSize = 16;
std::string CSGMesh::createHash(const std::string saltIn) const
{
const size_t verticesSize = vertices.size() * sizeof(CSGVertex);
const size_t indicesSize = indices.size() * sizeof(unsigned int);
size_t buffSize = verticesSize + indicesSize + saltSize;
std::vector<unsigned char> byteBuffer(buffSize);
std::vector<char> hash(saltSize + hashSize);
std::string salt = saltIn;
if (salt.empty())
{
salt.resize(saltSize);
generateRandomString(&salt[0], salt.size());
}
size_t copyOffset = 0;
memcpy(&byteBuffer[copyOffset], &vertices[0], verticesSize);
copyOffset += verticesSize;
memcpy(&byteBuffer[copyOffset], &indices[0], indicesSize);
copyOffset += indicesSize;
memcpy(&byteBuffer[copyOffset], salt.c_str(), salt.size());
LcmRand randGen;
for (size_t i = 0; i < buffSize; i++)
{
std::swap(byteBuffer[i], byteBuffer[randGen.value() % buffSize]);
}
boost::scoped_ptr<Aya::MD5Hasher> hasher(Aya::MD5Hasher::create());
hasher->addData((const char*)&byteBuffer[0], byteBuffer.size());
memcpy(&hash[0], hasher->toString().c_str(), hashSize);
memcpy(&hash[hashSize], salt.c_str(), saltSize);
std::string hashStr(&hash[0], hashSize + saltSize);
return hashStr;
}
void CSGMesh::computeDecalRemap()
{
if (!FFlag::FixGlowingCSG)
return;
for (unsigned i = 0; i < 6; ++i)
{
decalVertexRemap[i].clear();
decalIndexRemap[i].clear();
}
std::vector<unsigned> tmpTranslation;
tmpTranslation.resize(vertices.size());
for (unsigned vi = 0; vi < vertices.size(); ++vi)
{
unsigned face = vertices[vi].extra.r - 1;
AYAASSERT(face < 6);
decalVertexRemap[face].push_back(vi);
tmpTranslation[vi] = decalVertexRemap[face].size() - 1;
}
for (unsigned ii = 0; ii < indices.size(); ++ii)
{
unsigned face = vertices[indices[ii]].extra.r - 1;
decalIndexRemap[face].push_back(tmpTranslation[indices[ii]]);
}
}
void xorBuffer(std::string& buffer)
{
const size_t basicEncryptionKeySize = 31;
LcmRand randGen;
std::string basicEncryptionKey;
basicEncryptionKey.resize(basicEncryptionKeySize);
for (size_t i = 0; i < basicEncryptionKey.size(); i++)
basicEncryptionKey[i] = randGen.value() % CHAR_MAX;
for (size_t i = 0; i < buffer.size(); i++)
buffer[i] = buffer[i] ^ basicEncryptionKey[i % basicEncryptionKey.size()];
}
std::string headerTag("CSGMDL");
std::string CSGMesh::toBinaryString() const
{
std::stringstream stream;
stream.write(headerTag.c_str(), headerTag.size());
stream.write(reinterpret_cast<const char*>(&version), sizeof(version));
std::string hash = createHash();
stream.write(reinterpret_cast<const char*>(hash.c_str()), hashSize + saltSize);
unsigned int numVertices = vertices.size();
unsigned int vertexStride = sizeof(CSGVertex);
stream.write(reinterpret_cast<const char*>(&numVertices), sizeof(unsigned int));
stream.write(reinterpret_cast<const char*>(&vertexStride), sizeof(unsigned int));
stream.write(reinterpret_cast<const char*>(&vertices[0]), vertexStride * numVertices);
unsigned int numIndices = indices.size();
stream.write(reinterpret_cast<const char*>(&numIndices), sizeof(unsigned int));
stream.write(reinterpret_cast<const char*>(&indices[0]), sizeof(unsigned int) * numIndices);
std::string buffer(stream.str().c_str(), stream.str().size());
xorBuffer(buffer);
return buffer;
}
std::string CSGMesh::toBinaryStringForPhysics() const
{
std::vector<btVector3> vertexPositions;
for (unsigned int i = 0; i < vertices.size(); i++)
vertexPositions.push_back(btVector3(vertices[i].pos.x, vertices[i].pos.y, vertices[i].pos.z));
return TriangleMesh::generateStaticMeshData(indices, vertexPositions);
}
bool CSGMesh::fromBinaryString(const std::string& str)
{
std::string buffer(str.c_str(), str.size());
xorBuffer(buffer);
std::stringstream stream(buffer);
int currentVersion = version;
std::string fileId;
fileId.resize(headerTag.size());
stream.read(&fileId[0], headerTag.size());
if (fileId != headerTag)
return false;
stream.read(reinterpret_cast<char*>(&version), sizeof(int));
if (version != currentVersion)
return false;
std::string hash;
hash.resize(hashSize + saltSize);
stream.read(reinterpret_cast<char*>(&hash[0]), hashSize + saltSize);
unsigned int numVertices = 0;
unsigned int vertexStride = 0;
stream.read(reinterpret_cast<char*>(&numVertices), sizeof(unsigned int));
stream.read(reinterpret_cast<char*>(&vertexStride), sizeof(unsigned int));
if (vertexStride != sizeof(CSGVertex))
return false;
vertices.resize(numVertices);
stream.read(reinterpret_cast<char*>(&vertices[0]), vertexStride * numVertices);
unsigned int numIndices = 0;
stream.read(reinterpret_cast<char*>(&numIndices), sizeof(unsigned int));
indices.resize(numIndices);
stream.read(reinterpret_cast<char*>(&indices[0]), sizeof(unsigned int) * numIndices);
std::string salt(&hash[hashSize], saltSize);
std::string newHash = createHash(salt);
if (hash != newHash)
badMesh = true;
computeDecalRemap();
return true;
}
void CSGMesh::set(const std::vector<CSGVertex>& verticesIn, const std::vector<unsigned int>& indicesIn)
{
vertices = verticesIn;
indices = indicesIn;
}
CSGMesh* CSGMesh::clone() const
{
CSGMesh* mesh = new CSGMesh(*this);
return mesh;
}
void CSGVertex::generateUv()
{
switch (extra.r)
{
case CSGVertex::UV_BOX_X:
uv = Vector2(-pos.z, -pos.y);
break;
case CSGVertex::UV_BOX_X_NEG:
uv = Vector2(pos.z, -pos.y);
break;
case CSGVertex::UV_BOX_Y:
uv = Vector2(-pos.x, -pos.z);
break;
case CSGVertex::UV_BOX_Y_NEG:
uv = Vector2(pos.x, -pos.z);
break;
case CSGVertex::UV_BOX_Z:
uv = Vector2(pos.x, -pos.y);
break;
case CSGVertex::UV_BOX_Z_NEG:
uv = Vector2(-pos.x, -pos.y);
break;
}
}
Vector2 CSGVertex::generateUv(const Vector3& posIn) const
{
Vector2 uvResult;
switch (extra.r)
{
case CSGVertex::NO_UV_GENERATION:
uvResult = uv;
break;
case CSGVertex::UV_BOX_X:
uvResult = Vector2(-posIn.z, -posIn.y);
break;
case CSGVertex::UV_BOX_X_NEG:
uvResult = Vector2(posIn.z, -posIn.y);
break;
case CSGVertex::UV_BOX_Y:
uvResult = Vector2(-posIn.x, -posIn.z);
break;
case CSGVertex::UV_BOX_Y_NEG:
uvResult = Vector2(posIn.x, -posIn.z);
break;
case CSGVertex::UV_BOX_Z:
uvResult = Vector2(posIn.x, -posIn.y);
break;
case CSGVertex::UV_BOX_Z_NEG:
uvResult = Vector2(-posIn.x, -posIn.y);
break;
}
return uvResult;
}
bool CSGMesh::isNotEmpty() const
{
if ((getVertices().size() > 0) && (getIndices().size() > 0))
return true;
return false;
}
} // namespace Aya
#endif

View File

@@ -0,0 +1,179 @@
#pragma once
#include "platform.hpp"
#include "g3dmath.hpp"
#include "Vector2.hpp"
#include "Vector3.hpp"
#include "Vector4.hpp"
#include "Color4uint8.hpp"
#include "Color3uint8.hpp"
#include "CoordinateFrame.hpp"
#include "Debug.hpp"
namespace Aya
{
class CSGVertex
{
public:
G3D::Vector3 pos;
G3D::Vector3 normal;
G3D::Color4uint8 color;
G3D::Color4uint8 extra; // red = uv generation type
G3D::Vector2 uv;
G3D::Vector2 uvStuds;
G3D::Vector2 uvDecal;
G3D::Vector3 tangent;
G3D::Vector4 edgeDistances;
enum UVGenerationType
{
NO_UV_GENERATION = 0,
UV_BOX_X,
UV_BOX_Y,
UV_BOX_Z,
UV_BOX_X_NEG,
UV_BOX_Y_NEG,
UV_BOX_Z_NEG
};
CSGVertex()
{
;
}
G3D::Vector2 generateUv(const Vector3& pos) const;
void generateUv();
};
class CSGMesh
{
public:
CSGMesh();
virtual ~CSGMesh();
virtual CSGMesh* clone() const;
const std::vector<CSGVertex>& getVertices() const
{
return vertices;
}
const std::vector<unsigned int>& getIndices() const
{
return indices;
}
const std::vector<unsigned>& getIndexRemap(unsigned idx) const
{
AYAASSERT(idx < 6);
return decalIndexRemap[idx];
}
const std::vector<unsigned>& getVertexRemap(unsigned idx) const
{
AYAASSERT(idx < 6);
return decalVertexRemap[idx];
}
std::string createHash(const std::string salt = "") const;
bool isBadMesh() const
{
return badMesh;
}
virtual bool isValid() const
{
return true;
}
void set(const std::vector<CSGVertex>& vertices, const std::vector<unsigned int>& indices);
void clearMesh();
virtual void translate(const G3D::Vector3& translation) {}
virtual void applyCoordinateFrame(G3D::CoordinateFrame cFrame) {}
virtual void applyTranslation(const G3D::Vector3& trans) {}
virtual void applyColor(const G3D::Vector3& color) {}
virtual void applyScale(const G3D::Vector3& size) {}
virtual void triangulate() {}
virtual bool newTriangulate()
{
return true;
}
virtual void weldMesh(bool positionOnly = false) {}
virtual void buildBRep() {}
virtual bool unionMesh(const CSGMesh* a, const CSGMesh* b)
{
return false;
}
virtual bool intersectMesh(const CSGMesh* a, const CSGMesh* b)
{
return false;
}
virtual bool subractMesh(const CSGMesh* a, const CSGMesh* b)
{
return false;
}
bool isNotEmpty() const;
std::string toBinaryString() const;
std::string toBinaryStringForPhysics() const;
bool fromBinaryString(const std::string& str);
virtual std::string getBRepBinaryString() const
{
return "";
}
virtual void setBRepFromBinaryString(const std::string& str) {}
virtual size_t clusterVertices(float resolution)
{
return 0;
}
virtual bool makeHalfEdges(std::vector<int>& vertexEdges)
{
return true;
}
virtual G3D::Vector3 extentsCenter()
{
G3D::Vector3 v;
return v;
}
virtual G3D::Vector3 extentsSize()
{
G3D::Vector3 v;
return v;
}
void computeDecalRemap();
protected:
int version;
int brepVersion;
bool badMesh;
std::vector<CSGVertex> vertices;
std::vector<unsigned int> indices;
std::vector<unsigned> decalVertexRemap[6];
std::vector<unsigned> decalIndexRemap[6];
};
class CSGMeshFactory
{
public:
virtual CSGMesh* createMesh();
static CSGMeshFactory* singleton();
static void set(CSGMeshFactory* factory);
};
} // namespace Aya

View File

@@ -0,0 +1,98 @@
#pragma once
#include <vector>
#include "Tree/Service.hpp"
#include "DataModel/ContentProvider.hpp"
#include "Utility/ContentProviderJob.hpp"
#include "Utility/ControlledLRUCache.hpp"
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
typedef boost::unordered_set<std::string> Set;
namespace Aya
{
extern const char* const sCacheableContentProvider;
class CacheableContentProvider
: public DescribedNonCreatable<CacheableContentProvider, Instance, sCacheableContentProvider, Aya::Reflection::ClassDescriptor::RUNTIME_LOCAL>
, public Service
, public HeartbeatInstance
{
typedef DescribedNonCreatable<CacheableContentProvider, Instance, sCacheableContentProvider, Aya::Reflection::ClassDescriptor::RUNTIME_LOCAL>
Super;
protected:
class CachedItem
{
public:
AsyncHttpQueue::RequestResult requestResult;
shared_ptr<void> data;
public:
CachedItem(shared_ptr<void> data = shared_ptr<void>(), AsyncHttpQueue::RequestResult result = AsyncHttpQueue::Waiting)
: data(data)
, requestResult(result)
{
}
~CachedItem()
{
data.reset();
}
friend class CacheableContentProvider;
};
shared_ptr<ContentProviderJob> contentJob;
Aya::atomic<int> pendingRequests;
bool immediateMode;
boost::scoped_ptr<ConcurrentControlledLRUCache<std::string, boost::shared_ptr<CachedItem>>> lruCache;
boost::mutex failedCacheMutex;
Set failedCache;
public:
CacheableContentProvider(CacheSizeEnforceMethod enforceMethod, unsigned long size);
virtual ~CacheableContentProvider();
void setCacheSize(int size);
void setImmediateMode();
bool isRequestQueueEmpty()
{
return pendingRequests == 0;
}
bool clearContent();
bool hasContent(const ContentId& id);
boost::shared_ptr<void> requestContent(const ContentId& id, float priority, bool markUsed, AsyncHttpQueue::RequestResult& result);
boost::shared_ptr<void> blockingRequestContent(const ContentId& id, bool markUsed);
boost::shared_ptr<void> fetchContent(const ContentId& id);
bool isAssetFailed(const ContentId& id);
// HeartbeatInstance
/*override*/ void onHeartbeat(const Heartbeat& event);
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
protected:
static void LoadContentCallbackHelper(boost::weak_ptr<CacheableContentProvider> cacheableContentProvider, AsyncHttpQueue::RequestResult result,
std::istream* filestream, shared_ptr<const std::string> data, std::string id);
void LoadContentCallback(AsyncHttpQueue::RequestResult result, std::istream* filestream, shared_ptr<const std::string> data, std::string id);
static TaskScheduler::StepResult ProcessTaskHelper(
boost::weak_ptr<CacheableContentProvider> weakCcp, const std::string& id, shared_ptr<const std::string> data);
virtual TaskScheduler::StepResult ProcessTask(const std::string& id, shared_ptr<const std::string> data) = 0;
static void ErrorTaskHelper(boost::weak_ptr<CacheableContentProvider> weakCcp, const std::string& id);
virtual void ErrorTask(const std::string& id);
bool isAssetContent(ContentId id);
void markContentFailed(const std::string& id);
AsyncHttpQueue::RequestResult getContentStatus(const std::string& id);
virtual void updateContent(const std::string& id, boost::shared_ptr<CachedItem> cachedItem);
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,382 @@
#pragma once
#include "Tree/Instance.hpp"
#include "Utility/G3DCore.hpp"
#include "Utility/HeartbeatInstance.hpp"
#include <vector>
#include "Frustum.hpp"
namespace Aya
{
class ICameraOwner;
class CameraSubject;
class ContactManager;
class Primitive;
class NavKeys;
class Extents;
class ModelInstance;
class PartInstance;
class HitTestFilter;
extern const char* const sCamera;
class Camera
: public DescribedCreatable<Camera, Instance, sCamera, Reflection::ClassDescriptor::PERSISTENT_LOCAL>
, public HeartbeatInstance
{
public:
Aya::signal<void()> interpolationFinishedSignal;
Aya::signal<void(bool)> firstPersonTransitionSignal;
Aya::signal<void(CoordinateFrame)> cframeChangedSignal;
// Warning - these enums are part of the XML - only append
enum CameraType
{
FIXED_CAMERA = 0,
ATTACH_CAMERA = 1, // maintain fixed position and rotation w.r.t target coordinate frame
WATCH_CAMERA = 2, // rotate to keep target in view
TRACK_CAMERA = 3, // translate to keep target in view
FOLLOW_CAMERA = 4, // rotate and translate to keep target in view
CUSTOM_CAMERA = 5,
LOCKED_CAMERA = 6,
NUM_CAMERA_TYPE = 7
};
typedef enum
{
ZOOM_IN_OR_OUT,
ZOOM_OUT_ONLY
} ZoomType;
enum CameraMode
{
CAMERAMODE_CLASSIC = 0,
CAMERAMODE_LOCKFIRSTPERSON = 1
};
enum CameraPanMode
{
CAMERAPANMODE_CLASSIC = 0,
CAMERAPANMODE_EDGEBUMP = 1,
};
static float distanceDefault()
{
return 36.0f;
} // grids
static float distanceMin()
{
return 0.5f;
} // down from 4.0
static float distanceMax()
{
return 1000.0f;
}
static float distanceMaxCharacter()
{
return 400.0f;
}
static float distanceMinOcclude()
{
return 4.0f;
}
static double interpolationSpeed()
{
return 5.0f;
} // studs per second
static bool legalCameraCoord(const CoordinateFrame& c);
static float CameraKeyMoveFactor;
static float CameraMouseWheelMoveFactor;
static float CameraShiftKeyMoveFactor;
Camera();
~Camera() {}
// Yuck - called by Window code
void step(double elapsedTime);
void stepSubject();
void pushCameraHistoryStack();
std::pair<CoordinateFrame, CoordinateFrame> popCameraHistoryStack(bool backward);
void getHeadingElevationDistance(float& heading, float& elevation, float& distance);
bool isCharacterCamera() const;
bool isFirstPersonCamera() const; // hack for now - this should be in CameraSubject?? discuss
bool isPartInFrustum(const PartInstance& part) const;
bool isPartVisibleFast(const PartInstance& part, const ContactManager& contactManager,
const HitTestFilter* filter = NULL) const; // uses only one ray to do check (can have inaccurate results, but takes 1/4 the time)
bool isLockedToFirstPerson() const;
CameraSubject* getCameraSubject();
const CameraSubject* getConstCameraSubject() const;
Instance* getCameraSubjectInstanceDangerous() const; // for reflection only
void setCameraSubject(Instance* newSubject);
void setCameraLerpGoals(const CoordinateFrame& cameraCoordValue, const CoordinateFrame& cameraFocusValue);
const CoordinateFrame& getCameraFocus() const
{
return cameraFocus;
}
void setCameraFocus(const CoordinateFrame& value); // sets camera focus and camera focus goal
void setCameraFocusWithoutPropertyChange(const CoordinateFrame& value);
void setCameraFocusOnly(const CoordinateFrame& value); // sets camera focus
void setCameraFocusOnlyWithoutPropertyChange(const CoordinateFrame& value);
void setCameraFocusAndMaintainFocus(const CoordinateFrame& value, bool maintainFocusOnPoint);
const CoordinateFrame& getCameraCoordinateFrame() const
{
return cameraCoord;
}
void setCameraCoordinateFrame(const CoordinateFrame& value);
CoordinateFrame getRenderingCoordinateFrame() const;
CoordinateFrame getRenderingCoordinateFrameLua()
{
return getRenderingCoordinateFrame();
}
bool getHeadLocked() const
{
return headLocked;
}
void setHeadLocked(bool value);
CameraType getCameraType() const
{
return cameraType;
}
void setCameraType(CameraType value);
bool canZoom(bool inwards) const;
bool canTilt(int up) const;
// Cursor Control
void onMousePan(const Vector2& wrapMouseDelta);
void onMouseTrack(const Vector2& wrapMouseDelta);
// Zoom
bool zoom(float in);
bool setDistanceFromTarget(float newDistance);
bool setDistanceFromTarget(float newDistance, CoordinateFrame& newCameraPos, const CoordinateFrame& newCameraFocus);
bool zoomExtents(); // of the world
void zoomExtents(const ModelInstance* model, ZoomType zoomType);
void zoomExtents(const Extents& extents, ZoomType zoomType);
void lerpToExtents(const Extents& extents);
// Pan
void panRadians(float angle);
void panUnits(int units);
void panSpeedRadians(float tilt);
float getPanSpeed(void)
{
return panSpeed;
}
// Tilt
bool tiltRadians(float up);
bool tiltUnits(int up);
void tiltSpeedRadians(float tilt);
float getTiltSpeed(void)
{
return tiltSpeed;
}
//
void setCameraPanMode(Camera::CameraPanMode mode);
Camera::CameraPanMode getCameraPanMode() const
{
return cameraPanMode;
}
// LookAt
void lookAt(const Vector3& point, bool lerpCamera);
void setImageServerViewNoLerp(const CoordinateFrame& modelCoord);
// Fly
void doFly(const NavKeys& nav, int steps);
// Camera History
void stepCameraHistoryForward();
void stepCameraHistoryBackward();
// Util
static float getNewZoomDistance(float currentDistance, float in);
bool hasClientPlayer() const;
// Clipping plane, *not* imaging plane. Returns a negative z-value.
float nearPlaneZ() const;
// Returns a negative z-value.
inline float farPlaneZ() const
{
return -5e3f;
}
inline float getFieldOfView() const
{
return fieldOfView;
}
inline float getFieldOfViewDegrees() const
{
return G3D::toDegrees(fieldOfView);
}
void setFieldOfViewDegrees(float value);
inline float getRoll() const
{
return roll;
}
void setRoll(float value);
float getRollSlow();
// Taken from RbxCamera, a derivative of the G3D Camera.
// Returns the image plane depth, <I>s'</I>, given the current field
// of view for film of dimensions width x height. See
// setImagePlaneDepth for a discussion of worldspace values width and height.
float getImagePlaneDepth() const;
// Returns the Camera space width of the viewport.
float getViewportWidth() const;
// Returns the Camera space height of the viewport.
float getViewportHeight() const;
// Taken from RbxCamera, a derivative of the G3D Camera.
// Projects a world space point onto a width x height screen. The
// returned coordinate uses pixmap addressing: x = right and y =
// down. The resulting z value is <I>rhw</I>.
// If the point is behind the camera, Vector3::inf() is returned.
Vector3 project(const Vector3& point) const;
shared_ptr<const Reflection::Tuple> projectLua(Vector3 point);
shared_ptr<const Reflection::Tuple> projectViewportLua(Vector3 point);
// Taken from RbxCamera, a derivative of the G3D Camera.
// Returns the world space ray passing through the center of pixel
// (x, y) on the image plane. The pixel x and y axes are opposite
// the 3D object space axes: (0,0) is the upper left corner of the screen.
// They are in viewport coordinates, not screen coordinates.
// Integer (x, y) values correspond to
// the upper left corners of pixels. If you want to cast rays
// through pixel centers, add 0.5 to x and y.
RbxRay worldRay(float x, float y, float depth = 0.0f) const;
RbxRay worldRayLua(float x, float y, float depth);
RbxRay worldRayViewportLua(float x, float y, float depth);
// Taken from RbxCamera, a derivative of the G3D Camera.
// Returns the world space view frustum, which is a truncated pyramid describing
// the volume of space seen by this camera.
void frustum(const float farPlaneZ, Aya::Frustum& fr) const;
Aya::Frustum frustum() const;
const CoordinateFrame& coordinateFrame() const;
Vector2int16 getViewport() const
{
return viewport;
}
void setViewport(Vector2int16 newViewport);
// Calculates the dot product of camera direction and point. Useful for
// half space test or getting angle between the 2
float dot(const Vector3& point) const;
void beginCameraInterpolation(CoordinateFrame endPos, CoordinateFrame endFocus, float duration);
Vector4 projectPointToScreen(const Vector3& point) const;
private:
typedef DescribedCreatable<Camera, Instance, sCamera, Reflection::ClassDescriptor::PERSISTENT_LOCAL> Super;
enum CameraInterpolation
{
CAM_INTERPOLATION_CONSTANT_TIME,
CAM_INTERPOLATION_CONSTANT_SPEED,
CAM_INTERPOLATION_NONE,
};
void updateFocus();
bool isEditMode() const;
bool characterZoom(float in);
bool nonCharacterZoom(float in);
void tryZoomExtents(const Extents& extents);
float cameraToFocusDistance() const
{
return (cameraCoord.translation - cameraFocus.translation).magnitude();
}
void setHeadingElevationDistance(float heading, float elevation, float distance);
// Instance
/*override*/ bool askSetParent(const Instance* instance) const;
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
Super::onServiceProvider(oldProvider, newProvider);
onServiceProviderHeartbeatInstance(oldProvider, newProvider); // hooks up heartbeat
}
void fixedSpeedInterpolateCamera(double elapsedTime);
void zoomOut(CoordinateFrame& cameraPos, CoordinateFrame& cameraFocus, float currentFocusToCameraDistance);
Matrix4 getProjectionPerspective() const;
void stopInterpolation();
void signalInterpolationDone();
// HeartbeatInstance
/*override*/ void onHeartbeat(const Heartbeat& event);
CameraInterpolation camInterpolation;
CoordinateFrame cameraCoord; // Where is the camera
CoordinateFrame cameraFocus; // Looking at what?
CoordinateFrame cameraCoordGoal; // Where we want to be (used for camera transitions)
CoordinateFrame cameraFocusGoal; // Where we want to look (used for camera transitions)
CoordinateFrame cameraCoordPrev; // Where we used to be (used for camera interpolation)
CoordinateFrame cameraFocusPrev; // Where we used to look (used for camera interpolation)
G3D::Vector3 cameraUpDirPrev;
float interpolationDuration; // Time in seconds to interpolate position and focus; < 0 for constant speed interpolation
mutable float interpolationTime; // Time in seconds that we have been doing the current interpolation
bool headLocked;
CameraType cameraType;
shared_ptr<Instance> cameraSubject; // Guaranteed to be a CameraSubject
float fieldOfView;
float roll;
Vector2int16 viewport;
float panSpeed;
float tiltSpeed;
CameraPanMode cameraPanMode;
// The image plane depth corresponding to a vertical field of
// view, where the film size is 1x1.
float imagePlaneDepth;
bool hasFocalObject;
ICameraOwner* getCameraOwner();
std::vector<std::pair<CoordinateFrame, CoordinateFrame>> cameraHistoryStack;
int currentCameraHistoryPosition;
double lastHistoryPushTime;
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,166 @@
#pragma once
#include "Tree/Service.hpp"
#include "Utility/RunStateOwner.hpp"
#include "Voxel/Cell.hpp"
#include "Voxel/CellChangeListener.hpp"
#include "Utility/SpatialRegion.hpp"
#include <queue>
#include "Voxel2/GridListener.hpp"
namespace Aya
{
class DataModel;
class MegaClusterInstance;
extern const char* const sChangeHistoryService;
class ChangeHistoryService
: public DescribedCreatable<ChangeHistoryService, Instance, sChangeHistoryService, Reflection::ClassDescriptor::INTERNAL>
, public Service
, public Voxel::CellChangeListener
, public Voxel2::GridListener
{
private:
typedef DescribedCreatable<ChangeHistoryService, Instance, sChangeHistoryService, Reflection::ClassDescriptor::INTERNAL> Super;
DataModel* dataModel;
class Item;
class Waypoint;
shared_ptr<MegaClusterInstance> megaClusterInstance;
Waypoint* recording;
typedef std::list<Waypoint*> Waypoints; // must be a list and not a vector because we maintain iterators after reallocs
Waypoints waypoints;
Waypoints::iterator playWaypoint; // playing nextWaypoint is a "redo"
Waypoints::iterator unplayWaypoint; // unplaying unplayWaypoint is an "undo"
Waypoints::iterator runStartWaypoint; // unplaying up to runStartWaypoint is a "reset"
bool playing;
bool enabled;
int dataSize;
shared_ptr<RunService> runService;
shared_ptr<Instance> statsItem;
Aya::signals::scoped_connection itemAddedConnection;
Aya::signals::scoped_connection itemRemovedConnection;
Aya::signals::scoped_connection itemChangedConnection;
Aya::signals::scoped_connection runTransitionConnection;
public:
static const int minWaypoints = 3;
static const int maxWaypoints = 250;
static const int maxMemoryUsage = 250 * 1024 * 1024;
typedef enum
{
Aggregate, // Don't allow waypoints while running. All changes are recorded and made part of the reset waypoint
Snapshot, // When recording waypoints while running, take a snapshot of positions and velocities
Hybrid // When recording waypoints while running, ignore unrelated position and velocity changes
} RuntimeUndoBehavior;
static RuntimeUndoBehavior runtimeUndoBehavior;
ChangeHistoryService();
~ChangeHistoryService();
static void requestWaypoint(const char* name, const Instance* context);
void resetBaseWaypoint();
void clearWaypoints();
void setEnabled(bool state);
bool isEnabled() const
{
return enabled;
}
void requestWaypoint(const char* name);
void requestWaypoint2(std::string name)
{
requestWaypoint(name.c_str());
}
// TODO: rename play-->redo unplay-->undo???
bool getPlayWaypoint(std::string& name, int steps = 0) const;
bool getUnplayWaypoint(std::string& name, int steps = 0) const;
bool canPlay() const
{
std::string name;
return getPlayWaypoint(name);
}
bool canUnplay() const
{
std::string name;
return getUnplayWaypoint(name);
}
// returns a tuple: bool state, [string name]
shared_ptr<const Reflection::Tuple> canUnplay2();
shared_ptr<const Reflection::Tuple> canPlay2();
// std::string getPlayName() const { std::string name; getPlayWaypoint(name); return name; }
// std::string getUnplayName() const { std::string name; getUnplayWaypoint(name); return name; }
void play();
void playLua();
void unplay();
void unplayLua();
// to show progress dialog in studio
boost::function<void(boost::function<void()>, std::string)> withLongRunningOperation;
bool isResetEnabled() const;
void reset();
int getWaypointDataSize() const
{
return dataSize;
}
int getWaypointCount() const
{
return waypoints.size();
}
Aya::signal<void()> waypointChangedSignal;
Aya::signal<void(std::string)> undoSignal;
Aya::signal<void(std::string)> redoSignal;
MegaClusterInstance* getTerrain() const
{
return megaClusterInstance.get();
}
protected:
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
private:
void attach();
void dettach();
void trimWaypoints();
void mergeFirstTwoWaypoints();
void computeDataSize();
virtual void onItemAdded(shared_ptr<Aya::Instance> item);
virtual void onItemRemoved(shared_ptr<Aya::Instance> item);
virtual void onItemChanged(shared_ptr<Aya::Instance> item, const Reflection::PropertyDescriptor* descriptor);
bool isRecordable(Instance* instance);
/*override*/ virtual void terrainCellChanged(const Voxel::CellChangeInfo& cell);
/*override*/ virtual void onTerrainRegionChanged(const Voxel2::Region& region);
void setCell(const SpatialRegion::Id& chunkPos, const Vector3int16& cellInChunk, Voxel::Cell detail, Voxel::CellMaterial material);
typedef enum
{
Accept,
Reject
} CheckResult;
CheckResult checkSettingWaypoint();
void onRunTransition(RunTransition event);
void setWaypoint(const char* name);
void setRunWaypoint();
void reportMissedPhysicsChanges(shared_ptr<Aya::Instance> instance);
};
} // namespace Aya

View File

@@ -0,0 +1,450 @@
#include "DataModel/CharacterAppearance.hpp"
#include "DataModel/Decal.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/ModelInstance.hpp"
#include "Humanoid/Humanoid.hpp"
#include "Players.hpp"
DYNAMIC_FASTFLAG(UseR15Character)
namespace Aya
{
const char* const sCharacterAppearance = "CharacterAppearance";
const char* const sShirt = "Shirt";
const char* const sPants = "Pants";
const char* const sShirtGraphic = "ShirtGraphic";
const char* const sClothing = "Clothing";
Reflection::BoundProp<TextureId> ShirtGraphic::prop_Graphic("Graphic", category_Appearance, &ShirtGraphic::graphic, &ShirtGraphic::dataChanged);
Reflection::BoundProp<TextureId> Clothing::prop_outfit1(
"Outfit1", category_Appearance, &Clothing::outfit1, &Clothing::dataChanged, Reflection::PropertyDescriptor::LEGACY);
Reflection::BoundProp<TextureId> Clothing::prop_outfit2(
"Outfit2", category_Appearance, &Clothing::outfit2, &Clothing::dataChanged, Reflection::PropertyDescriptor::LEGACY);
Reflection::PropDescriptor<Shirt, TextureId> Shirt::prop_ShirtTemplate(
"ShirtTemplate", category_Appearance, &Shirt::getTemplate, &Shirt::setTemplate);
Reflection::PropDescriptor<Pants, TextureId> Pants::prop_PantsTemplate(
"PantsTemplate", category_Appearance, &Pants::getTemplate, &Pants::setTemplate);
ShirtGraphic::ShirtGraphic()
{
setName("Shirt Graphic");
}
Clothing::Clothing()
{
setName("Clothing");
}
Shirt::Shirt()
{
// Don't remove this! Gcc needs it so that Creatable works
}
Pants::Pants()
{
// Don't remove this! Gcc needs it so that Creatable works
}
void ShirtGraphic::applyByMyself(Humanoid* humanoid)
{
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
if (Decal* decal = torso->findFirstChildOfType<Decal>())
decal->setTexture(graphic);
}
void Clothing::applyByMyself(Humanoid* humanoid)
{
if (humanoid->getUseR15())
{
Instance* pParent = getParent();
if (pParent)
{
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LowerTorso")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("UpperTorso")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("Head")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperArm")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerArm")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightHand")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperArm")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerArm")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftHand")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperLeg")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerLeg")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightFoot")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperLeg")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerLeg")))
answer->fireOutfitChanged();
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftFoot")))
answer->fireOutfitChanged();
}
}
else
{
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
{
leftLeg->fireOutfitChanged();
}
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
{
rightLeg->fireOutfitChanged();
}
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
{
torso->fireOutfitChanged();
}
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
{
leftSleeve->fireOutfitChanged();
}
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
{
rightSleeve->fireOutfitChanged();
}
}
}
void Shirt::setTemplate(TextureId value)
{
if (value != Clothing::outfit2)
{
prop_outfit2.setValue(this, value);
raisePropertyChanged(prop_ShirtTemplate);
}
}
void Pants::setTemplate(TextureId value)
{
if (value != Clothing::outfit1)
{
prop_outfit1.setValue(this, value);
raisePropertyChanged(prop_PantsTemplate);
}
}
const char* const sSkin = "Skin";
Reflection::BoundProp<BrickColor> Skin::prop_skinColor("SkinColor", category_Appearance, &Skin::skinColor, &Skin::dataChanged);
Skin::Skin()
: skinColor(BrickColor::brick_226)
{
setName("Skin");
}
void Skin::applyByMyself(Humanoid* humanoid)
{
if (PartInstance* head = humanoid->getHeadSlow())
head->setColor(skinColor);
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
leftLeg->setColor(skinColor);
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
rightLeg->setColor(skinColor);
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
torso->setColor(skinColor);
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
leftSleeve->setColor(skinColor);
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
rightSleeve->setColor(skinColor);
}
const char* const sBodyColors = "BodyColors";
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_HeadColor(
"HeadColor", category_Appearance, &BodyColors::getHeadColor, &BodyColors::setHeadColor);
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_LeftArmColor(
"LeftArmColor", category_Appearance, &BodyColors::getLeftArmColor, &BodyColors::setLeftArmColor);
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_RightArmColor(
"RightArmColor", category_Appearance, &BodyColors::getRightArmColor, &BodyColors::setRightArmColor);
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_TorsoColor(
"TorsoColor", category_Appearance, &BodyColors::getTorsoColor, &BodyColors::setTorsoColor);
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_LeftLegColor(
"LeftLegColor", category_Appearance, &BodyColors::getLeftLegColor, &BodyColors::setLeftLegColor);
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_RightLegColor(
"RightLegColor", category_Appearance, &BodyColors::getRightLegColor, &BodyColors::setRightLegColor);
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_HeadColor3(
"HeadColor3", category_Appearance, &BodyColors::getHeadColor3, &BodyColors::setHeadColor3);
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_LeftArmColor3(
"LeftArmColor3", category_Appearance, &BodyColors::getLeftArmColor3, &BodyColors::setLeftArmColor3);
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_RightArmColor3(
"RightArmColor3", category_Appearance, &BodyColors::getRightArmColor3, &BodyColors::setRightArmColor3);
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_TorsoColor3(
"TorsoColor3", category_Appearance, &BodyColors::getTorsoColor3, &BodyColors::setTorsoColor3);
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_LeftLegColor3(
"LeftLegColor3", category_Appearance, &BodyColors::getLeftLegColor3, &BodyColors::setLeftLegColor3);
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_RightLegColor3(
"RightLegColor3", category_Appearance, &BodyColors::getRightLegColor3, &BodyColors::setRightLegColor3);
BodyColors::BodyColors()
: headColor(BrickColor::brick_226)
, leftArmColor(BrickColor::brick_226)
, rightArmColor(BrickColor::brick_226)
, torsoColor(BrickColor::brick_28)
, leftLegColor(BrickColor::brick_23)
, rightLegColor(BrickColor::brick_23)
, headColor3(BrickColor(BrickColor::brick_226).color3uint8())
, leftArmColor3(BrickColor(BrickColor::brick_226).color3uint8())
, rightArmColor3(BrickColor(BrickColor::brick_226).color3uint8())
, torsoColor3(BrickColor(BrickColor::brick_28).color3uint8())
, leftLegColor3(BrickColor(BrickColor::brick_23).color3uint8())
, rightLegColor3(BrickColor(BrickColor::brick_23).color3uint8())
{
setName("Body Colors");
}
void BodyColors::setHeadColor(BrickColor value)
{
if (value != headColor)
{
headColor = value;
headColor3 = value.color3uint8();
raisePropertyChanged(prop_HeadColor);
raisePropertyChanged(prop_HeadColor3);
}
}
void BodyColors::setLeftArmColor(BrickColor value)
{
if (value != leftArmColor)
{
leftArmColor = value;
leftArmColor3 = value.color3uint8();
raisePropertyChanged(prop_LeftArmColor);
raisePropertyChanged(prop_LeftArmColor3);
}
}
void BodyColors::setRightArmColor(BrickColor value)
{
if (value != rightArmColor)
{
rightArmColor = value;
rightArmColor3 = value.color3uint8();
raisePropertyChanged(prop_RightArmColor);
raisePropertyChanged(prop_RightArmColor3);
}
}
void BodyColors::setTorsoColor(BrickColor value)
{
if (value != torsoColor)
{
torsoColor = value;
torsoColor3 = value.color3uint8();
raisePropertyChanged(prop_TorsoColor);
raisePropertyChanged(prop_TorsoColor3);
}
}
void BodyColors::setLeftLegColor(BrickColor value)
{
if (value != leftLegColor)
{
leftLegColor = value;
leftLegColor3 = value.color3uint8();
raisePropertyChanged(prop_LeftLegColor);
raisePropertyChanged(prop_LeftLegColor3);
}
}
void BodyColors::setRightLegColor(BrickColor value)
{
if (value != rightLegColor)
{
rightLegColor = value;
rightLegColor3 = value.color3uint8();
raisePropertyChanged(prop_RightLegColor);
raisePropertyChanged(prop_RightLegColor3);
}
}
void BodyColors::setHeadColor3(Color3 value)
{
if (value != Color3(headColor3))
{
headColor3 = value;
headColor = BrickColor::closest(value);
raisePropertyChanged(prop_HeadColor3);
raisePropertyChanged(prop_HeadColor);
}
}
void BodyColors::setLeftArmColor3(Color3 value)
{
if (value != Color3(leftArmColor3))
{
leftArmColor3 = value;
leftArmColor = BrickColor::closest(value);
raisePropertyChanged(prop_LeftArmColor3);
raisePropertyChanged(prop_LeftArmColor);
}
}
void BodyColors::setRightArmColor3(Color3 value)
{
if (value != Color3(rightArmColor3))
{
rightArmColor3 = value;
rightArmColor = BrickColor::closest(value);
raisePropertyChanged(prop_RightArmColor3);
raisePropertyChanged(prop_RightArmColor);
}
}
void BodyColors::setTorsoColor3(Color3 value)
{
if (value != Color3(torsoColor3))
{
torsoColor3 = value;
torsoColor = BrickColor::closest(value);
raisePropertyChanged(prop_TorsoColor3);
raisePropertyChanged(prop_TorsoColor);
}
}
void BodyColors::setLeftLegColor3(Color3 value)
{
if (value != Color3(leftLegColor3))
{
leftLegColor3 = value;
leftLegColor = BrickColor::closest(value);
raisePropertyChanged(prop_LeftLegColor3);
raisePropertyChanged(prop_LeftLegColor);
}
}
void BodyColors::setRightLegColor3(Color3 value)
{
if (value != Color3(rightLegColor3))
{
rightLegColor3 = value;
rightLegColor = BrickColor::closest(value);
raisePropertyChanged(prop_RightLegColor3);
raisePropertyChanged(prop_RightLegColor);
}
}
void BodyColors::applyByMyself(Humanoid* humanoid)
{
bool hasSkin = ModelInstance::findFirstModifierOfType<Skin>(getParent()) != 0;
if (hasSkin)
return;
if (humanoid->getUseR15())
{
Instance* pParent = getParent();
if (pParent)
{
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LowerTorso")))
answer->setColor3(torsoColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("UpperTorso")))
answer->setColor3(torsoColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("Head")))
answer->setColor3(headColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperArm")))
answer->setColor3(rightArmColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerArm")))
answer->setColor3(rightArmColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightHand")))
answer->setColor3(rightArmColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperArm")))
answer->setColor3(leftArmColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerArm")))
answer->setColor3(leftArmColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftHand")))
answer->setColor3(leftArmColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperLeg")))
answer->setColor3(rightLegColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerLeg")))
answer->setColor3(rightLegColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightFoot")))
answer->setColor3(rightLegColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperLeg")))
answer->setColor3(leftLegColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerLeg")))
answer->setColor3(leftLegColor3);
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftFoot")))
answer->setColor3(leftLegColor3);
}
}
else
{
if (PartInstance* head = humanoid->getHeadSlow())
head->setColor3(headColor3);
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
leftLeg->setColor3(leftLegColor3);
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
rightLeg->setColor3(rightLegColor3);
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
torso->setColor3(torsoColor3);
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
leftSleeve->setColor3(leftArmColor3);
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
rightSleeve->setColor3(rightArmColor3);
}
}
void LegacyCharacterAppearance::apply()
{
if (Network::Players::backendProcessing(this, false))
Super::apply();
}
void CharacterAppearance::apply()
{
if (Humanoid* humanoid = Humanoid::modelIsCharacter(getParent()))
applyByMyself(humanoid);
}
void CharacterAppearance::onAncestorChanged(const AncestorChanged& event)
{
Super::onAncestorChanged(event);
// Search for a Humanoid sibling:
if (event.child == this)
{
if (event.newParent)
{
Aya::Humanoid* humanoid = Humanoid::modelIsCharacter(event.newParent);
if (humanoid)
{
applyByMyself(humanoid);
}
}
if (event.oldParent)
{
Aya::Humanoid* humanoid = Humanoid::modelIsCharacter(event.oldParent);
if (humanoid)
{
applyByMyself(humanoid);
}
}
}
}
bool CharacterAppearance::askSetParent(const Instance* instance) const
{
return Instance::fastDynamicCast<ModelInstance>(instance) != NULL;
}
} // namespace Aya

View File

@@ -0,0 +1,242 @@
#pragma once
#include "DataModel/GlobalSettings.hpp"
#include "Utility/BrickColor.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/IModelModifier.hpp"
#include "Utility/TextureId.hpp"
namespace Aya
{
class Humanoid;
extern const char* const sCharacterAppearance;
class AyaBaseClass CharacterAppearance
: public DescribedNonCreatable<CharacterAppearance, Instance, sCharacterAppearance>
, public IModelModifier
{
private:
typedef Instance Super;
public:
virtual void apply();
protected:
virtual void onAncestorChanged(const AncestorChanged& event);
virtual bool askSetParent(const Instance* instance) const;
private:
virtual void applyByMyself(Humanoid* humanoid) = 0;
};
class LegacyCharacterAppearance : public CharacterAppearance
{
private:
typedef CharacterAppearance Super;
public:
// Hack: This apply() function is ugly, but necessary because
// this class changes properties of other objects (like
// Part colors and Decal images). However, to avoid
// crosstalk the apply function should only do something
// in the backend case.
/*override*/ void apply();
};
// Old-style T-shirts
extern const char* const sShirtGraphic;
class ShirtGraphic : public DescribedCreatable<ShirtGraphic, LegacyCharacterAppearance, sShirtGraphic>
{
public:
TextureId graphic;
static Reflection::BoundProp<TextureId> prop_Graphic;
ShirtGraphic();
protected:
/*override*/ void applyByMyself(Humanoid* humanoid);
private:
void dataChanged(const Reflection::PropertyDescriptor&)
{
CharacterAppearance::apply();
}
};
extern const char* const sClothing;
class Clothing : public DescribedNonCreatable<Clothing, CharacterAppearance, sClothing>
{
friend class CharacterAppearance;
public:
TextureId outfit1;
TextureId outfit2;
static Reflection::BoundProp<TextureId> prop_outfit1;
static Reflection::BoundProp<TextureId> prop_outfit2;
Clothing();
virtual TextureId getTemplate() const
{
AYAASSERT(false);
return NULL;
}
protected:
/*override*/ void applyByMyself(Humanoid* humanoid);
void dataChanged(const Reflection::PropertyDescriptor&)
{
CharacterAppearance::apply();
}
};
extern const char* const sPants;
class Pants : public DescribedCreatable<Pants, Clothing, sPants>
{
public:
Pants();
static Reflection::PropDescriptor<Pants, TextureId> prop_PantsTemplate;
TextureId getTemplate() const
{
return outfit1;
}
void setTemplate(TextureId value);
};
extern const char* const sShirt;
class Shirt : public DescribedCreatable<Shirt, Clothing, sShirt>
{
public:
Shirt();
static Reflection::PropDescriptor<Shirt, TextureId> prop_ShirtTemplate;
TextureId getTemplate() const
{
return outfit2;
}
void setTemplate(TextureId value);
};
extern const char* const sBodyColors;
class BodyColors : public DescribedCreatable<BodyColors, LegacyCharacterAppearance, sBodyColors>
{
BrickColor headColor;
Color3uint8 headColor3;
BrickColor leftArmColor;
Color3uint8 leftArmColor3;
BrickColor rightArmColor;
Color3uint8 rightArmColor3;
BrickColor torsoColor;
Color3uint8 torsoColor3;
BrickColor leftLegColor;
Color3uint8 leftLegColor3;
BrickColor rightLegColor;
Color3uint8 rightLegColor3;
BrickColor getHeadColor() const
{
return headColor;
}
BrickColor getLeftArmColor() const
{
return leftArmColor;
}
BrickColor getRightArmColor() const
{
return rightArmColor;
}
BrickColor getTorsoColor() const
{
return torsoColor;
}
BrickColor getLeftLegColor() const
{
return leftLegColor;
}
BrickColor getRightLegColor() const
{
return rightLegColor;
}
Color3 getHeadColor3() const
{
return headColor3;
}
Color3 getLeftArmColor3() const
{
return leftArmColor3;
}
Color3 getRightArmColor3() const
{
return rightArmColor3;
}
Color3 getTorsoColor3() const
{
return torsoColor3;
}
Color3 getLeftLegColor3() const
{
return leftLegColor3;
}
Color3 getRightLegColor3() const
{
return rightLegColor3;
}
void setHeadColor(BrickColor value);
void setLeftArmColor(BrickColor value);
void setRightArmColor(BrickColor value);
void setTorsoColor(BrickColor value);
void setLeftLegColor(BrickColor value);
void setRightLegColor(BrickColor value);
void setHeadColor3(Color3 value);
void setLeftArmColor3(Color3 value);
void setRightArmColor3(Color3 value);
void setTorsoColor3(Color3 value);
void setLeftLegColor3(Color3 value);
void setRightLegColor3(Color3 value);
public:
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_HeadColor;
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_LeftArmColor;
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_RightArmColor;
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_TorsoColor;
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_LeftLegColor;
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_RightLegColor;
static Reflection::PropDescriptor<BodyColors, Color3> prop_HeadColor3;
static Reflection::PropDescriptor<BodyColors, Color3> prop_LeftArmColor3;
static Reflection::PropDescriptor<BodyColors, Color3> prop_RightArmColor3;
static Reflection::PropDescriptor<BodyColors, Color3> prop_TorsoColor3;
static Reflection::PropDescriptor<BodyColors, Color3> prop_LeftLegColor3;
static Reflection::PropDescriptor<BodyColors, Color3> prop_RightLegColor3;
BodyColors();
private:
virtual void applyByMyself(Humanoid* humanoid);
void dataChanged(const Reflection::PropertyDescriptor&)
{
CharacterAppearance::apply();
}
};
extern const char* const sSkin;
class Skin : public DescribedCreatable<Skin, LegacyCharacterAppearance, sSkin>
{
BrickColor skinColor;
public:
static Reflection::BoundProp<BrickColor> prop_skinColor;
Skin();
private:
virtual void applyByMyself(Humanoid* humanoid);
void dataChanged(const Reflection::PropertyDescriptor&)
{
CharacterAppearance::apply();
}
};
} // namespace Aya

View File

@@ -0,0 +1,131 @@
#include "DataModel/CharacterMesh.hpp"
#include "Humanoid/Humanoid.hpp"
namespace Aya
{
const char* const sCharacterMesh = "CharacterMesh";
static const Reflection::EnumPropDescriptor<CharacterMesh, CharacterMesh::BodyPart> prop_bodyPart(
"BodyPart", category_Data, &CharacterMesh::getBodyPart, &CharacterMesh::setBodyPart);
Reflection::BoundProp<int64_t> CharacterMesh::prop_baseTextureAssetId("BaseTextureId", "Data", &CharacterMesh::baseTextureAssetId);
Reflection::BoundProp<int64_t> CharacterMesh::prop_overlayTextureAssetId("OverlayTextureId", "Data", &CharacterMesh::overlayTextureAssetId);
Reflection::BoundProp<int64_t> CharacterMesh::prop_meshAssetId("MeshId", "Data", &CharacterMesh::meshAssetId);
REFLECTION_END();
namespace Reflection
{
template<>
EnumDesc<CharacterMesh::BodyPart>::EnumDesc()
: EnumDescriptor("BodyPart")
{
addPair(CharacterMesh::HEAD, "Head");
addPair(CharacterMesh::TORSO, "Torso");
addPair(CharacterMesh::LEFTARM, "LeftArm");
addPair(CharacterMesh::RIGHTARM, "RightArm");
addPair(CharacterMesh::LEFTLEG, "LeftLeg");
addPair(CharacterMesh::RIGHTLEG, "RightLeg");
}
template<>
CharacterMesh::BodyPart& Variant::convert<CharacterMesh::BodyPart>(void)
{
return genericConvert<CharacterMesh::BodyPart>();
}
} // namespace Reflection
template<>
bool StringConverter<CharacterMesh::BodyPart>::convertToValue(const std::string& text, CharacterMesh::BodyPart& value)
{
return Reflection::EnumDesc<CharacterMesh::BodyPart>::singleton().convertToValue(text.c_str(), value);
}
CharacterMesh::CharacterMesh()
: DescribedCreatable<CharacterMesh, CharacterAppearance, sCharacterMesh>()
, bodyPart(HEAD)
, baseTextureAssetId(0)
, overlayTextureAssetId(0)
, meshAssetId(0)
{
setName(sCharacterMesh);
}
void CharacterMesh::onPropertyChanged(const Reflection::PropertyDescriptor& descriptor)
{
CharacterAppearance::apply();
}
void CharacterMesh::setBodyPart(BodyPart value)
{
if (bodyPart != value)
{
bodyPart = value;
raisePropertyChanged(prop_bodyPart);
}
}
void CharacterMesh::applyByMyself(Humanoid* humanoid)
{
// re-using fireOutfitChanged. consider: own event.
// right now, both end up doing the exact same thing, so reuse for now.
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
{
leftLeg->fireOutfitChanged();
}
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
{
rightLeg->fireOutfitChanged();
}
if (PartInstance* torso = humanoid->getTorsoSlow())
{
torso->fireOutfitChanged();
}
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
{
leftSleeve->fireOutfitChanged();
}
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
{
rightSleeve->fireOutfitChanged();
}
}
TextureId CharacterMesh::getBaseTextureId() const
{
if (baseTextureAssetId != 0)
{
return TextureId(Aya::format("ayaassetid://%d", baseTextureAssetId));
}
else
{
return TextureId::nullTexture();
}
}
TextureId CharacterMesh::getOverlayTextureId() const
{
if (overlayTextureAssetId != 0)
{
return TextureId(Aya::format("ayaassetid://%d", overlayTextureAssetId));
}
else
{
return TextureId::nullTexture();
}
}
MeshId CharacterMesh::getMeshId() const
{
if (meshAssetId != 0)
{
return MeshId(Aya::format("ayaassetid://%d", meshAssetId));
}
else
{
return MeshId();
}
}
} // namespace Aya

View File

@@ -0,0 +1,57 @@
#pragma once
#include "Utility/TextureId.hpp"
#include "Utility/MeshId.hpp"
#include "CharacterAppearance.hpp"
namespace Aya
{
class Humanoid;
extern const char* const sCharacterMesh;
class CharacterMesh : public DescribedCreatable<CharacterMesh, CharacterAppearance, sCharacterMesh>
{
public:
enum BodyPart
{
HEAD = 0,
TORSO = 1,
LEFTARM = 2,
RIGHTARM = 3,
LEFTLEG = 4,
RIGHTLEG = 5
};
CharacterMesh();
BodyPart getBodyPart() const
{
return bodyPart;
}
void setBodyPart(BodyPart value);
int64_t baseTextureAssetId;
int64_t overlayTextureAssetId;
int64_t meshAssetId;
static Reflection::BoundProp<int64_t> prop_baseTextureAssetId;
static Reflection::BoundProp<int64_t> prop_overlayTextureAssetId;
static Reflection::BoundProp<int64_t> prop_meshAssetId;
TextureId getBaseTextureId() const;
TextureId getOverlayTextureId() const;
MeshId getMeshId() const;
protected:
virtual void applyByMyself(Humanoid* humanoid);
/*override*/ void onPropertyChanged(const Reflection::PropertyDescriptor& descriptor);
private:
BodyPart bodyPart;
};
} // namespace Aya

View File

@@ -0,0 +1,166 @@
#include "DataModel/ChatService.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/Workspace.hpp"
#include "DataModel/HttpRbxApiService.hpp"
#include "Xml/WebParser.hpp"
#include "WebChatFilter.hpp"
#include "Players.hpp"
namespace Aya
{
const char* const sChatService = "Chat";
static Reflection::BoundFuncDesc<ChatService, void(shared_ptr<Instance>, std::string, ChatService::ChatColor)> func_chat(
&ChatService::chat, "Chat", "partOrCharacter", "message", "color", ChatService::CHAT_BLUE, Security::None);
static Reflection::BoundYieldFuncDesc<ChatService, std::string(std::string, shared_ptr<Instance>)> func_filterString(
&ChatService::filterStringForPlayer, "FilterStringForPlayerAsync", "stringToFilter", "playerToFilterFor", Security::None);
static Reflection::RemoteEventDesc<ChatService, void(shared_ptr<Instance>, std::string, ChatService::ChatColor)> event_Chatted(
&ChatService::chattedSignal, "Chatted", "part", "message", "color", Security::None, Reflection::RemoteEventCommon::SCRIPTING,
Reflection::RemoteEventCommon::BROADCAST);
REFLECTION_END();
namespace Reflection
{
template<>
EnumDesc<ChatService::ChatColor>::EnumDesc()
: EnumDescriptor("ChatColor")
{
addPair(ChatService::CHAT_BLUE, "Blue");
addPair(ChatService::CHAT_GREEN, "Green");
addPair(ChatService::CHAT_RED, "Red");
}
template<>
ChatService::ChatColor& Variant::convert<ChatService::ChatColor>(void)
{
return genericConvert<ChatService::ChatColor>();
}
} // namespace Reflection
template<>
bool Aya::StringConverter<ChatService::ChatColor>::convertToValue(const std::string& text, ChatService::ChatColor& value)
{
return Reflection::EnumDesc<ChatService::ChatColor>::singleton().convertToValue(text.c_str(), value);
}
ChatService::ChatService()
{
setName(sChatService);
}
void ChatService::chat(shared_ptr<Instance> instance, std::string message, ChatService::ChatColor chatColor)
{
if (!instance)
throw std::runtime_error("partOrCharacter must be non-nil");
if (!Workspace::contextInWorkspace(instance.get()))
throw std::runtime_error("partOrCharacter must be in the Workspace");
if (instance->fastDynamicCast<ModelInstance>())
{
Network::Players* players = ServiceProvider::create<Network::Players>(this);
if (!players->playerFromCharacter(instance))
throw std::runtime_error("partOrCharacter is not a legal character");
}
else if (!instance->fastDynamicCast<PartInstance>())
{
throw std::runtime_error("partOrCharacter must be a Part or a Character");
}
if (message.empty())
throw std::runtime_error("message must be non-empty");
event_Chatted.fireAndReplicateEvent(this, instance, message, chatColor);
}
void ChatService::gotFilteredStringSuccess(std::string response, Network::Player* player, boost::function<void(std::string)> resumeFunction,
boost::function<void(std::string)> errorFunction)
{
if (!response.empty())
{
Reflection::Variant jsonVariant;
WebParser::parseJSONObject(response, jsonVariant);
if (jsonVariant.isType<shared_ptr<const Reflection::ValueTable>>())
{
shared_ptr<const Reflection::ValueTable> filteredStringTable = jsonVariant.cast<shared_ptr<const Reflection::ValueTable>>();
Reflection::Variant dataVariant = filteredStringTable->at("data");
if (dataVariant.isType<shared_ptr<const Reflection::ValueTable>>())
{
shared_ptr<const Reflection::ValueTable> dataTable = dataVariant.cast<shared_ptr<const Reflection::ValueTable>>();
Reflection::Variant whiteListPolicyFilterResult = dataTable->at(kWebChatWhiteListPolicy);
Reflection::Variant blackListPolicyFilterResult = dataTable->at(kWebChatBlackListPolicy);
if (player->getChatFilterType() == Network::Player::CHAT_FILTER_WHITELIST)
{
std::string whiteListString = whiteListPolicyFilterResult.get<std::string>();
resumeFunction(whiteListString);
return;
}
else
{
std::string blackListString = blackListPolicyFilterResult.get<std::string>();
resumeFunction(blackListString);
return;
}
}
}
errorFunction("ChatService:FilterString could not parse response");
}
else
{
errorFunction("ChatService:FilterString returned an empty response");
}
}
void ChatService::gotFilterStringError(std::string error, boost::function<void(std::string)> errorFunction)
{
errorFunction(error);
}
void ChatService::filterStringForPlayer(std::string stringToFilter, shared_ptr<Instance> playerToFilterFor,
boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
if (!Network::Players::serverIsPresent(this))
{
errorFunction("ChatService:FilterString called on a client, only works from server.");
return;
}
Network::Player* playerFilter = Instance::fastDynamicCast<Network::Player>(playerToFilterFor.get());
if (!playerFilter)
{
errorFunction("ChatService:FilterString called without a valid Player object.");
return;
}
std::stringstream params;
Aya::HttpAux::AdditionalHeaders headers;
{
params << "filters=" << kWebChatWhiteListPolicy;
params << "&filters=" << kWebChatBlackListPolicy;
params << "&text=" << Http::urlEncode(stringToFilter);
}
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
{
apiService->postAsync("moderation/filtertext", params.str(), true, Aya::PRIORITY_DEFAULT, Aya::HttpService::APPLICATION_URLENCODED,
boost::bind(&ChatService::gotFilteredStringSuccess, this, _1, playerFilter, resumeFunction, errorFunction),
boost::bind(&ChatService::gotFilterStringError, this, _1, errorFunction));
}
else
{
errorFunction("ChatService:FilterString could not find ApiService.");
return;
}
}
} // namespace Aya

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Tree/Service.hpp"
#include "Tree/Instance.hpp"
#include "Player.hpp"
namespace Aya
{
class PartInstance;
extern const char* const sChatService;
class ChatService
: public DescribedCreatable<ChatService, Instance, sChatService, Reflection::ClassDescriptor::INTERNAL>
, public Service
{
private:
void gotFilteredStringSuccess(std::string response, Network::Player* player, boost::function<void(std::string)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void gotFilterStringError(std::string error, boost::function<void(std::string)> errorFunction);
public:
enum ChatColor
{
CHAT_BLUE,
CHAT_GREEN,
CHAT_RED
};
ChatService();
void chat(shared_ptr<Instance> instance, std::string message, ChatService::ChatColor chatColor);
void filterStringForPlayer(std::string stringToFilter, shared_ptr<Instance> playerToFilterFor, boost::function<void(std::string)> resumeFunction,
boost::function<void(std::string)> errorFunction);
Aya::remote_signal<void(shared_ptr<Instance>, std::string, ChatService::ChatColor)> chattedSignal;
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,337 @@
#pragma once
#include <deque>
#include <vector>
#include <mutex>
#include "DataModel/GuiObject.hpp"
#include "DataModel/GuiMixin.hpp"
#include "Utility/Sound.hpp"
#include "Utility/SoundChannel.hpp"
#include "fmod_common.h"
#ifdef ENABLE_CHROMIUM_FRAMES
#include <include/cef_app.h>
#include <include/cef_base.h>
#endif
namespace Aya
{
class PartInstance;
enum ChromiumConsoleMessageSeverity
{
Default,
Verbose,
Info,
Warning,
Error,
Fatal
};
enum ChromiumLoadState
{
Stopped,
Loaded,
Loading,
LoadError
};
enum ChromiumMouseCursor
{
Pointer,
Cross,
Hand,
IBeam,
Wait,
Help,
EastResize,
NorthResize,
NortheastResize,
NorthwestResize,
SouthResize,
SoutheastResize,
SouthwestResize,
WestResize,
NorthSouthResize,
EastWestResize,
NortheastSouthwestResize,
NorthwestSoutheastResize,
ColumnResize,
RowResize,
MiddlePanning,
EastPanning,
NorthPanning,
NortheastPanning,
NorthwestPanning,
SouthPanning,
SoutheastPanning,
SouthwestPanning,
WestPanning,
Move,
VerticalText,
Cell,
ContextMenu,
Alias,
Progress,
NoDrop,
Copy,
None,
NotAllowed,
ZoomIn,
ZoomOut,
Grab,
Grabbing,
MiddlePanningVertical,
MiddlePanningHorizontal,
Custom,
DndNone,
DndMove,
DndCopy,
DndLink
};
// fwd decl
class AyaCefRenderer;
class AyaCefClient;
extern const char* const sChromiumFrame;
class ChromiumFrame
: public DescribedCreatable<ChromiumFrame, GuiLabel, sChromiumFrame>
, public GuiImageMixin
{
typedef DescribedCreatable<ChromiumFrame, GuiLabel, sChromiumFrame> Super;
public:
ChromiumFrame();
~ChromiumFrame();
void initialize();
void uninitialize();
void mouseLeftClick(int x, int y, bool isDown = true);
void mouseMiddleClick(int x, int y, bool isDown = true);
void mouseRightClick(int x, int y, bool isDown = true);
void mouseWheel(int x, int y, int deltaX, int deltaY);
void mouseMove(int x, int y);
void keyEvent(int keyCode, int modifiers, bool isKeyDown = true);
std::string runJS(std::string code);
void refresh();
void refreshNoCache();
void backward();
void forward();
void cancel();
void focus();
void unfocus();
Color3 getColorAtPixel(int x, int y);
bool getIsLoaded() const
{
return (loadState == ChromiumLoadState::Loaded);
}
bool getCanPageBackward() const
{
return canPageBackward;
}
void setCanPageBackward(bool value);
bool getCanPageForward() const
{
return canPageForward;
}
void setCanPageForward(bool value);
bool getInitialized() const
{
return initialized;
}
void setInitializedInternal(bool value);
std::string getPageTitle() const
{
return pageTitle;
}
void setPageTitleInternal(std::string value);
int getStatusCode() const
{
return statusCode;
}
void setStatusCodeInternal(int value);
ChromiumLoadState getLoadState() const
{
return loadState;
}
void setLoadStateInternal(ChromiumLoadState value);
Vector2 getResolution() const
{
return this->resolution;
}
void setResolution(Vector2 resolution);
Vector2 getMousePosition() const
{
return this->mousePosition;
}
void setMousePosition(Vector2 position);
std::string getURL() const
{
return this->url;
}
void setURL(std::string url);
void setURLInternal(std::string url); // called by AyaCefClient::OnLoadStart
ChromiumMouseCursor getMouseCursor() const
{
return this->mouseCursor;
}
void setMouseCursorInternal(ChromiumMouseCursor value);
Vector2 getScrollOffset() const
{
return this->scrollOffset;
}
void setScrollOffsetInternal(Vector2 value);
float getZoom() const
{
return this->zoom;
}
void setZoom(float value);
void setZoomInternal(float value);
float getVolume() const
{
return this->volume;
}
void setVolume(float value);
float getPitch() const
{
return this->pitch;
}
void setPitch(float value);
bool isPlaying() const
{
return this->playing;
}
void setIsPlayingInternal(bool value);
void setWorldAudioEnabled(bool value);
bool getWorldAudioEnabled() const
{
return this->worldAudioEnabled;
}
void updateWorldAudio();
float getWorldAudioMinDistance() const
{
return this->worldAudioMinDistance;
}
void setWorldAudioMinDistance(float value);
float getWorldAudioMaxDistance() const
{
return this->worldAudioMaxDistance;
}
void setWorldAudioMaxDistance(float value);
Soundscape::RollOffMode getWorldAudioRollOff() const
{
return this->worldAudioRollOff;
}
void setWorldAudioRollOff(Soundscape::RollOffMode value);
typedef std::pair<int, float*> ChromiumFrameAudioData;
std::deque<ChromiumFrameAudioData> audioData;
std::mutex audioMutex;
void startSound(int channels, int sampleRate, int framesPerBuffer);
void pushSoundData(const float** data, int frames, int64_t pts);
ChromiumFrameAudioData popSoundData();
int pendingSoundData()
{
return audioData.size();
};
void stopSound();
// These are public so that AyaCefRenderer::OnPaint may modify them
bool shouldReuploadTexture = false;
std::vector<char> buffer;
Aya::remote_signal<void(bool)> isLoadedSignal;
Aya::remote_signal<void(ChromiumMouseCursor)> mouseCursorChangedSignal;
Aya::remote_signal<void(std::string, ChromiumConsoleMessageSeverity, std::string, int)> onConsoleMessageSignal;
Aya::remote_signal<void(Vector2)> scrollOffsetChangedSignal;
static Reflection::RemoteEventDesc<ChromiumFrame, void(std::string, ChromiumConsoleMessageSeverity, std::string, int)>
event_ChromiumFrameConsoleMessage;
#if defined(ENABLE_CHROMIUM_FRAMES) && !defined(AYA_SERVER)
// These are public so that the cefLoop may set them upon initialization
CefRefPtr<CefBrowser> cefInstance;
CefRefPtr<AyaCefRenderer> cefRenderer;
CefRefPtr<AyaCefClient> cefClient;
#endif
int lastPaintWidth;
int lastPaintHeight;
private:
#if defined(ENABLE_CHROMIUM_FRAMES) && !defined(AYA_SERVER)
void mouseClick(int x, int y, cef_mouse_button_type_t button, bool isDown = true);
#endif
static FMOD_RESULT F_CALLBACK AudioCallback(FMOD_SOUND* _sound, void* _data, unsigned int datalen);
std::string url;
std::string pageTitle;
bool initialized;
bool canPageBackward = false;
bool canPageForward = false;
bool worldAudioEnabled;
bool playing = false;
int statusCode;
float volume;
float pitch;
float worldAudioMinDistance;
float worldAudioMaxDistance;
Soundscape::RollOffMode worldAudioRollOff;
float zoom = 0.0f;
Vector2 resolution;
Vector2 mousePosition;
Vector2 scrollOffset;
FMOD::Sound* fmod_sound;
FMOD::Channel* fmod_channel; // the latest channel
PartInstance* part; // The Part (if any) that this sound is attached to
ChromiumLoadState loadState;
ChromiumMouseCursor mouseCursor;
shared_ptr<Graphics::Texture> texture;
protected:
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
/*override*/ void onAncestorChanged(const AncestorChanged& event);
//
// IAdornable
//
/*override*/ void render2d(Adorn* adorn);
/*override*/ void renderBackground2d(Adorn* adorn);
};
} // namespace Aya

View File

@@ -0,0 +1,215 @@
#include "DataModel/ClickDetector.hpp"
#include "DataModel/Workspace.hpp"
#include "DataModel/PartInstance.hpp"
#include "Players.hpp"
#include "Draw.hpp"
namespace Aya
{
const char* const sClickDetector = "ClickDetector";
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_MouseClick(&ClickDetector::mouseClickSignal, "MouseClick",
"playerWhoClicked", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> dep_MouseClick(&ClickDetector::mouseClickSignal, "mouseClick",
"playerWhoClicked", Security::None,
Reflection::RemoteEventCommon::Attributes::deprecated(Reflection::RemoteEventCommon::SCRIPTING, &desc_MouseClick),
Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_RightMouseClick(&ClickDetector::rightMouseClickSignal,
"RightMouseClick", "playerWhoClicked", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_MouseHoverEnter(&ClickDetector::mouseHoverSignal,
"MouseHoverEnter", "playerWhoHovered", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_MouseHoverLeave(&ClickDetector::mouseHoverLeaveSignal,
"MouseHoverLeave", "playerWhoHovered", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
Reflection::BoundProp<float> ClickDetector::propMaxActivationDistance("MaxActivationDistance", category_Data, &ClickDetector::maxActivationDistance);
Reflection::BoundProp<TextureId> ClickDetector::propCursorIcon("CursorIcon", category_Image, &ClickDetector::cursorIcon);
REFLECTION_END();
ClickDetector::ClickDetector()
: cycle(0)
, maxActivationDistance(32.0)
{
setName("ClickDetector");
lastHoverPart = shared_ptr<PartInstance>();
}
bool containsClickDetector(Instance* instance)
{
for (size_t i = 0; i < instance->numChildren(); ++i)
{
Instance* child = instance->getChild(i);
if (Instance::fastDynamicCast<ClickDetector>(child))
{
return true;
}
}
return false;
}
bool ancestorContainsClickDetector(Instance* instance)
{
AYAASSERT(instance);
if (containsClickDetector(instance))
{
return true;
}
else
{
if (Instance* parent = instance->getParent())
{
if (!Instance::fastDynamicCast<Workspace>(parent))
{ // bail out at workspace level to prevent going through all workspace children
return ancestorContainsClickDetector(parent);
}
}
return false;
}
}
void renderClickDetector(shared_ptr<Aya::Instance> descendant, Adorn* adorn, int cycle)
{
PartInstance* part = Instance::fastDynamicCast<PartInstance>(descendant.get());
if (part)
{
// Vector3 size = 0.5f * (part->getPartSizeXml() + 0.5 * Vector3::one()); // one stud bigger
// AABox fieldBox(-size, size);
// adorn->setObjectToWorldMatrix(part->getCoordinateFrame());
// adorn->box(fieldBox, Color4(0.0, 0.0, 1.0, 0.5) );
int max = ClickDetector::cycles();
int half = max / 2;
int value = cycle < half ? cycle : max - cycle;
float percent = static_cast<float>(value) / static_cast<float>(half);
Color4 color(1.0f - percent, percent, 0.5f);
Draw::selectionBox(part->getPart(), adorn, color);
}
}
void ClickDetector::render3dAdorn(Adorn* adorn)
{
/*
Instance* parent = this->getParent();
AYAASSERT(!dynamic_cast<Workspace*>(parent));
cycle = (cycle + 1) % cycles();
parent->visitDescendants(boost::bind(&renderClickDetector, _1, adorn, cycle));
renderClickDetector(shared_from(parent), adorn, cycle);
*/
}
void ClickDetector::fireMouseClick(float distance, Aya::Network::Player* player)
{
if (distance < maxActivationDistance)
desc_MouseClick.fireAndReplicateEvent(this, shared_from(player));
}
void ClickDetector::fireRightMouseClick(float distance, Aya::Network::Player* player)
{
if (distance < maxActivationDistance)
desc_RightMouseClick.fireAndReplicateEvent(this, shared_from(player));
}
bool ClickDetector::isClickable(shared_ptr<PartInstance> part, float distanceToCharacter, bool raiseClickedEvent, Aya::Network::Player* player,
TextureId** cursorIcon, bool isRightClick)
{
shared_ptr<Instance> clickDetectorParent = part;
while (clickDetectorParent && fastSharedDynamicCast<Workspace>(clickDetectorParent) == NULL)
{
if (shared_ptr<ClickDetector> cd = shared_from(clickDetectorParent->findFirstChildOfType<ClickDetector>()))
{
**cursorIcon = cd->getCursorIcon();
if (distanceToCharacter < cd->getMaxActivationDistance())
{
if (raiseClickedEvent)
{
if (isRightClick)
cd->fireRightMouseClick(distanceToCharacter, player);
else
cd->fireMouseClick(distanceToCharacter, player);
}
return true;
}
}
clickDetectorParent = shared_from(clickDetectorParent->getParent());
}
return false;
}
bool ClickDetector::updateLastHoverPart(shared_ptr<Instance> newHover, Aya::Network::Player* player)
{
if (newHover != lastHoverPart)
{
if (newHover)
fireMouseHover(player);
lastHoverPart = newHover;
return true;
}
return false;
}
void ClickDetector::fireMouseHover(Aya::Network::Player* player)
{
desc_MouseHoverEnter.fireAndReplicateEvent(this, shared_from(player));
}
void ClickDetector::fireMouseHoverLeave(Aya::Network::Player* player)
{
desc_MouseHoverLeave.fireAndReplicateEvent(this, shared_from(player));
lastHoverPart = shared_ptr<PartInstance>();
}
void ClickDetector::stopHover(shared_ptr<PartInstance> part, Aya::Network::Player* player)
{
if (part && part.get())
{
if (Instance* instance = fastDynamicCast<Instance>(part.get()))
{
do
{
if (ClickDetector* cd = instance->findFirstChildOfType<ClickDetector>())
cd->fireMouseHoverLeave(player);
instance = instance->getParent();
} while (instance != NULL && Instance::fastDynamicCast<Workspace>(instance) == NULL);
}
}
}
bool ClickDetector::isHovered(PartInstance* part, float distanceToCharacter, bool raiseHoveredEvent, Aya::Network::Player* player)
{
if (part)
{
shared_ptr<Instance> i = shared_from(part);
do
{
if (shared_ptr<ClickDetector> cd = shared_from(i->findFirstChildOfType<ClickDetector>()))
{
if ((distanceToCharacter < cd->getMaxActivationDistance()) && cd)
{
return cd->updateLastHoverPart(i, player);
}
}
i = shared_from(i->getParent());
} while (i != NULL && Instance::fastDynamicCast<Workspace>(i.get()) == NULL);
}
return false;
}
} // namespace Aya

View File

@@ -0,0 +1,90 @@
#pragma once
#include "Tree/Instance.hpp"
#include "Base/IAdornable.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/ModelInstance.hpp"
#include "Utility/TextureId.hpp"
namespace Aya
{
namespace Network
{
class Players;
class Player;
} // namespace Network
class PartInstance;
class ModelInstance;
extern const char* const sClickDetector;
class ClickDetector
: public DescribedCreatable<ClickDetector, Instance, sClickDetector>
, public IAdornable
{
private:
int cycle;
float maxActivationDistance; // max distance a character can be from the button and still raise events
TextureId cursorIcon; // cursor icon that shows when the player hovers over this clickdetector
shared_ptr<Instance> lastHoverPart;
// IAdornable
/*override*/ bool shouldRender3dAdorn() const
{
return true;
}
/*override*/ void render3dAdorn(Adorn* adorn);
public:
ClickDetector();
virtual ~ClickDetector() {}
void fireMouseClick(float distance, Aya::Network::Player* player);
void fireRightMouseClick(float distance, Aya::Network::Player* player);
void fireMouseHover(Aya::Network::Player* player);
void fireMouseHoverLeave(Aya::Network::Player* player);
static Reflection::BoundProp<float> propMaxActivationDistance;
static Reflection::BoundProp<TextureId> propCursorIcon;
shared_ptr<Instance> getLastHoverPart()
{
return lastHoverPart;
}
bool updateLastHoverPart(shared_ptr<Instance> newHover, Aya::Network::Player* player);
Aya::remote_signal<void(shared_ptr<Instance>)> mouseClickSignal;
Aya::remote_signal<void(shared_ptr<Instance>)> rightMouseClickSignal;
Aya::remote_signal<void(shared_ptr<Instance>)> mouseHoverSignal;
Aya::remote_signal<void(shared_ptr<Instance>)> mouseHoverLeaveSignal;
static int cycles()
{
return 30;
}
float getMaxActivationDistance()
{
return maxActivationDistance;
}
TextureId getCursorIcon()
{
return cursorIcon;
}
static bool isClickable(shared_ptr<PartInstance> part, float distanceToCharacter, bool raiseClickedEvent, Aya::Network::Player* player,
TextureId** cursorIcon = NULL, bool isRightClick = false);
static bool isHovered(PartInstance* part, float distanceToCharacter, bool raiseHoveredEvent, Aya::Network::Player* player);
static void stopHover(shared_ptr<PartInstance> part, Aya::Network::Player* player);
/* override */ bool askSetParent(const Instance* parent) const
{
return (Instance::fastDynamicCast<PartInstance>(parent) != NULL) || (Instance::fastDynamicCast<ModelInstance>(parent) != NULL);
}
/* override */ bool askAddChild(const Instance* instance) const
{
return true;
}
};
} // namespace Aya

View File

@@ -0,0 +1,84 @@
#include "DataModel/CollectionService.hpp"
#include "DataModel/Configuration.hpp"
namespace Aya
{
const char* const sCollectionService = "CollectionService";
static Reflection::BoundFuncDesc<CollectionService, shared_ptr<const Instances>(std::string)> func_GetCollection(
&CollectionService::getCollection, "GetCollection", "class", Security::None);
Reflection::EventDesc<CollectionService, void(shared_ptr<Instance>)> event_collectionItemAdded(
&CollectionService::itemAddedSignal, "ItemAdded", "instance");
Reflection::EventDesc<CollectionService, void(shared_ptr<Instance>)> event_collectionItemRemoved(
&CollectionService::itemRemovedSignal, "ItemRemoved", "instance");
REFLECTION_END();
CollectionService::CollectionService()
: DescribedNonCreatable<CollectionService, Instance, sCollectionService>(sCollectionService)
{
}
shared_ptr<const Instances> CollectionService::getCollection(const Name& className)
{
return getCollection(className.toString());
}
shared_ptr<const Instances> CollectionService::getCollection(std::string className)
{
const CollectionMap::iterator iter = collections.find(className);
if (iter != collections.end())
{
return iter->second->read();
}
return shared_ptr<const Instances>();
}
void CollectionService::removeInstance(shared_ptr<Instance> instance)
{
const std::string& className = instance->getDescriptor().name.toString();
CollectionMap::iterator collectionIter = collections.find(className);
AYAASSERT(collectionIter != collections.end());
Instances::iterator iter;
shared_ptr<Instances> c(collectionIter->second->write());
iter = std::find(c->begin(), c->end(), instance);
if (iter != c->end())
{
// Fast-remove. This can make a huge speed improvement over regular remove
*iter = c->back();
c->pop_back();
itemRemovedSignal(instance);
}
else
{
AYAASSERT(0);
}
}
void CollectionService::addInstance(shared_ptr<Instance> instance)
{
const std::string& className = instance->getDescriptor().name.toString();
CollectionMap::iterator collectionIter = collections.find(className);
if (collectionIter == collections.end())
{
collections[className].reset(new copy_on_write_ptr<Instances>());
}
shared_ptr<Instances> c(collections[className]->write());
c->push_back(instance);
itemAddedSignal(instance);
}
} // namespace Aya

View File

@@ -0,0 +1,37 @@
#pragma once
#include "Tree/Service.hpp"
#include <queue>
namespace Aya
{
extern const char* const sCollectionService;
class CollectionService
: public DescribedNonCreatable<CollectionService, Instance, sCollectionService>
, public Service
{
public:
CollectionService();
Aya::signal<void(shared_ptr<Instance>)> itemAddedSignal;
Aya::signal<void(shared_ptr<Instance>)> itemRemovedSignal;
shared_ptr<const Instances> getCollection(std::string type);
shared_ptr<const Instances> getCollection(const Name& className);
template<class T>
shared_ptr<const Instances> getCollection()
{
return getCollection(T::classDescriptor());
}
void removeInstance(shared_ptr<Instance> instance);
void addInstance(shared_ptr<Instance> instance);
private:
// TODO: Lookup by const Aya::Name*
typedef std::map<std::string, shared_ptr<copy_on_write_ptr<Instances>>> CollectionMap;
CollectionMap collections;
};
} // namespace Aya

View File

@@ -0,0 +1,63 @@
#include "ColorCorrectionEffect.hpp"
namespace Aya
{
const char* const sColorCorrectionEffect = "ColorCorrectionEffect";
static Reflection::PropDescriptor<ColorCorrectionEffect, float> prop_brightness("Brightness", "State", &ColorCorrectionEffect::getBrightness, &ColorCorrectionEffect::setBrightness, Reflection::PropertyDescriptor::STANDARD);
static Reflection::PropDescriptor<ColorCorrectionEffect, float> prop_contrast("Contrast", "State", &ColorCorrectionEffect::getContrast, &ColorCorrectionEffect::setContrast, Reflection::PropertyDescriptor::STANDARD);
static Reflection::PropDescriptor<ColorCorrectionEffect, float> prop_saturation("Saturation", "State", &ColorCorrectionEffect::getSaturation, &ColorCorrectionEffect::setSaturation, Reflection::PropertyDescriptor::STANDARD);
static Reflection::PropDescriptor<ColorCorrectionEffect, Color3> prop_tintColor("TintColor", "State", &ColorCorrectionEffect::getTintColor, &ColorCorrectionEffect::setTintColor, Reflection::PropertyDescriptor::STANDARD);
ColorCorrectionEffect::ColorCorrectionEffect()
: m_brightness(0)
, m_contrast(0)
, m_saturation(0)
, m_tintColor(Color3::white())
{
setName(sColorCorrectionEffect);
}
ColorCorrectionEffect::~ColorCorrectionEffect()
{
//
}
void ColorCorrectionEffect::setBrightness(float brightness)
{
if (m_brightness == brightness)
return;
m_brightness = brightness;
raisePropertyChanged(prop_brightness);
}
void ColorCorrectionEffect::setContrast(float contrast)
{
if (m_contrast == contrast)
return;
m_contrast = contrast;
raisePropertyChanged(prop_contrast);
}
void ColorCorrectionEffect::setSaturation(float saturation)
{
if (m_saturation == saturation)
return;
m_saturation = -saturation; // NOTE: negative is intnetional, scenemanager treats this as grayscalelevel, too lazy to fix :P
raisePropertyChanged(prop_saturation);
}
void ColorCorrectionEffect::setTintColor(Color3 tint)
{
if (m_tintColor == tint)
return;
m_tintColor = tint;
raisePropertyChanged(prop_tintColor);
}
} // namespace Aya

View File

@@ -0,0 +1,32 @@
#pragma once
#include "PostEffect.hpp"
namespace Aya
{
extern const char* const sColorCorrectionEffect;
class ColorCorrectionEffect : public DescribedCreatable<ColorCorrectionEffect, PostEffect, sColorCorrectionEffect>
{
public:
ColorCorrectionEffect();
~ColorCorrectionEffect();
void setBrightness(float brightness);
void setContrast(float contrast);
void setSaturation(float saturation);
void setTintColor(Color3 tint);
float getBrightness() const { return m_brightness; }
float getContrast() const { return m_contrast; }
float getSaturation() const { return m_saturation; }
Color3 getTintColor() const { return m_tintColor; }
private:
float m_brightness = 0.0f;
float m_contrast = 0.0f;
float m_saturation = 0.0f;
Color3 m_tintColor = Color3::white();
};
} // namespace Aya

View File

@@ -0,0 +1,294 @@
#include "DataModel/ColorSequence.hpp"
#include "g3dmath.hpp"
#include "g3dmath.hpp"
#include "Debug.hpp"
#include "Utility/G3DCore.hpp"
using G3D::clamp;
using G3D::Color3;
using G3D::lerp;
namespace Aya
{
static inline Color3 clamp(Color3 v)
{
v.r = G3D::clamp(v.r, 0, 1);
v.g = G3D::clamp(v.g, 0, 1);
v.b = G3D::clamp(v.b, 0, 1);
return v;
}
ColorSequence::ColorSequence(Color3 constant)
: m_data(2)
{
m_data[0] = Key(0, constant, 0);
m_data[1] = Key(1, constant, 0);
}
ColorSequence::ColorSequence(Color3 a, Color3 b)
: m_data(2)
{
m_data[0] = Key(0, a, 0);
m_data[1] = Key(1, b, 0);
}
ColorSequence::ColorSequence(const std::vector<Key>& keys, bool exceptions)
{
if (!validate(keys, exceptions))
{
ColorSequence().m_data.swap(m_data);
return;
}
m_data = keys;
m_data.front().time = 0;
m_data.back().time = 1;
}
ColorSequence::ColorSequence(const ColorSequence& r)
{
AYAASSERT(validate(r.m_data, false));
m_data = r.m_data;
for (unsigned j = 0, e = m_data.size(); j < e; ++j)
{
m_data[j].time = G3D::clamp(m_data[j].time, 0, 1);
m_data[j].value = clamp(m_data[j].value);
m_data[j].envelope = 0;
}
}
void ColorSequence::resample(G3D::Vector3* min, G3D::Vector3* max, int numPoints) const
{
// invariant violation: this should not happen under any circumstances:
AYAASSERT(m_data.size() >= 2);
AYAASSERT(m_data.front().time == 0.0f); // yup, straight equal
AYAASSERT(m_data.back().time == 1.0f); // ditto
int src = 0;
float t = 0, dt = 1.0f / (numPoints - 1.0f) - 1e-5f;
const std::vector<Key>& data = m_data;
min[0] = max[0] = Vector3(data[0].value);
for (int j = 1; j < numPoints; ++j)
{
t += dt;
AYAASSERT(data[src].time <= data[src + 1].time); // invariant violation
while (m_data[src + 1].time < t)
src++; // find the next key
float s = (t - data[src].time) / (data[src + 1].time - data[src].time);
max[j] = min[j] = Vector3(lerp(data[src].value, data[src + 1].value, s));
}
}
bool ColorSequenceKeypoint::operator==(const ColorSequenceKeypoint& r) const
{
return 0 == memcmp(this, &r, sizeof(r));
}
bool ColorSequence::operator==(const ColorSequence& r) const
{
return m_data == r.m_data;
}
bool ColorSequence::validate(const std::vector<Key>& keys, bool exc)
{
if (keys.size() < 2)
{
if (exc)
throw Aya::runtime_error("ColorSequence: requires at least 2 keypoints");
else
return false;
}
if (keys.size() > kMaxSize)
{
if (exc)
throw Aya::runtime_error("NumberSequence: max number of keypoints exceeded.");
else
return false;
}
for (unsigned j = 0; j < keys.size(); ++j)
{
if (j < keys.size() - 1 && keys[j].time >= keys[j + 1].time)
{
if (exc)
throw Aya::runtime_error("ColorSequence: all keypoints must be ordered by time");
else
return false;
}
if (keys[j].value.min() < 0 || keys[j].value.max() > 1)
{
if (exc)
throw Aya::runtime_error("ColorSequence: color value out of range");
else
return false;
}
}
if (fabsf(keys.front().time) > 1e-4f)
{
if (exc)
throw Aya::runtime_error("ColorSequence must start at time=0.0");
else
return false;
}
if (fabsf(keys.back().time - 1.0f) > 1e-4f)
{
if (exc)
throw Aya::runtime_error("ColorSequence must end at time=1.0");
else
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////
// serialization support
static std::string tostr(const ColorSequence& cs)
{
const std::vector<ColorSequence::Key>& keys = cs.getPoints();
std::ostringstream oss;
for (int j = 0, e = keys.size(); j < e; ++j)
{
oss << keys[j].time << " " << keys[j].value.r << " " << keys[j].value.g << " " << keys[j].value.b << " " << keys[j].envelope << " ";
}
return oss.str();
}
static ColorSequence fromstr(const std::string& str)
{
std::vector<ColorSequence::Key> keys;
std::istringstream iss(str);
while (true)
{
ColorSequence::Key k;
iss >> k.time >> k.value.r >> k.value.g >> k.value.b >> k.envelope;
if (iss.eof())
break;
keys.push_back(k);
}
return keys;
}
namespace Reflection
{
template<>
int TypedPropertyDescriptor<ColorSequence>::getDataSize(const DescribedBase* instance) const
{
return sizeof(ColorSequence::Key) * getValue(instance).getPoints().size();
}
template<>
void TypedPropertyDescriptor<ColorSequence>::readValue(DescribedBase* instance, const XmlElement* element, IReferenceBinder& binder) const
{
std::string nodeval;
element->getValue(nodeval);
setValue(instance, fromstr(nodeval));
}
template<>
void TypedPropertyDescriptor<ColorSequence>::writeValue(const DescribedBase* instance, XmlElement* element) const
{
std::string nodeval = tostr(getValue(instance));
element->setValue(nodeval);
}
template<>
bool TypedPropertyDescriptor<ColorSequence>::hasStringValue() const
{
return true;
}
template<>
std::string TypedPropertyDescriptor<ColorSequence>::getStringValue(const DescribedBase* instance) const
{
ColorSequence s = getValue(instance);
std::string text = tostr(s);
return text;
}
template<>
bool TypedPropertyDescriptor<ColorSequence>::setStringValue(DescribedBase* instance, const std::string& text) const
{
ColorSequence s = fromstr(text);
setValue(instance, s);
return true;
}
template<>
ColorSequence& Variant::convert<ColorSequence>()
{
return genericConvert<ColorSequence>();
}
template<>
Type const& Type::getSingleton<ColorSequence>()
{
static TType<ColorSequence> type("ColorSequence");
return type;
}
//////////////////////////////////////////////////////////////////////////
template<>
ColorSequenceKeypoint& Variant::convert<ColorSequenceKeypoint>()
{
return genericConvert<ColorSequenceKeypoint>();
}
template<>
Type const& Type::getSingleton<ColorSequenceKeypoint>()
{
static TType<ColorSequenceKeypoint> type("ColorSequenceKeypoint");
return type;
}
} // namespace Reflection
template<>
bool StringConverter<ColorSequence>::convertToValue(const std::string& text, ColorSequence& value)
{
if (text.empty())
return false;
value = fromstr(text);
return true;
}
template<>
std::string StringConverter<ColorSequence>::convertToString(const ColorSequence& value)
{
return tostr(value);
}
template<>
bool StringConverter<ColorSequenceKeypoint>::convertToValue(const std::string& text, ColorSequenceKeypoint& value)
{
if (text.empty())
return false;
std::istringstream iss(text);
iss >> value.time >> value.value.r >> value.value.g >> value.value.b >> value.envelope;
return true;
}
template<>
std::string StringConverter<ColorSequenceKeypoint>::convertToString(const ColorSequenceKeypoint& value)
{
std::ostringstream oss;
oss << value.time << " " << value.value.r << " " << value.value.g << " " << value.value.b << " " << value.envelope << " ";
return oss.str();
}
} // namespace Aya

View File

@@ -0,0 +1,74 @@
#pragma once
#include <vector>
#include <sstream>
#undef min
#undef max
#include "Utility/G3DCore.hpp"
#include "Reflection/Reflection.hpp"
using G3D::Color3;
namespace Aya
{
class ColorSequenceKeypoint
{
public:
float time;
Color3 value;
float envelope;
ColorSequenceKeypoint()
: time(0)
, value(0, 0, 0)
, envelope(0)
{
}
ColorSequenceKeypoint(float t, Color3 v, float e)
: time(t)
, value(v)
, envelope(e)
{
}
bool operator==(const ColorSequenceKeypoint& r) const;
};
class ColorSequence
{
public:
typedef ColorSequenceKeypoint Key;
enum
{
kMaxSize = 20
}; // max number of keypoints permitted
explicit ColorSequence(Color3 constant = Color3(1, 1, 1));
ColorSequence(Color3 a, Color3 b);
ColorSequence(const std::vector<Key>& keys, bool exceptions = false);
ColorSequence(const ColorSequence& cs);
const std::vector<Key>& getPoints() const
{
return m_data;
}
Key start() const
{
return m_data.front();
}
Key end() const
{
return m_data.back();
}
void resample(G3D::Vector3* min, G3D::Vector3* max, int numPoints) const;
bool operator==(const ColorSequence& r) const;
static bool validate(const std::vector<Key>& keys, bool exceptions);
private:
std::vector<Key> m_data;
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,925 @@
#pragma once
#include "Tree/Verb.hpp"
#include "Utility/RunStateOwner.hpp"
#include "Tool/ToolsArrow.hpp"
#include "DataModel/Workspace.hpp"
#include "PartOperation.hpp"
DYNAMIC_FASTFLAG(UseRemoveTypeIDTricks)
namespace Aya
{
class PVInstance;
class Workspace;
class DataModel;
class Camera;
// Utility function
void AddChildToRoot(XmlElement* root, shared_ptr<Instance> wsi, const boost::function<bool(Instance*)>& isInScope, Aya::CreatorRole creatorRole);
void AddSelectionToRoot(XmlElement* root, Selection* sel, Aya::CreatorRole creatorRole);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BASE COMMAND TYPES
//
class RunStateVerb : public Verb
{
protected:
DataModel* dataModel;
ServiceClient<RunService> runService;
RunStateVerb(std::string name, DataModel* dataModel, bool blacklisted = false);
void playActionSound();
public:
virtual ~RunStateVerb();
};
// Edit commands are only enabled when the workspace is at Frame 0
// and when something is selected
class EditSelectionVerb : public Verb
{
protected:
shared_ptr<Workspace> workspace;
ServiceClient<Selection> selection;
DataModel* dataModel;
EditSelectionVerb(std::string name, DataModel* dataModel);
EditSelectionVerb(VerbContainer* container, std::string name, DataModel* dataModel);
public:
virtual ~EditSelectionVerb();
virtual bool isEnabled() const;
};
// Toggles the value of a bool property
class BoolPropertyVerb : public EditSelectionVerb
{
private:
const Name& propertyName;
protected:
BoolPropertyVerb(const std::string& name, DataModel* dataModel, const char* propertyName);
/*override*/ void doIt(IDataState* dataState);
/*override*/ bool isChecked() const;
};
// Camera commands - always enabled
class CameraVerb : public Verb
{
protected:
Workspace* workspace;
Camera* getCamera();
const Camera* getCamera() const;
ServiceClient<Selection> selection;
public:
CameraVerb(std::string name, Workspace* _workspace);
virtual bool isEnabled() const
{
return true;
}
virtual void doIt(IDataState* dataState);
};
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//
// EDIT MENU - descend from RunFrameZero commands
class DeleteBase : public EditSelectionVerb
{
private:
bool rewardHopper;
protected:
DeleteBase(VerbContainer* container, DataModel* dataModel, std::string name);
DeleteBase(DataModel* dataModel, std::string name, bool rewardHopper = false);
public:
virtual void doIt(IDataState* dataState);
};
class DeleteSelectionVerb : public DeleteBase
{
protected:
DeleteSelectionVerb(VerbContainer* container, DataModel* dataModel, std::string name)
: DeleteBase(container, dataModel, name)
{
}
public:
DeleteSelectionVerb(DataModel* dataModel)
: DeleteBase(dataModel, "Delete")
{
}
};
class PlayDeleteSelectionVerb : public DeleteBase
{
public:
PlayDeleteSelectionVerb(DataModel* dataModel)
: DeleteBase(dataModel, "PlayDelete", true)
{
}
};
class SelectAllCommand : public RunStateVerb
{
public:
SelectAllCommand(DataModel* dataModel)
: RunStateVerb("SelectAll", dataModel)
{
}
virtual void doIt(IDataState* dataState);
};
class SelectChildrenVerb : public EditSelectionVerb
{
private:
typedef EditSelectionVerb Super;
public:
SelectChildrenVerb(DataModel* dataModel);
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class SnapSelectionVerb : public EditSelectionVerb
{
private:
typedef EditSelectionVerb Super;
public:
SnapSelectionVerb(DataModel* dataModel);
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class UnlockAllVerb : public RunStateVerb
{
public:
UnlockAllVerb(DataModel* dataModel)
: RunStateVerb("UnlockAll", dataModel)
{
}
virtual void doIt(IDataState* dataState);
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// VIEW MENU
//
class CameraTiltUpCommand : public CameraVerb
{
public:
CameraTiltUpCommand(Workspace* workspace)
: CameraVerb("CameraTiltUp", workspace)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class CameraTiltDownCommand : public CameraVerb
{
public:
CameraTiltDownCommand(Workspace* workspace)
: CameraVerb("CameraTiltDown", workspace)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class CameraPanLeftCommand : public CameraVerb
{
public:
CameraPanLeftCommand(Workspace* workspace)
: CameraVerb("CameraPanLeft", workspace)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class CameraPanRightCommand : public CameraVerb
{
public:
CameraPanRightCommand(Workspace* workspace)
: CameraVerb("CameraPanRight", workspace)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class CameraZoomInCommand : public CameraVerb
{
public:
CameraZoomInCommand(Workspace* workspace)
: CameraVerb("CameraZoomIn", workspace)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class CameraZoomOutCommand : public CameraVerb
{
public:
CameraZoomOutCommand(Workspace* workspace)
: CameraVerb("CameraZoomOut", workspace)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class CameraZoomExtentsCommand : public CameraVerb
{
public:
CameraZoomExtentsCommand(Workspace* workspace);
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class CameraCenterCommand : public CameraVerb
{
public:
CameraCenterCommand(Workspace* workspace);
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class FirstPersonCommand : public Verb
{
private:
DataModel* dataModel;
public:
FirstPersonCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ void doIt(IDataState* dataState) {}
};
class ToggleViewMode : public Verb
{
private:
DataModel* dataModel;
public:
ToggleViewMode(Aya::DataModel* dm);
/*override*/ bool isChecked() const;
/*override*/ bool isEnabled() const;
/*override*/ bool isSelected() const;
/*override*/ void doIt(Aya::IDataState* dataState);
};
/////////////////////////////////////////////////////////////////////
class StatsCommand : public Verb
{
protected:
DataModel* dataModel;
public:
StatsCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ bool isChecked() const;
/*override*/ void doIt(IDataState* dataState);
};
class RenderStatsCommand : public Verb
{
protected:
DataModel* dataModel;
public:
RenderStatsCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ bool isChecked() const;
/*override*/ void doIt(IDataState* dataState);
};
class SummaryStatsCommand : public Verb
{
protected:
DataModel* dataModel;
public:
SummaryStatsCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ bool isChecked() const;
/*override*/ void doIt(IDataState* dataState);
};
class CustomStatsCommand : public Verb
{
protected:
DataModel* dataModel;
public:
CustomStatsCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ bool isChecked() const;
/*override*/ void doIt(IDataState* dataState);
};
class NetworkStatsCommand : public Verb
{
protected:
DataModel* dataModel;
public:
NetworkStatsCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ bool isChecked() const;
/*override*/ void doIt(IDataState* dataState);
};
class PhysicsStatsCommand : public Verb
{
protected:
DataModel* dataModel;
public:
PhysicsStatsCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ bool isChecked() const;
/*override*/ void doIt(IDataState* dataState);
};
class EngineStatsCommand : public Verb
{
private:
DataModel* dataModel;
public:
EngineStatsCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const
{
return true;
}
/*override*/ void doIt(IDataState* dataState);
};
class JoinCommand : public Verb
{
protected:
DataModel* dataModel;
public:
JoinCommand(DataModel* dataModel);
/*override*/ bool isEnabled() const;
/*override*/ void doIt(IDataState* dataState);
};
class ChatMenuCommand : public Verb
{
private:
int menu1;
int menu2;
int menu3;
public:
ChatMenuCommand(DataModel* dataModel, int menu1, int menu2, int menu3);
/*override*/ void doIt(IDataState* dataState) {}
static std::string getChatString(int menu1, int menu2, int menu3);
};
class MouseCommand;
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//
// TOOL Launchers
template<class MouseCommandClass, class ParentClass = RunStateVerb>
class TToolVerb : public ParentClass
{
private:
bool toggle;
public:
TToolVerb(DataModel* dataModel, bool toggle = true, bool blacklisted = true)
: ParentClass(MouseCommandClass::name().toString() +
"Tool", // MouseCommand should be a Aya::Named<> descendant, or must implement a static name() function
dataModel, blacklisted)
, toggle(toggle)
{
}
bool sameType(MouseCommand* mouseCommand) const
{
if (DFFlag::UseRemoveTypeIDTricks)
{
return mouseCommand ? MouseCommandClass::name() == mouseCommand->getName() : false;
}
else
{
return mouseCommand ? typeid(MouseCommandClass) == typeid(*mouseCommand) : false;
}
}
void doIt(IDataState* dataState)
{
if (isChecked() && toggle)
{
if (!MouseCommand::isAdvArrowToolEnabled())
ParentClass::dataModel->getWorkspace()->setNullMouseCommand(); // Toggle on / off if already on
else
ParentClass::dataModel->getWorkspace()->setDefaultMouseCommand(); // Toggle on / off if already on
}
else
{
ParentClass::dataModel->getWorkspace()->setMouseCommand(newMouseCommand());
}
}
bool isChecked() const
{
return sameType(ParentClass::dataModel->getWorkspace()->getCurrentMouseCommand());
}
virtual shared_ptr<MouseCommand> newMouseCommand()
{
return Creatable<MouseCommand>::create<MouseCommandClass>(ParentClass::dataModel->getWorkspace());
}
};
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//
// FORMAT MENU
class AnchorVerb : public EditSelectionVerb
{
public:
AnchorVerb(DataModel* dataModel);
virtual void doIt(IDataState* dataState);
virtual bool isChecked() const;
private:
FilteredSelection<Instance>* m_selection;
};
class MaterialVerb : public EditSelectionVerb
{
public:
MaterialVerb(DataModel* dataModel, std::string name = "MaterialVerb")
: EditSelectionVerb(name, dataModel)
{
}
virtual void doIt(IDataState* dataState);
static PartMaterial getCurrentMaterial()
{
return m_currentMaterial;
}
static void setCurrentMaterial(PartMaterial val)
{
m_currentMaterial = val;
}
static PartMaterial parseMaterial(const std::string materialString);
private:
static PartMaterial m_currentMaterial;
};
class ColorVerb : public EditSelectionVerb
{
public:
ColorVerb(DataModel* dataModel, std::string name = "ColorVerb")
: EditSelectionVerb(name, dataModel)
{
}
virtual void doIt(IDataState* dataState);
static Aya::BrickColor getCurrentColor()
{
return m_currentColor;
}
static void setCurrentColor(Aya::BrickColor val)
{
m_currentColor = val;
}
private:
static Aya::BrickColor m_currentColor;
};
class TranslucentVerb : public BoolPropertyVerb
{
public:
TranslucentVerb(DataModel* dataModel)
: BoolPropertyVerb("TranslucentVerb", dataModel, "Transparent")
{
}
};
class CanCollideVerb : public BoolPropertyVerb
{
public:
CanCollideVerb(DataModel* dataModel)
: BoolPropertyVerb("CanCollideVerb", dataModel, "CanCollide")
{
}
};
class AllCanSelectCommand : public RunStateVerb
{
public:
AllCanSelectCommand(DataModel* dataModel)
: RunStateVerb("AllCanSelect", dataModel)
{
}
virtual void doIt(IDataState* dataState);
};
class CanNotSelectCommand : public EditSelectionVerb
{
public:
CanNotSelectCommand(DataModel* dataModel)
: EditSelectionVerb("CanNotSelect", dataModel)
{
}
virtual void doIt(IDataState* dataState);
};
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
//
class MoveUpSelectionVerb : public EditSelectionVerb
{
private:
float moveUpHeight;
public:
MoveUpSelectionVerb(DataModel* dataModel, const std::string& name, float moveUpHeight)
: EditSelectionVerb(name, dataModel)
, moveUpHeight(moveUpHeight)
{
}
/*override*/ void doIt(IDataState* dataState);
};
class MoveUpPlateVerb : public MoveUpSelectionVerb
{
public:
MoveUpPlateVerb(DataModel* dataModel)
: MoveUpSelectionVerb(dataModel, "SelectionUpPlate", 0.4f)
{
}
};
class MoveUpBrickVerb : public MoveUpSelectionVerb
{
public:
MoveUpBrickVerb(DataModel* dataModel)
: MoveUpSelectionVerb(dataModel, "SelectionUpBrick", 1.2f)
{
}
};
class MoveDownSelectionVerb : public EditSelectionVerb
{
public:
MoveDownSelectionVerb(DataModel* dataModel);
virtual void doIt(IDataState* dataState);
};
class RotateAxisCommand : public EditSelectionVerb
{
protected:
RotateAxisCommand(std::string name, DataModel* dataModel)
: EditSelectionVerb(name, dataModel)
{
}
void rotateAboutAxis(const G3D::Matrix3& rotMatrix, const std::vector<PVInstance*>& selectedInstances);
virtual G3D::Matrix3 getRotationAxis() = 0;
public:
virtual void doIt(IDataState* dataState);
};
class RotateSelectionVerb : public RotateAxisCommand
{
public:
RotateSelectionVerb(DataModel* dataModel);
protected:
virtual G3D::Matrix3 getRotationAxis();
};
class TiltSelectionVerb : public RotateAxisCommand
{
public:
TiltSelectionVerb(DataModel* dataModel);
protected:
virtual G3D::Matrix3 getRotationAxis();
};
class CharacterCommand : public EditSelectionVerb
{
protected:
CharacterCommand(const std::string& name, DataModel* dataModel)
: EditSelectionVerb(name, dataModel)
{
}
public:
virtual bool isEnabled() const;
};
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//
// RUN MENU
//
class RunCommand : public RunStateVerb
{
public:
RunCommand(DataModel* dataModel)
: RunStateVerb("Run", dataModel)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class StopCommand : public RunStateVerb
{
public:
StopCommand(DataModel* dataModel)
: RunStateVerb("Stop", dataModel)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
class ResetCommand : public RunStateVerb
{
public:
ResetCommand(DataModel* dataModel)
: RunStateVerb("Reset", dataModel)
{
}
virtual bool isEnabled() const;
virtual void doIt(IDataState* dataState);
};
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//
// Advanced Build Related
//
class TurnOnManualJointCreation : public Verb
{
private:
DataModel* dataModel;
public:
TurnOnManualJointCreation(DataModel* dataModel);
virtual bool isEnabled() const
{
return true;
}
virtual bool isChecked() const
{
return (AdvArrowTool::advCreateJointsMode && AdvArrowTool::advManualJointMode);
}
virtual void doIt(IDataState* dataState);
};
class SetDragGridToOne : public Verb
{
private:
DataModel* dataModel;
public:
SetDragGridToOne(DataModel* dataModel);
virtual bool isEnabled() const
{
return true;
}
virtual bool isChecked() const
{
return AdvArrowTool::advGridMode == DRAG::ONE_STUD;
}
virtual void doIt(IDataState* dataState)
{
AdvArrowTool::advGridMode = DRAG::ONE_STUD;
}
};
class SetDragGridToOneFifth : public Verb
{
private:
DataModel* dataModel;
public:
SetDragGridToOneFifth(DataModel* dataModel);
virtual bool isEnabled() const
{
return true;
}
virtual bool isChecked() const
{
return AdvArrowTool::advGridMode == DRAG::QUARTER_STUD;
}
virtual void doIt(IDataState* dataState)
{
AdvArrowTool::advGridMode = DRAG::QUARTER_STUD;
}
};
class SetDragGridToOff : public Verb
{
private:
DataModel* dataModel;
public:
SetDragGridToOff(DataModel* dataModel);
virtual bool isEnabled() const
{
return true;
}
virtual bool isChecked() const
{
return AdvArrowTool::advGridMode == DRAG::OFF;
}
virtual void doIt(IDataState* dataState)
{
AdvArrowTool::advGridMode = DRAG::OFF;
}
};
class SetGridSizeToTwo : public Verb
{
private:
Workspace* workspace;
public:
SetGridSizeToTwo(DataModel* dataModel);
virtual bool isEnabled() const
{
return workspace->getShow3DGrid();
}
virtual bool isChecked() const
{
return (int)Workspace::gridSizeModifier == 2;
}
virtual void doIt(IDataState* dataState)
{
Workspace::gridSizeModifier = 2.0f;
}
};
class SetGridSizeToFour : public Verb
{
private:
Workspace* workspace;
public:
SetGridSizeToFour(DataModel* dataModel);
virtual bool isEnabled() const
{
return workspace->getShow3DGrid();
}
virtual bool isChecked() const
{
return (int)Workspace::gridSizeModifier == 4;
}
virtual void doIt(IDataState* dataState)
{
Workspace::gridSizeModifier = 4.0f;
}
};
class SetGridSizeToSixteen : public Verb
{
private:
Workspace* workspace;
public:
SetGridSizeToSixteen(DataModel* dataModel);
virtual bool isEnabled() const
{
return workspace->getShow3DGrid();
}
virtual bool isChecked() const
{
return (int)Workspace::gridSizeModifier == 16;
}
virtual void doIt(IDataState* dataState)
{
Workspace::gridSizeModifier = 16.0f;
}
};
class SetManualJointToWeak : public Verb
{
private:
DataModel* dataModel;
public:
SetManualJointToWeak(DataModel* dataModel);
virtual bool isEnabled() const
{
return false;
}
virtual bool isChecked() const
{
return false;
}
virtual void doIt(IDataState* dataState)
{
AdvArrowTool::advManualJointType = DRAG::WEAK_MANUAL_JOINT;
}
};
class SetManualJointToStrong : public Verb
{
private:
DataModel* dataModel;
public:
SetManualJointToStrong(DataModel* dataModel);
virtual bool isEnabled() const
{
return AdvArrowTool::advManualJointMode;
}
virtual bool isChecked() const
{
return AdvArrowTool::advManualJointType == DRAG::STRONG_MANUAL_JOINT;
}
virtual void doIt(IDataState* dataState)
{
AdvArrowTool::advManualJointType = DRAG::STRONG_MANUAL_JOINT;
}
};
class SetManualJointToInfinite : public Verb
{
private:
DataModel* dataModel;
public:
SetManualJointToInfinite(DataModel* dataModel);
virtual bool isEnabled() const
{
return false;
}
virtual bool isChecked() const
{
return false;
}
virtual void doIt(IDataState* dataState)
{
AdvArrowTool::advManualJointType = DRAG::INFINITE_MANUAL_JOINT;
}
};
} // namespace Aya

View File

@@ -0,0 +1,120 @@
#include "DataModel/CommonVerbs.hpp" // TODO - minimize these includes, and in the .h file
#include "DataModel/Workspace.hpp"
#include "DataModel/Camera.hpp"
#include "DataModel/UserController.hpp"
#include "DataModel/JointsService.hpp"
#include "DataModel/Sky.hpp"
#include "DataModel/Lighting.hpp"
#include "DataModel/Teams.hpp"
#include "DataModel/Hopper.hpp"
#include "DataModel/Backpack.hpp"
#include "DataModel/Tool.hpp"
#include "Humanoid/Humanoid.hpp"
#include "Player.hpp"
#include "Players.hpp"
#include "Tree/Verb.hpp"
#include "World/World.hpp"
#include "Kernel/Kernel.hpp"
#include "GUI/GUI.hpp"
#include "Script/LuaInstanceBridge.hpp"
#include "Script/ScriptContext.hpp"
#include "Utility/SoundWorld.hpp"
#include "Utility/Sound.hpp"
#include "Utility/StandardOut.hpp"
#include "Log.hpp"
#include "DrawPrimitives.hpp"
#include "Utility/Http.hpp"
#include "format.hpp"
#include "Xml/Serializer.hpp"
#include "boost/cast.hpp"
#include "boost/scoped_ptr.hpp"
namespace Aya
{
CommonVerbs::CommonVerbs(DataModel* dataModel)
: joinCommand(dataModel)
, statsCommand(dataModel)
, renderStatsCommand(dataModel)
, engineStatsCommand(dataModel)
, networkStatsCommand(dataModel)
, physicsStatsCommand(dataModel)
, summaryStatsCommand(dataModel)
, customStatsCommand(dataModel)
, runCommand(dataModel)
, stopCommand(dataModel)
, resetCommand(dataModel)
, axisRotateToolVerb(dataModel)
, resizeToolVerb(dataModel)
, advMoveToolVerb(dataModel)
, advRotateToolVerb(dataModel)
, advArrowToolVerb(dataModel, false)
, turnOnManualJointCreationVerb(dataModel)
, setDragGridToOneVerb(dataModel)
, setDragGridToOneFifthVerb(dataModel)
, setDragGridToOffVerb(dataModel)
, setGridSizeToTwoVerb(dataModel)
, setGridSizeToFourVerb(dataModel)
, setGridSizeToSixteenVerb(dataModel)
, setManualJointToWeakVerb(dataModel)
, setManualJointToStrongVerb(dataModel)
, setManualJointToInfiniteVerb(dataModel)
,
flatToolVerb(dataModel)
, glueToolVerb(dataModel)
, weldToolVerb(dataModel)
, studsToolVerb(dataModel)
, inletToolVerb(dataModel)
, universalToolVerb(dataModel)
, hingeToolVerb(dataModel)
, rightMotorToolVerb(dataModel)
, leftMotorToolVerb(dataModel)
, oscillateMotorToolVerb(dataModel)
, smoothNoOutlinesToolVerb(dataModel)
,
anchorToolVerb(dataModel)
, lockToolVerb(dataModel)
, fillToolVerb(dataModel, false)
, materialToolVerb(dataModel, false)
, materialVerb(dataModel)
, colorVerb(dataModel)
, anchorVerb(dataModel)
, dropperToolVerb(dataModel)
,
firstPersonCommand(dataModel)
, selectChildrenVerb(dataModel)
, snapSelectionVerb(dataModel)
, playDeleteSelectionVerb(dataModel)
, deleteSelectionVerb(dataModel)
, moveUpPlateVerb(dataModel)
, moveUpBrickVerb(dataModel)
, moveDownSelectionVerb(dataModel)
, rotateSelectionVerb(dataModel)
, tiltSelectionVerb(dataModel)
, selectAllCommand(dataModel)
, allCanSelectCommand(dataModel)
, canNotSelectCommand(dataModel)
, translucentVerb(dataModel)
, canCollideVerb(dataModel)
, unlockAllVerb(dataModel)
, gameToolVerb(dataModel)
, grabToolVerb(dataModel)
, cloneToolVerb(dataModel)
, hammerToolVerb(dataModel)
{
}
} // namespace Aya

View File

@@ -0,0 +1,141 @@
#pragma once
#include "DataModel/DataModel.hpp"
#include "DataModel/Commands.hpp"
#include "DataModel/ToolsPart.hpp"
#include "DataModel/ToolsSurface.hpp"
#include "DataModel/ToolsModel.hpp"
#include "Tool/ToolsArrow.hpp"
#include "Tool/ResizeTool.hpp"
#include "Tool/HammerTool.hpp"
#include "Tool/GrabTool.hpp"
#include "Tool/CloneTool.hpp"
#include "Tool/NullTool.hpp"
#include "Tool/GameTool.hpp"
#include "Tool/AxisMoveTool.hpp"
#include "Tool/AxisRotateTool.hpp"
#include "Tool/MoveResizeJoinTool.hpp"
#include "Tool/AdvMoveTool.hpp"
#include "Tool/AdvRotateTool.hpp"
#include "DataModel/UndoRedo.hpp"
#include "DataModel/InputObject.hpp"
#include "Tree/Instance.hpp"
#include "Tree/Service.hpp"
#include "Utility/RunStateOwner.hpp"
#include "Utility/InsertMode.hpp"
class XmlElement;
namespace Aya
{
class Fonts;
class GuiRoot;
class GuiItem;
class ContentProvider;
class TimeState;
class Hopper;
class PlayerHopper;
class StarterPackService;
class Adorn;
// Contain a set of verbs used by Roblox
class CommonVerbs
{
public:
CommonVerbs(DataModel* dataModel);
///////////////////////////////////////////////////////////////////////
//
// Play Mode Commands
PlayDeleteSelectionVerb playDeleteSelectionVerb;
// Edit Menu
DeleteSelectionVerb deleteSelectionVerb;
SelectAllCommand selectAllCommand;
SelectChildrenVerb selectChildrenVerb;
SnapSelectionVerb snapSelectionVerb;
UnlockAllVerb unlockAllVerb;
ColorVerb colorVerb;
MaterialVerb materialVerb;
// Format Menu
AnchorVerb anchorVerb;
TranslucentVerb translucentVerb;
CanCollideVerb canCollideVerb;
CanNotSelectCommand canNotSelectCommand;
AllCanSelectCommand allCanSelectCommand;
MoveUpPlateVerb moveUpPlateVerb;
MoveUpBrickVerb moveUpBrickVerb;
MoveDownSelectionVerb moveDownSelectionVerb;
RotateSelectionVerb rotateSelectionVerb;
TiltSelectionVerb tiltSelectionVerb;
// Run Menu
RunCommand runCommand;
StopCommand stopCommand;
ResetCommand resetCommand;
// Test Menu
FirstPersonCommand firstPersonCommand;
StatsCommand statsCommand;
RenderStatsCommand renderStatsCommand;
EngineStatsCommand engineStatsCommand;
NetworkStatsCommand networkStatsCommand;
PhysicsStatsCommand physicsStatsCommand;
SummaryStatsCommand summaryStatsCommand;
CustomStatsCommand customStatsCommand;
JoinCommand joinCommand;
// Adv Build Related
TurnOnManualJointCreation turnOnManualJointCreationVerb;
SetDragGridToOne setDragGridToOneVerb;
SetDragGridToOneFifth setDragGridToOneFifthVerb;
SetDragGridToOff setDragGridToOffVerb;
SetGridSizeToTwo setGridSizeToTwoVerb;
SetGridSizeToFour setGridSizeToFourVerb;
SetGridSizeToSixteen setGridSizeToSixteenVerb;
SetManualJointToWeak setManualJointToWeakVerb;
SetManualJointToStrong setManualJointToStrongVerb;
SetManualJointToInfinite setManualJointToInfiniteVerb;
// Tools
TToolVerb<AxisRotateTool> axisRotateToolVerb;
TToolVerb<AdvMoveTool> advMoveToolVerb;
TToolVerb<AdvRotateTool> advRotateToolVerb;
TToolVerb<AdvArrowTool> advArrowToolVerb;
TToolVerb<MoveResizeJoinTool> resizeToolVerb;
TToolVerb<FlatTool> flatToolVerb;
TToolVerb<GlueTool> glueToolVerb;
TToolVerb<WeldTool> weldToolVerb;
TToolVerb<StudsTool> studsToolVerb;
TToolVerb<InletTool> inletToolVerb;
TToolVerb<UniversalTool> universalToolVerb;
TToolVerb<HingeTool> hingeToolVerb;
TToolVerb<RightMotorTool> rightMotorToolVerb;
TToolVerb<LeftMotorTool> leftMotorToolVerb;
TToolVerb<OscillateMotorTool> oscillateMotorToolVerb;
TToolVerb<SmoothNoOutlinesTool> smoothNoOutlinesToolVerb;
TToolVerb<AnchorTool> anchorToolVerb;
TToolVerb<LockTool> lockToolVerb;
TToolVerb<FillTool> fillToolVerb;
TToolVerb<MaterialTool> materialToolVerb;
TToolVerb<DropperTool> dropperToolVerb;
// Runtime Tools
TToolVerb<GameTool> gameToolVerb;
TToolVerb<GrabTool> grabToolVerb;
TToolVerb<CloneTool> cloneToolVerb;
TToolVerb<HammerTool> hammerToolVerb;
};
} // namespace Aya

View File

@@ -0,0 +1,61 @@
#include "DataModel/Configuration.hpp"
#include "DataModel/CollectionService.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/ModelInstance.hpp"
#include "DataModel/Value.hpp"
namespace Aya
{
const char* const sConfiguration = "Configuration";
Configuration::Configuration()
: DescribedCreatable<Configuration, Instance, sConfiguration>()
{
setName("Configuration");
}
bool Configuration::askForbidChild(const Instance* instance) const
{
return (dynamic_cast<const IValue*>(instance) == NULL);
}
bool Configuration::askSetParent(const Instance* instance) const
{
if (!fastDynamicCast<PartInstance>(instance))
if (instance->getDescriptor() != ModelInstance::classDescriptor()) // ModelInstance, but not Workspace
return false;
if (instance->getChildren())
{
Instances::const_iterator end = instance->getChildren()->end();
for (Instances::const_iterator iter = instance->getChildren()->begin(); iter != end; ++iter)
{
if (fastDynamicCast<Configuration>(iter->get()))
// There can be only one Configuration object per part
return false;
}
}
return true;
}
void Configuration::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
if (oldProvider)
{
oldProvider->create<CollectionService>()->removeInstance(shared_from(this));
}
Super::onServiceProvider(oldProvider, newProvider);
if (newProvider)
{
newProvider->create<CollectionService>()->addInstance(shared_from(this));
}
}
} // namespace Aya

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Tree/Instance.hpp"
namespace Aya
{
extern const char* const sConfiguration;
class Configuration : public DescribedCreatable<Configuration, Instance, sConfiguration>
{
private:
typedef DescribedCreatable<Configuration, Instance, sConfiguration> Super;
public:
Configuration();
////////////////////////////////////////////////////////////////////////////////////
//
// Instance
/*override*/ bool askForbidChild(const Instance* instance) const;
/*override*/ bool askSetParent(const Instance* instance) const;
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,315 @@
#pragma once
#include "stdio.h"
#include "Utility/Name.hpp"
#include <string>
#include <istream>
#include <memory>
#include <vector>
#include "boost.hpp"
#include "time.hpp"
#include "Utility/ContentId.hpp"
#include "Utility/HeartbeatInstance.hpp"
#include "Utility/ThreadPool.hpp"
#include "Utility/AsyncHttpCache.hpp"
#include "Utility/LRUCache.hpp"
#include "Tree/Service.hpp"
#include "Log.hpp"
#include "Utility/ProtectedString.hpp"
#include "boost/filesystem.hpp"
#include "boost/optional.hpp"
void SetAssetFolder(const std::string& folder);
std::string GetAssetFolder();
void SetPlatformAssetFolder(const std::string& folder);
std::string GetPlatformAssetFolder();
void SetItemsFolder(const std::string& folder);
std::string GetItemsFolder();
void SetLevelsFolder(const std::string& folder);
std::string GetLevelsFolder();
void SetModelsFolder(const std::string& folder);
std::string GetModelsFolder();
void SetShadersFolder(const std::string& folder);
std::string GetShadersFolder();
void SetPluginsFolder(const std::string& folder);
std::string GetPluginsFolder();
namespace Aya
{
class AssetFetchMediator;
class Instance;
class Http;
extern const char* const sContentProvider;
class ContentProvider
: public DescribedNonCreatable<ContentProvider, Instance, sContentProvider, Reflection::ClassDescriptor::RUNTIME_LOCAL>
, public Service
, public HeartbeatInstance
{
private:
typedef DescribedNonCreatable<ContentProvider, Instance, sContentProvider, Reflection::ClassDescriptor::RUNTIME_LOCAL> Super;
public:
static Log* appLog;
static Aya::mutex* appLogLock;
static float PRIORITY_DEFAULT;
static float PRIORITY_MFC;
static float PRIORITY_SCRIPT;
static float PRIORITY_MESH;
static float PRIORITY_SOLIDMODEL;
static float PRIORITY_INSERT;
static float PRIORITY_CHARACTER;
static float PRIORITY_ANIMATION;
static float PRIORITY_TEXTURE;
static float PRIORITY_DECAL;
static float PRIORITY_SOUND;
static float PRIORITY_GUI;
static float PRIORITY_SKY;
private:
boost::shared_ptr<boost::thread> legacyContentCleanupThread;
boost::shared_ptr<boost::thread> contentCleanupThread;
static boost::filesystem::path assetFolderPath;
static boost::filesystem::path platformAssetFolderPath;
static boost::filesystem::path itemsFolderPath;
static boost::filesystem::path levelsFolderPath;
static boost::filesystem::path modelsFolderPath;
static boost::filesystem::path shadersFolderPath;
static boost::filesystem::path pluginsFolderPath;
static std::string assetFolderString;
static std::string platformAssetFolderString;
static std::string itemsFolderString;
static std::string levelsFolderString;
static std::string modelsFolderString;
static std::string shadersFolderString;
static std::string pluginsFolderString;
static bool assetFolderAlreadyInit;
struct CachedContent
{
shared_ptr<const std::string> data;
shared_ptr<const std::string> filename;
CachedContent() {}
CachedContent(shared_ptr<const std::string> filename)
: filename(filename)
{
}
CachedContent(shared_ptr<const std::string> data, shared_ptr<const std::string> filename)
: data(data)
, filename(filename)
{
}
};
boost::shared_ptr<AsyncHttpCache<CachedContent>> contentCache;
class PreloadAsyncRequest
{
public:
int outstanding;
int failed;
PreloadAsyncRequest()
: outstanding(0)
, failed(0)
{
}
PreloadAsyncRequest(int requestCount)
: outstanding(requestCount)
, failed(0)
{
}
};
public:
ContentProvider();
~ContentProvider();
static ContentId registerContent(std::istream& stream);
bool isUrlBad(Aya::ContentId id);
void blockingLoadInstances(ContentId id, std::vector<shared_ptr<Instance>>& instances);
bool isRequestQueueEmpty();
const std::string& getInstanceName() const;
const std::string& getInstanceCurrency() const;
const std::string& getInstanceMotd() const;
const std::string& getBaseUrl() const;
const std::string getApiBaseUrl() const;
const std::string getUnsecureApiBaseUrl() const;
static std::string getApiBaseUrl(const std::string& baseUrl);
static std::string getUnsecureApiBaseUrl(const std::string& baseUrl);
void setInstanceName(std::string name);
void setInstanceCurrency(std::string currency);
void setInstanceMotd(std::string motd);
void setBaseUrl(std::string url);
void setThreadPool(int count);
void setCacheSize(int count);
void preloadContentWithCallback(Aya::ContentId id, float priority, boost::function<void(AsyncHttpQueue::RequestResult)> callback,
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline, const std::string& expectedType = "");
void preloadContent(Aya::ContentId id);
void preloadContentBlockingList(
shared_ptr<const Reflection::ValueArray> idList, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
static boost::mutex preloadContentBlockingMutex;
void preloadContentBlockingListHelper(AsyncHttpQueue::RequestResult results, boost::function<void()> resumeFunction,
boost::function<void(std::string)> errorFunction, PreloadAsyncRequest* pRequest, ContentId id);
void preloadContentResultCallback(AsyncHttpQueue::RequestResult results, ContentId id);
void clearContent();
void invalidateCache(ContentId contentId);
// returns true if the content is available.
bool hasContent(const ContentId& id);
// returns non-NULL if the content is available. If not available, the provider does an asynchronous request of the content for later
shared_ptr<const std::string> requestContentString(const ContentId& id, float priority);
// Async request
void getContent(const Aya::ContentId& id, float priority, AsyncHttpQueue::RequestCallback callback,
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline, const std::string& expectedType = "");
void loadContent(const Aya::ContentId& id, float priority,
boost::function<void(AsyncHttpQueue::RequestResult, shared_ptr<Instances>, shared_ptr<std::exception>)> callback,
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline);
void loadContentString(const Aya::ContentId& id, float priority,
boost::function<void(AsyncHttpQueue::RequestResult, shared_ptr<const std::string>, shared_ptr<std::exception>)> callback,
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline);
// The following functions throw exceptions upon failure or return NULL
shared_ptr<const std::string> getContentString(ContentId id);
std::auto_ptr<std::istream> getContent(const ContentId& contentId, const std::string& expectedType = "");
std::string getFile(ContentId contentId);
static std::string getAssetFile(const char* filePath);
// throws an exception
static void verifyRequestedScriptSignature(const ProtectedString& source, const std::string& assetId, bool required);
static void verifyScriptSignature(const ProtectedString& source, bool required);
int getRequestQueueSize() const;
shared_ptr<const Reflection::ValueArray> getFailedUrls();
shared_ptr<const Reflection::ValueArray> getRequestQueueUrls();
shared_ptr<const Reflection::ValueArray> getRequestedUrls();
static void setAssetFolder(const char* path);
static void setPlatformAssetFolder(const char* path);
static void setLevelsFolder(const char* path);
static void setItemsFolder(const char* path);
static void setModelsFolder(const char* path);
static void setShadersFolder(const char* path);
static void setPluginsFolder(const char* path);
static std::string getAssetFolder()
{
return assetFolderPath.string();
};
static std::string assetFolder();
static std::string platformAssetFolder();
static std::string getPlatformAssetFolder()
{
return platformAssetFolderPath.string();
};
static std::string itemsFolder();
static std::string getItemsFolder()
{
return itemsFolderPath.string();
};
static std::string levelsFolder();
static std::string getLevelsFolder()
{
return levelsFolderPath.string();
};
static std::string modelsFolder();
static std::string getModelsFolder()
{
return modelsFolderPath.string();
};
static std::string shadersFolder();
static std::string getShadersFolder()
{
return shadersFolderPath.string();
};
static std::string pluginsFolder();
static std::string getPluginsFolder()
{
return pluginsFolderPath.string();
};
static bool isUrl(const std::string& s);
static bool isHttpUrl(const std::string& s);
static std::string findAsset(Aya::ContentId contentId);
static Reflection::PropDescriptor<ContentProvider, std::string> desc_baseUrl;
static Reflection::PropDescriptor<ContentProvider, std::string> desc_instanceName;
static Reflection::PropDescriptor<ContentProvider, std::string> desc_instanceCurrency;
static Reflection::PropDescriptor<ContentProvider, std::string> desc_instanceMotd;
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
contentCache->resetStatsItem(newProvider);
Super::onServiceProvider(oldProvider, newProvider);
onServiceProviderHeartbeatInstance(oldProvider, newProvider); // hooks up heartbeat
}
/*implement*/ virtual void onHeartbeat(const Heartbeat& event);
void printContentNames()
{
contentCache->printContentNames();
}
void setAssetFetchMediator(AssetFetchMediator* afm);
private:
// FullAsyncRequest: all requests (including disk requests) can be asynchronous. disk requests are returned as memory streams, exactly like http
// responses.
typedef enum
{
NoHttpRequest,
AsyncHttpRequest,
SyncHttpRequest,
FullAsyncRequest
} RequestType;
bool clearFinishFlag;
AssetFetchMediator* afm;
std::string baseUrl;
std::string instanceName;
std::string instanceCurrency;
std::string instanceMotd;
bool isContentLoaded(ContentId id);
bool blockingLoadContent(ContentId id, CachedContent* result, const std::string& expectedType = "");
AsyncHttpQueue::RequestResult privateLoadContent(ContentId& id, RequestType httpRequestType, float priority, CachedContent* result,
AsyncHttpQueue::RequestCallback* callback, AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline,
const std::string& expectedType = "");
static std::string findHashFile(ContentId contentId);
static bool findLocalFile(const std::string& url, std::string* filename);
bool registerFile(const ContentId& id, CachedContent* item);
static bool isInSandbox(const boost::filesystem::path& path, const boost::filesystem::path& sandbox);
};
class AssetFetchMediator
{
public:
virtual boost::optional<std::string> findCachedAssetOrEmpty(const ContentId& contentId, int universeId) = 0;
};
} // namespace Aya

View File

@@ -0,0 +1,948 @@
//
// ContextActionService.cpp
// App
//
// Created by Ben Tkacheff on 1/17/13.
//
//
#include "DataModel/ContextActionService.hpp"
#include "DataModel/ModelInstance.hpp"
#include "DataModel/GuiService.hpp"
#include "DataModel/Tool.hpp"
#include "DataModel/UserInputService.hpp"
#include "DataModel/GuiObject.hpp"
#include "Script/ScriptContext.hpp"
#include "Players.hpp"
DYNAMIC_FASTFLAGVARIABLE(TurnOffFakeEventsForCAS, false)
namespace Aya
{
const char* const sContextActionService = "ContextActionService";
// tool convenience functions (todo: remove these)
static Reflection::BoundFuncDesc<ContextActionService, std::string()> func_getCurrentLocalToolIcon(
&ContextActionService::getCurrentLocalToolIcon, "GetCurrentLocalToolIcon", Security::None);
static Reflection::EventDesc<ContextActionService, void(shared_ptr<Instance>)> event_LocalToolEquipped(
&ContextActionService::equippedToolSignal, "LocalToolEquipped", "toolEquipped");
static Reflection::EventDesc<ContextActionService, void(shared_ptr<Instance>)> event_LocalToolUnequipped(
&ContextActionService::unequippedToolSignal, "LocalToolUnequipped", "toolUnequipped");
// lua function to action binding (user facing functions)
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, Lua::WeakFunctionRef, bool, shared_ptr<const Reflection::Tuple>)>
func_bindCore(&ContextActionService::bindCoreActionForInputTypes, "BindCoreAction", "actionName", "functionToBind", "createTouchButton",
"inputTypes", Security::RobloxScript);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, Lua::WeakFunctionRef, bool, shared_ptr<const Reflection::Tuple>)> func_bind(
&ContextActionService::bindActionForInputTypes, "BindAction", "actionName", "functionToBind", "createTouchButton", "inputTypes", Security::None);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, Lua::WeakFunctionRef, bool, shared_ptr<const Reflection::Tuple>)>
func_bind_old(&ContextActionService::bindActionForInputTypes, "BindActionToInputTypes", "actionName", "functionToBind", "createTouchButton",
"inputTypes", Security::None, Reflection::Descriptor::Attributes::deprecated(func_bind));
static Reflection::BoundFuncDesc<ContextActionService, void(InputObject::UserInputType, KeyCode)> func_bindActivate(
&ContextActionService::bindActivate, "BindActivate", "userInputTypeForActivation", "keyCodeForActivation", SDLK_UNKNOWN, Security::None);
static Reflection::BoundFuncDesc<ContextActionService, void(InputObject::UserInputType, KeyCode)> func_unbindActivate(
&ContextActionService::unbindActivate, "UnbindActivate", "userInputTypeForActivation", "keyCodeForActivation", SDLK_UNKNOWN, Security::None);
// touch button interface
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, std::string)> func_AddTitle(
&ContextActionService::setTitleForAction, "SetTitle", "actionName", "title", Security::None);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, std::string)> func_AddDesc(
&ContextActionService::setDescForAction, "SetDescription", "actionName", "description", Security::None);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, std::string)> func_AddImage(
&ContextActionService::setImageForAction, "SetImage", "actionName", "image", Security::None);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, UDim2)> func_SetPosition(
&ContextActionService::setPositionForAction, "SetPosition", "actionName", "position", Security::None);
static Reflection::BoundYieldFuncDesc<ContextActionService, shared_ptr<Instance>(std::string)> func_GetButton(
&ContextActionService::getButton, "GetButton", "actionName", Security::None);
// unbinding
static Reflection::BoundFuncDesc<ContextActionService, void(std::string)> func_unbindCoreAction(
&ContextActionService::unbindCoreAction, "UnbindCoreAction", "actionName", Security::RobloxScript);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string)> func_unbindAction(
&ContextActionService::unbindAction, "UnbindAction", "actionName", Security::None);
static Reflection::BoundFuncDesc<ContextActionService, void()> func_unbindAll(&ContextActionService::unbindAll, "UnbindAllActions", Security::None);
// bound info get
static Reflection::BoundFuncDesc<ContextActionService, shared_ptr<const Reflection::ValueTable>(std::string)> func_getBoundFunctionData(
&ContextActionService::getBoundActionData, "GetBoundActionInfo", "actionName", Security::None);
static Reflection::BoundFuncDesc<ContextActionService, shared_ptr<const Reflection::ValueTable>()> func_getAllBoundFunctionData(
&ContextActionService::getAllBoundActionData, "GetAllBoundActionInfo", Security::None);
// backend signals (for core scripts, not user exposed)
static Reflection::EventDesc<ContextActionService, void(std::string, std::string, shared_ptr<const Reflection::ValueTable>)>
event_boundFunctionUpdated(
&ContextActionService::boundActionChangedSignal, "BoundActionChanged", "actionChanged", "changeName", "changeTable", Security::RobloxScript);
static Reflection::EventDesc<ContextActionService, void(std::string, bool, shared_ptr<const Reflection::ValueTable>)> event_addedBoundFunction(
&ContextActionService::boundActionAddedSignal, "BoundActionAdded", "actionAdded", "createTouchButton", "functionInfoTable",
Security::RobloxScript);
static Reflection::EventDesc<ContextActionService, void(std::string, shared_ptr<const Reflection::ValueTable>)> event_removedBoundFunction(
&ContextActionService::boundActionRemovedSignal, "BoundActionRemoved", "actionRemoved", "functionInfoTable", Security::RobloxScript);
static Reflection::EventDesc<ContextActionService, void(std::string)> event_getActionButton(
&ContextActionService::getActionButtonSignal, "GetActionButtonEvent", "actionName", Security::RobloxScript);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, shared_ptr<Instance>)> func_actionButtonFound(
&ContextActionService::fireActionButtonFoundSignal, "FireActionButtonFoundSignal", "actionName", "actionButton", Security::RobloxScript);
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, InputObject::UserInputState, shared_ptr<Instance>)> func_callFunction(
&ContextActionService::callFunction, "CallFunction", "actionName", "state", "inputObject", Security::RobloxScript);
REFLECTION_END();
ContextActionService::ContextActionService()
: Super()
{
setName(sContextActionService);
Aya::Guid::generateRBXGUID(activateGuid);
}
Tool* ContextActionService::getCurrentLocalTool()
{
Aya::Network::Players* players = ServiceProvider::create<Aya::Network::Players>(this);
if (Aya::Network::Player* player = players->getLocalPlayer())
if (ModelInstance* character = player->getCharacter())
if (Aya::Tool* activeTool = character->findFirstChildOfType<Aya::Tool>())
return activeTool;
return NULL;
}
std::string ContextActionService::getCurrentLocalToolIcon()
{
if (Tool* activeTool = getCurrentLocalTool())
return activeTool->getTextureId().c_str();
return "";
}
void ContextActionService::disconnectAllCharacterConnections()
{
characterChildAddConnection.disconnect();
characterChildRemoveConnection.disconnect();
}
Tool* ContextActionService::isTool(shared_ptr<Instance> instance)
{
if (Instance* baldInstance = instance.get())
if (Tool* tool = Instance::fastDynamicCast<Tool>(baldInstance))
return tool;
return NULL;
}
void ContextActionService::checkForNewTool(shared_ptr<Instance> newChildOfCharacter)
{
if (Tool* newTool = isTool(newChildOfCharacter))
equippedToolSignal(shared_from(newTool));
}
void ContextActionService::checkForToolRemoval(shared_ptr<Instance> removedChildOfCharacter)
{
if (Tool* removedTool = isTool(removedChildOfCharacter))
unequippedToolSignal(shared_from(removedTool));
}
void ContextActionService::setupLocalCharacterConnections(ModelInstance* character)
{
characterChildAddConnection = character->onDemandWrite()->childAddedSignal.connect(boost::bind(&ContextActionService::checkForNewTool, this, _1));
characterChildRemoveConnection =
character->onDemandWrite()->childRemovedSignal.connect(boost::bind(&ContextActionService::checkForToolRemoval, this, _1));
}
void ContextActionService::localCharacterAdded(shared_ptr<Instance> character)
{
disconnectAllCharacterConnections();
if (ModelInstance* characterModel = Instance::fastDynamicCast<ModelInstance>(character.get()))
setupLocalCharacterConnections(characterModel);
}
void ContextActionService::checkForLocalPlayer(shared_ptr<Instance> newPlayer)
{
if (Aya::Network::Players* players = ServiceProvider::create<Aya::Network::Players>(this))
{
if (Aya::Network::Player* player = Instance::fastDynamicCast<Aya::Network::Player>(newPlayer.get()))
{
if (player == players->getLocalPlayer())
{
localPlayerAddConnection.disconnect();
setupLocalPlayerConnections(player);
}
}
}
}
void ContextActionService::setupLocalPlayerConnections(Aya::Network::Player* localPlayer)
{
if (ModelInstance* character = localPlayer->getCharacter())
localCharacterAdded(shared_from(character));
else
localPlayer->characterAddedSignal.connect(boost::bind(&ContextActionService::localCharacterAdded, this, _1));
}
void ContextActionService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
if (oldProvider)
{
// currently nothing needed to be done here (we disconnect on new adds)
}
Super::onServiceProvider(oldProvider, newProvider);
if (newProvider)
{
Aya::Network::Players* players = ServiceProvider::create<Aya::Network::Players>(newProvider);
if (Aya::Network::Player* player = players->getLocalPlayer())
setupLocalPlayerConnections(player);
else
localPlayerAddConnection =
players->onDemandWrite()->childAddedSignal.connect(boost::bind(&ContextActionService::checkForLocalPlayer, this, _1));
}
}
bool processInputForPlayerMovement(const PlayerActionType& playerMovement, const shared_ptr<InputObject>& inputObject)
{
// todo: remove hard coded keys, allow users to set this
if (inputObject->isKeyEvent())
{
switch (inputObject->getKeyCode())
{
case SDLK_w:
{
if (playerMovement == CHARACTER_FORWARD)
{
return true;
}
break;
}
case SDLK_UP:
{
if (playerMovement == CHARACTER_FORWARD)
{
return true;
}
break;
}
case SDLK_s:
{
if (playerMovement == CHARACTER_BACKWARD)
{
return true;
}
break;
}
case SDLK_DOWN:
{
if (playerMovement == CHARACTER_BACKWARD)
{
return true;
}
break;
}
case SDLK_a:
{
if (playerMovement == CHARACTER_LEFT)
{
return true;
}
break;
}
case SDLK_d:
{
if (playerMovement == CHARACTER_RIGHT)
{
return true;
}
break;
}
case SDLK_SPACE:
{
if (playerMovement == CHARACTER_JUMP)
{
return true;
}
break;
}
default:
break;
}
}
return false;
}
bool tupleContainsInputObject(shared_ptr<const Reflection::Tuple> inputBindings, const shared_ptr<InputObject>& inputObject, bool& sinkInput)
{
sinkInput = true;
if (inputBindings && inputBindings->values.size() > 0)
{
bool inputObjectIsKeyEvent = inputObject->isKeyEvent() || inputObject->isGamepadEvent();
Reflection::ValueArray inputBindingsValues = inputBindings->values;
// loop thru all the input bindings and compare them with the input object
for (Reflection::ValueArray::iterator tupleIter = inputBindingsValues.begin(); tupleIter != inputBindingsValues.end(); ++tupleIter)
{
// we have a key event, check and see if user passed in key code, or string that is key code
if (inputObjectIsKeyEvent)
{
KeyCode keyCode = SDLK_UNKNOWN;
if (tupleIter->isType<KeyCode>())
{
keyCode = tupleIter->cast<KeyCode>();
}
else if (tupleIter->isString())
{
std::string keyString = tupleIter->get<std::string>();
if (keyString.length() == 1)
{
keyCode = (KeyCode)keyString[0];
}
}
if (keyCode != SDLK_UNKNOWN && inputObject->getKeyCode() == keyCode)
{
return true;
}
if (tupleIter->isType<InputObject::UserInputType>())
{
InputObject::UserInputType inputType = tupleIter->cast<InputObject::UserInputType>();
if (inputType == inputObject->getUserInputType())
{
return true;
}
}
else if (tupleIter->isType<PlayerActionType>())
{
PlayerActionType playerMovement = tupleIter->cast<PlayerActionType>();
if (processInputForPlayerMovement(playerMovement, inputObject))
{
sinkInput = false;
return true;
}
}
}
// otherwise we might have a userinputtype, check against these
else if (tupleIter->isType<InputObject::UserInputType>())
{
InputObject::UserInputType inputType = tupleIter->cast<InputObject::UserInputType>();
if (inputType == inputObject->getUserInputType())
{
return true;
}
}
}
}
sinkInput = false;
return false;
}
void ContextActionService::callFunction(boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction, const std::string actionName,
const InputObject::UserInputState state, const shared_ptr<Instance> inputObject)
{
if (!luaFunction.empty())
{
shared_ptr<Reflection::Tuple> args = Aya::make_shared<Reflection::Tuple>();
args->values.push_back(actionName);
args->values.push_back(state);
args->values.push_back(inputObject);
luaFunction(args);
}
}
void ContextActionService::callFunction(const std::string actionName, const InputObject::UserInputState state, const shared_ptr<Instance> inputObject)
{
FunctionMap::iterator iter = findAction(actionName);
if (iter == functionMap.end())
{
StandardOut::singleton()->printf(MESSAGE_ERROR, "ContextActionService::CallFunction does have a function for %s", actionName.c_str());
return;
}
callFunction(iter->second.luaFunction, actionName, state, inputObject);
}
GuiResponse ContextActionService::tryProcess(shared_ptr<InputObject> inputObject, FunctionVector& funcVector, bool menuIsOpen)
{
if (DFFlag::TurnOffFakeEventsForCAS && !inputObject->isPublicEvent())
return GuiResponse::notSunk();
if (!inputObject)
return GuiResponse::notSunk();
if (!inputObject.get())
return GuiResponse::notSunk();
for (FunctionVector::reverse_iterator iter = funcVector.rbegin(); iter != funcVector.rend(); ++iter)
{
if (iter->second.inputTypes && iter->second.inputTypes->values.size() > 0)
{
bool sink = false;
if (tupleContainsInputObject(iter->second.inputTypes, inputObject, sink))
{
// first check to see if BindAction should call an action
if (!iter->second.luaFunction.empty())
{
if (shared_ptr<Instance> instance = shared_from(Instance::fastDynamicCast<Instance>(inputObject.get())))
{
iter->second.lastInput = weak_from(inputObject.get());
callFunction(iter->second.luaFunction, iter->first,
menuIsOpen ? InputObject::INPUT_STATE_CANCEL : inputObject->getUserInputState(), instance);
}
if (sink)
return GuiResponse::sunk();
return GuiResponse::notSunk();
}
// see if we should fire Activate from BindActivate
else if (iter->first.compare(activateGuid) == 0)
{
bool canActivate = false;
Reflection::ValueArray activateTuple = iter->second.inputTypes->values;
if (!activateTuple.empty() && activateTuple.begin()->isType<InputObject::UserInputType>() &&
activateTuple.begin()->get<InputObject::UserInputType>() == inputObject->getUserInputType())
{
if (activateTuple.size() == 1)
{
canActivate = true;
}
else if (activateTuple.back().isType<KeyCode>() && activateTuple.back().get<KeyCode>() == inputObject->getKeyCode())
{
canActivate = true;
}
}
if (canActivate && !menuIsOpen)
{
if (UserInputService* inputService = Aya::ServiceProvider::find<UserInputService>(this))
{
Vector2 currentPos = inputService->getCurrentMousePosition()->get2DPosition();
bool isDown = inputObject->getUserInputState() == InputObject::INPUT_STATE_BEGIN;
shared_ptr<InputObject> simulateInputObject = Aya::Creatable<Aya::Instance>::create<Aya::InputObject>(
InputObject::TYPE_MOUSEBUTTON1, isDown ? InputObject::INPUT_STATE_BEGIN : InputObject::INPUT_STATE_END,
Vector3(currentPos.x, currentPos.y, 0), Vector3::zero(), Aya::DataModel::get(this));
// r2 and l2 are special kinds of input, they don't have simple up and down states
if (inputObject->getKeyCode() == SDLK_GAMEPAD_BUTTONR2 || inputObject->getKeyCode() == SDLK_GAMEPAD_BUTTONL2)
{
float zPos = inputObject->getPosition().z;
float lastZPos = 0.0f;
InputObject* input = inputObject.get();
if (lastZPositionsForActivate.find(input) != lastZPositionsForActivate.end())
{
lastZPos = lastZPositionsForActivate[input];
}
if (zPos >= 0.5 && lastZPos < 0.5)
{
simulateInputObject->setInputState(InputObject::INPUT_STATE_BEGIN);
Aya::DataModel::get(this)->processInputObject(simulateInputObject);
}
else if (zPos <= 0.2 && lastZPos > 0.2)
{
simulateInputObject->setInputState(InputObject::INPUT_STATE_END);
Aya::DataModel::get(this)->processInputObject(simulateInputObject);
}
lastZPositionsForActivate[input] = zPos;
}
else
{
Aya::DataModel::get(this)->processInputObject(simulateInputObject);
}
}
}
}
// guess we have nothing, error out
else
{
StandardOut::singleton()->printf(
MESSAGE_ERROR, "ContextActionService::CallFunction does have a function for %s", iter->first.c_str());
return GuiResponse::notSunk();
}
}
}
}
return GuiResponse::notSunk();
}
GuiResponse ContextActionService::processCoreBindings(const shared_ptr<InputObject>& inputObject)
{
return tryProcess(inputObject, coreFunctionVector, false);
}
GuiResponse ContextActionService::processDevBindings(const shared_ptr<InputObject>& inputObject, bool menuIsOpen)
{
return tryProcess(inputObject, functionVector, menuIsOpen);
}
static shared_ptr<Reflection::ValueTable> constructTableFromBoundFunctionData(const BoundFunctionData& functionData)
{
shared_ptr<Reflection::ValueTable> functionTable = Aya::make_shared<Reflection::ValueTable>();
if (!functionData.title.empty())
{
(*functionTable)["title"] = functionData.title;
}
if (!functionData.image.empty())
{
(*functionTable)["image"] = functionData.image;
}
if (!functionData.description.empty())
{
(*functionTable)["description"] = functionData.description;
}
if (functionData.hasTouchButton)
{
(*functionTable)["createTouchButton"] = functionData.hasTouchButton;
}
if (functionData.inputTypes)
{
shared_ptr<Reflection::ValueArray> inputArray = Aya::make_shared<Reflection::ValueArray>(functionData.inputTypes->values);
(*functionTable)["inputTypes"] = Reflection::Variant(shared_ptr<const Reflection::ValueArray>(inputArray));
}
return functionTable;
}
void unbindActionInternal(const std::string actionName, FunctionMap& funcMap, FunctionVector& funcVector,
Aya::signal<void(std::string, shared_ptr<const Reflection::ValueTable>)>& removedSignal)
{
if (funcMap.find(actionName) != funcMap.end())
{
BoundFunctionData functionData = funcMap[actionName];
shared_ptr<const Reflection::ValueTable> functionTable = constructTableFromBoundFunctionData(functionData);
funcMap.erase(actionName);
for (FunctionVector::iterator iter = funcVector.begin(); iter != funcVector.end(); ++iter)
{
if ((*iter).first == actionName)
{
funcVector.erase(iter);
break;
}
}
// now fire a signal so we know a new function was removed (used at least by core scripts)
removedSignal(actionName, functionTable);
}
}
void ContextActionService::unbindCoreAction(const std::string actionName)
{
unbindActionInternal(actionName, coreFunctionMap, coreFunctionVector, boundActionRemovedSignal);
}
void ContextActionService::unbindAction(const std::string actionName)
{
unbindActionInternal(actionName, functionMap, functionVector, boundActionRemovedSignal);
}
void ContextActionService::unbindAll()
{
for (FunctionMap::iterator iter = functionMap.begin(); iter != functionMap.end(); ++iter)
{
shared_ptr<const Reflection::ValueTable> functionTable = constructTableFromBoundFunctionData(iter->second);
boundActionRemovedSignal((iter->first), functionTable);
}
functionMap.clear();
functionVector.clear();
}
static void InvokeCallback(weak_ptr<ContextActionService> weakActionService, Lua::WeakFunctionRef callback, shared_ptr<Reflection::Tuple> args)
{
if (shared_ptr<ContextActionService> strongThis = weakActionService.lock())
{
if (Lua::ThreadRef threadRef = callback.lock())
{
if (ScriptContext* sc = ServiceProvider::create<ScriptContext>(strongThis.get()))
{
try
{
sc->callInNewThread(callback, *(args.get()));
}
catch (Aya::base_exception& e)
{
StandardOut::singleton()->printf(MESSAGE_ERROR, "ContextActionService: Unexpected error while invoking callback: %s", e.what());
}
}
}
}
}
void ContextActionService::checkForInputOverride(const Reflection::Variant& newInputType, const FunctionVector& funcVector)
{
for (FunctionVector::const_reverse_iterator iter = funcVector.rbegin(); iter != funcVector.rend(); ++iter)
{
if (iter->second.inputTypes && iter->second.inputTypes->values.size() > 0)
{
Reflection::ValueArray inputBindingsValues = iter->second.inputTypes->values;
for (Reflection::ValueArray::const_iterator tupleIter = inputBindingsValues.begin(); tupleIter != inputBindingsValues.end(); ++tupleIter)
{
bool didOverride = false;
if (newInputType.isType<KeyCode>() && tupleIter->isType<KeyCode>())
{
didOverride = (tupleIter->cast<KeyCode>() == newInputType.cast<KeyCode>());
}
else if (newInputType.isString() && tupleIter->isString())
{
didOverride = (tupleIter->cast<std::string>() == newInputType.cast<std::string>());
}
else if (newInputType.isType<InputObject::UserInputType>() && tupleIter->isType<InputObject::UserInputType>())
{
didOverride = (tupleIter->cast<InputObject::UserInputType>() == newInputType.cast<InputObject::UserInputType>());
}
else if (newInputType.isType<PlayerActionType>() && tupleIter->isType<PlayerActionType>())
{
didOverride = (tupleIter->cast<PlayerActionType>() == newInputType.cast<PlayerActionType>());
}
if (didOverride)
{
shared_ptr<InputObject> cancelInputObject = iter->second.lastInput.lock();
InputObject::UserInputState prevInputState = InputObject::INPUT_STATE_NONE;
if (cancelInputObject)
{
prevInputState = cancelInputObject->getUserInputState();
cancelInputObject->setInputState(InputObject::INPUT_STATE_CANCEL);
}
else
{
cancelInputObject = Aya::Creatable<Aya::Instance>::create<Aya::InputObject>(Aya::InputObject::TYPE_NONE,
Aya::InputObject::INPUT_STATE_CANCEL, Aya::Vector3::zero(), Aya::Vector3::zero(), Aya::DataModel::get(this));
}
if (shared_ptr<Instance> instance = shared_from(Instance::fastDynamicCast<Instance>(cancelInputObject.get())))
{
callFunction(iter->second.luaFunction, iter->first, InputObject::INPUT_STATE_CANCEL, instance);
}
cancelInputObject->setInputState(prevInputState);
return;
}
}
}
}
}
void ContextActionService::bindActionInternal(const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton,
shared_ptr<const Reflection::Tuple> hotkeys, FunctionMap& funcMap, FunctionVector& funcVector)
{
if (!Network::Players::frontendProcessing(this))
{
throw Aya::runtime_error("ContextActionService:BindAction can only be called from a local script");
return;
}
if (actionName.empty())
{
throw Aya::runtime_error("ContextActionService:BindAction called with an empty actionName");
return;
}
if (hotkeys && !hotkeys->values.empty())
{
for (Reflection::ValueArray::const_iterator iter = hotkeys->values.begin(); iter != hotkeys->values.end(); ++iter)
{
if (!iter->isType<KeyCode>() && !iter->isString() && !iter->isType<InputObject::UserInputType>() && !iter->isType<PlayerActionType>())
{
throw Aya::runtime_error(
"ContextActionService:BindAction called with an invalid hotkey (should be either Enum.KeyCode, Enum.UserInputType or string)");
return;
}
// Now check to see if this bind is overriding another bind action (so we can signal the action it is no longer active)
// core Aya overrides only happen when its another core bind
if (funcVector == coreFunctionVector)
{
checkForInputOverride(*iter, coreFunctionVector);
}
// game developer overrides happen for all new binds
checkForInputOverride(*iter, functionVector);
}
}
if (funcMap.find(actionName) != funcMap.end())
{
if (funcMap == coreFunctionMap)
{
unbindCoreAction(actionName);
}
else
{
unbindAction(actionName);
}
}
boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction = boost::bind(&InvokeCallback, shared_from(this), functionToBind, _1);
BoundFunctionData data;
if (hotkeys)
{
data = BoundFunctionData(hotkeys, luaFunction, createTouchButton);
}
else
{
data = BoundFunctionData(luaFunction, createTouchButton);
}
funcMap[actionName] = data;
funcVector.push_back(std::pair<std::string, BoundFunctionData>(actionName, data));
// now fire a signal so we know a new function was bound (used by core scripts)
shared_ptr<Reflection::ValueTable> functionTable(Aya::make_shared<Reflection::ValueTable>());
(*functionTable)["title"] = Aya::Reflection::Variant();
(*functionTable)["image"] = Aya::Reflection::Variant();
(*functionTable)["description"] = Aya::Reflection::Variant();
(*functionTable)["createTouchButton"] = Aya::Reflection::Variant(createTouchButton);
if (hotkeys && !hotkeys->values.empty())
{
shared_ptr<Reflection::ValueArray> hotkeyArray = Aya::make_shared<Reflection::ValueArray>(hotkeys->values);
(*functionTable)["inputTypes"] = shared_ptr<const Aya::Reflection::ValueArray>(hotkeyArray);
}
boundActionAddedSignal(actionName, createTouchButton, functionTable);
}
void ContextActionService::bindCoreActionForInputTypes(
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys)
{
bindActionInternal(actionName, functionToBind, createTouchButton, hotkeys, coreFunctionMap, coreFunctionVector);
}
void ContextActionService::bindActionForInputTypes(
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys)
{
bindActionInternal(actionName, functionToBind, createTouchButton, hotkeys, functionMap, functionVector);
}
void ContextActionService::bindActivate(InputObject::UserInputType inputType, KeyCode keyCode)
{
if (!Network::Players::frontendProcessing(this))
{
throw Aya::runtime_error("ContextActionService:BindActivate can only be called from a local script");
}
if (inputType == InputObject::TYPE_NONE)
{
throw Aya::runtime_error("ContextActionService:BindActivate called with Enum.UserInputType.None, must be some other input type.");
}
unbindActivate(inputType, keyCode);
shared_ptr<Reflection::Tuple> activateTuple = Aya::make_shared<Reflection::Tuple>();
activateTuple->values.push_back(inputType);
if (keyCode != SDLK_UNKNOWN)
{
activateTuple->values.push_back(keyCode);
}
functionVector.push_back(std::pair<std::string, BoundFunctionData>(activateGuid, BoundFunctionData(activateTuple)));
}
void ContextActionService::unbindActivate(InputObject::UserInputType inputType, KeyCode keyCode)
{
if (!Network::Players::frontendProcessing(this))
{
throw Aya::runtime_error("ContextActionService:UnbindActivate can only be called from a local script");
}
if (inputType == InputObject::TYPE_NONE)
{
throw Aya::runtime_error("ContextActionService:UnbindActivate called with Enum.UserInputType.None, must be some other input type.");
}
for (FunctionVector::reverse_iterator iter = functionVector.rbegin(); iter != functionVector.rend(); ++iter)
{
if ((*iter).first == activateGuid)
{
Reflection::ValueArray currentInputTypes = (*iter).second.inputTypes->values;
if (!currentInputTypes.empty() && currentInputTypes.begin()->isType<InputObject::UserInputType>())
{
if (inputType == currentInputTypes.begin()->get<InputObject::UserInputType>())
{
functionVector.erase(--iter.base());
break;
}
}
}
}
}
FunctionMap::iterator ContextActionService::findAction(const std::string actionName)
{
FunctionMap::iterator iter = functionMap.find(actionName);
if (iter == functionMap.end())
{
StandardOut::singleton()->printf(MESSAGE_WARNING, "ContextActionService could not find the function passed in, doing nothing.");
}
return iter;
}
void ContextActionService::fireBoundActionChangedSignal(FunctionMap::iterator iter, const std::string& changeName)
{
if (iter == functionMap.end())
{
return;
}
shared_ptr<Reflection::ValueTable> functionTable = constructTableFromBoundFunctionData(iter->second);
(*functionTable)["position"] = iter->second.position;
boundActionChangedSignal((iter->first), changeName, functionTable);
}
void ContextActionService::setTitleForAction(const std::string actionName, std::string title)
{
FunctionMap::iterator iter = findAction(actionName);
if (iter == functionMap.end())
{
return;
}
iter->second.title = title;
fireBoundActionChangedSignal(iter, "title");
}
void ContextActionService::setDescForAction(const std::string actionName, std::string description)
{
FunctionMap::iterator iter = findAction(actionName);
if (iter == functionMap.end())
{
return;
}
iter->second.description = description;
fireBoundActionChangedSignal(iter, "description");
}
void ContextActionService::setImageForAction(const std::string actionName, std::string image)
{
FunctionMap::iterator iter = findAction(actionName);
if (iter == functionMap.end())
{
return;
}
iter->second.image = image;
fireBoundActionChangedSignal(iter, "image");
}
void ContextActionService::setPositionForAction(const std::string actionName, UDim2 position)
{
FunctionMap::iterator iter = findAction(actionName);
if (iter == functionMap.end())
{
return;
}
iter->second.position = position;
fireBoundActionChangedSignal(iter, "position");
}
void ContextActionService::getButton(
const std::string actionName, boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction)
{
FunctionMap::iterator iter = findAction(actionName);
if (iter == functionMap.end())
{
return;
}
yieldFunctionMap[actionName] = FunctionPair(resumeFunction, errorFunction);
getActionButtonSignal(actionName);
}
void ContextActionService::fireActionButtonFoundSignal(const std::string actionName, shared_ptr<Instance> actionButton)
{
try
{
yieldFunctionMap.at(actionName);
}
catch (std::exception& e)
{
StandardOut::singleton()->print(MESSAGE_ERROR, e.what());
return;
}
FunctionPair functionPair = yieldFunctionMap[actionName];
functionPair.first(actionButton);
}
shared_ptr<const Reflection::ValueTable> ContextActionService::getAllBoundActionData()
{
shared_ptr<Reflection::ValueTable> reflectedFuncMap = Aya::make_shared<Reflection::ValueTable>();
for (FunctionMap::iterator iter = functionMap.begin(); iter != functionMap.end(); ++iter)
{
if (iter != functionMap.end())
{
shared_ptr<const Reflection::ValueTable> functionInfo = getBoundActionData(iter->first);
Reflection::Variant variantValue = shared_ptr<const Aya::Reflection::ValueTable>(functionInfo);
(*reflectedFuncMap)[iter->first] = variantValue;
}
}
return reflectedFuncMap;
}
shared_ptr<const Reflection::ValueTable> ContextActionService::getBoundCoreActionData(const std::string actionName)
{
FunctionMap::iterator iter = coreFunctionMap.find(actionName);
if (iter != coreFunctionMap.end())
{
return constructTableFromBoundFunctionData(iter->second);
}
return Aya::make_shared<Reflection::ValueTable>();
}
shared_ptr<const Reflection::ValueTable> ContextActionService::getBoundActionData(const std::string actionName)
{
FunctionMap::iterator iter = functionMap.find(actionName);
if (iter != functionMap.end())
{
return constructTableFromBoundFunctionData(iter->second);
}
return Aya::make_shared<Reflection::ValueTable>();
}
namespace Reflection
{
template<>
EnumDesc<PlayerActionType>::EnumDesc()
: EnumDescriptor("PlayerActions")
{
addPair(CHARACTER_FORWARD, "CharacterForward");
addPair(CHARACTER_BACKWARD, "CharacterBackward");
addPair(CHARACTER_LEFT, "CharacterLeft");
addPair(CHARACTER_RIGHT, "CharacterRight");
addPair(CHARACTER_JUMP, "CharacterJump");
}
} // Namespace Reflection
} // Namespace Aya

View File

@@ -0,0 +1,228 @@
//
// ContextActionService.h
// App
//
// Created by Ben Tkacheff on 1/17/13.
//
// This service is used to figure out what actions a local player can do (right now this is just for tools, but could expand to several functions)
#pragma once
#include "Utility/TextureId.hpp"
#include "Utility/UDim.hpp"
#include "GUI/GuiEvent.hpp"
#include "Tree/Service.hpp"
#include "Script/ThreadRef.hpp"
#include "DataModel/InputObject.hpp"
namespace Aya
{
namespace Network
{
class Player;
}
class ModelInstance;
class Tool;
class GuiButton;
struct BoundFunctionData
{
std::string title;
std::string description;
std::string image;
UDim2 position;
bool hasTouchButton;
shared_ptr<const Reflection::Tuple> inputTypes;
boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction;
weak_ptr<InputObject> lastInput;
BoundFunctionData()
: image("")
, inputTypes()
, title("")
, description("")
, position()
, hasTouchButton(false)
{
}
BoundFunctionData(shared_ptr<const Reflection::Tuple> newInputTypes)
: inputTypes(newInputTypes)
, image("")
, title("")
, description("")
, position()
, hasTouchButton(false)
{
}
BoundFunctionData(
shared_ptr<const Reflection::Tuple> newInputTypes, boost::function<void(shared_ptr<Reflection::Tuple>)> newFunction, bool touchButton)
: inputTypes(newInputTypes)
, image("")
, title("")
, description("")
, position()
, luaFunction(newFunction)
, hasTouchButton(touchButton)
{
}
BoundFunctionData(boost::function<void(shared_ptr<Reflection::Tuple>)> newFunction, bool touchButton)
: inputTypes()
, image("")
, title("")
, description("")
, position()
, luaFunction(newFunction)
, hasTouchButton(touchButton)
{
}
friend bool operator==(const BoundFunctionData& lhs, const BoundFunctionData& rhs)
{
if (lhs.inputTypes && rhs.inputTypes)
{
return (lhs.inputTypes.get() == rhs.inputTypes.get()) && (lhs.image == rhs.image) && (lhs.title == rhs.title) &&
(lhs.description == rhs.description) && (lhs.position == rhs.position) && (lhs.hasTouchButton == rhs.hasTouchButton);
}
return (lhs.image == rhs.image) && (lhs.title == rhs.title) && (lhs.description == rhs.description) && (lhs.position == rhs.position) &&
(lhs.hasTouchButton == rhs.hasTouchButton);
}
};
typedef boost::unordered_map<std::string, BoundFunctionData> FunctionMap;
typedef std::vector<std::pair<std::string, BoundFunctionData>> FunctionVector;
typedef std::pair<boost::function<void(shared_ptr<Instance>)>, boost::function<void(std::string)>> FunctionPair;
typedef boost::unordered_map<std::string, FunctionPair> FunctionPairMap;
typedef enum
{
CHARACTER_FORWARD = 0,
CHARACTER_BACKWARD = 1,
CHARACTER_LEFT = 2,
CHARACTER_RIGHT = 3,
CHARACTER_JUMP = 4
} PlayerActionType;
extern const char* const sContextActionService;
class ContextActionService
: public DescribedNonCreatable<ContextActionService, Instance, sContextActionService>
, public Service
{
public:
ContextActionService();
Aya::signal<void(shared_ptr<Instance>)> equippedToolSignal;
Aya::signal<void(shared_ptr<Instance>)> unequippedToolSignal;
Aya::signal<void(std::string, std::string, shared_ptr<const Reflection::ValueTable>)> boundActionChangedSignal;
Aya::signal<void(std::string, bool, shared_ptr<const Reflection::ValueTable>)> boundActionAddedSignal;
Aya::signal<void(std::string, shared_ptr<const Reflection::ValueTable>)> boundActionRemovedSignal;
Aya::signal<void(std::string)> getActionButtonSignal;
Aya::signal<void(std::string, shared_ptr<Instance>)> actionButtonFoundSignal;
std::string getCurrentLocalToolIcon();
void bindCoreActionForInputTypes(
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys);
void unbindCoreAction(const std::string actionName);
void bindActionForInputTypes(
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys);
void unbindAction(const std::string actionName);
void unbindAll();
void bindActivate(InputObject::UserInputType inputType, KeyCode keyCode);
void unbindActivate(InputObject::UserInputType inputType, KeyCode keyCode);
void setTitleForAction(const std::string actionName, std::string title);
void setDescForAction(const std::string actionName, std::string description);
void setImageForAction(const std::string actionName, std::string image);
void setPositionForAction(const std::string actionName, UDim2 position);
void getButton(
const std::string actionName, boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
shared_ptr<const Reflection::ValueTable> getBoundCoreActionData(const std::string actionName);
shared_ptr<const Reflection::ValueTable> getBoundActionData(const std::string actionName);
shared_ptr<const Reflection::ValueTable> getAllBoundActionData();
void fireActionButtonFoundSignal(const std::string actionName, shared_ptr<Instance> actionButton);
//////////////////////////////////////////////////////////////
//
// Instance
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
//////////////////////////////////////////////////////////////
//
// Proccessing of input
//
GuiResponse processCoreBindings(const shared_ptr<InputObject>& inputObject);
GuiResponse processDevBindings(const shared_ptr<InputObject>& inputObject, bool menuIsOpen);
void callFunction(boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction, const std::string actionName,
const InputObject::UserInputState state, const shared_ptr<Instance> inputObject);
void callFunction(const std::string actionName, const InputObject::UserInputState state, const shared_ptr<Instance> inputObject);
protected:
Aya::signals::scoped_connection characterChildAddConnection;
Aya::signals::scoped_connection characterChildRemoveConnection;
Aya::signals::scoped_connection localPlayerAddConnection;
private:
typedef DescribedNonCreatable<ContextActionService, Instance, sContextActionService> Super;
// these are used for user context binds
FunctionMap functionMap;
FunctionVector functionVector;
// these are used for Aya context binds (For Aya menu, etc.)
FunctionMap coreFunctionMap;
FunctionVector coreFunctionVector;
FunctionPairMap yieldFunctionMap;
// used for binding activate (used to simulate mouse clicks on different inputs
std::string activateGuid;
boost::unordered_map<InputObject*, float> lastZPositionsForActivate;
Tool* getCurrentLocalTool();
Tool* isTool(shared_ptr<Instance> instance);
void checkForToolRemoval(shared_ptr<Instance> removedChildOfCharacter);
void checkForNewTool(shared_ptr<Instance> newChildOfCharacter);
void disconnectAllCharacterConnections();
void localCharacterAdded(shared_ptr<Instance> character);
void setupLocalCharacterConnections(ModelInstance* character);
void checkForLocalPlayer(shared_ptr<Instance> newPlayer);
void setupLocalPlayerConnections(Aya::Network::Player* localPlayer);
void bindActionInternal(const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton,
shared_ptr<const Reflection::Tuple> hotkeys, FunctionMap& funcMap, FunctionVector& funcVector);
FunctionMap::iterator findAction(const std::string actionName);
GuiResponse tryProcess(shared_ptr<InputObject> inputObject, FunctionVector& funcVector, bool menuIsOpen);
void processActivateAction(const shared_ptr<InputObject>& inputObject);
void fireBoundActionChangedSignal(FunctionMap::iterator iter, const std::string& changeName);
void checkForInputOverride(const Reflection::Variant& newInputType, const FunctionVector& funcVector);
};
} // namespace Aya

View File

@@ -0,0 +1,33 @@
#include "DataModel/CornerWedgeInstance.hpp"
#include "World/CornerWedgePoly.hpp"
#include "World/Primitive.hpp"
namespace Aya
{
const char* const sCornerWedge = "CornerWedgePart";
using namespace Reflection;
const char* category_CornerWedge = "Part ";
static const Vector3 InitialCornerWedgePartSize = Vector3(2.0, 2.0, 2.0);
CornerWedgeInstance::CornerWedgeInstance()
: DescribedCreatable<CornerWedgeInstance, PartInstance, sCornerWedge>(InitialCornerWedgePartSize)
{
setName("CornerWedge");
Primitive* myPrim = this->getPartPrimitive();
myPrim->setGeometryType(Geometry::GEOMETRY_CORNERWEDGE);
myPrim->setSurfaceType(NORM_X_NEG, NO_SURFACE);
myPrim->setSurfaceType(NORM_Y_NEG, NO_SURFACE);
myPrim->setSurfaceType(NORM_Y, NO_SURFACE);
shouldRenderSetDirty();
}
CornerWedgeInstance::~CornerWedgeInstance() {}
} // namespace Aya

View File

@@ -0,0 +1,26 @@
#pragma once
#include "DataModel/PartInstance.hpp"
#include "DataModel/BasicPartInstance.hpp"
#include "Reflection/Reflection.hpp"
namespace Aya
{
extern const char* const sCornerWedge;
class CornerWedgeInstance : public DescribedCreatable<CornerWedgeInstance, PartInstance, sCornerWedge>
{
public:
CornerWedgeInstance();
~CornerWedgeInstance();
/*override*/ virtual PartType getPartType() const
{
return CORNERWEDGE_PART;
}
};
} // namespace Aya

View File

@@ -0,0 +1,26 @@
#include "DataModel/CustomEvent.hpp"
#include "Reflection/Reflection.hpp"
namespace Aya
{
const char* const sCustomEvent = "CustomEvent";
Reflection::PropDescriptor<CustomEvent, float> CustomEvent::prop_PersistedCurrentValue("PersistedCurrentValue", category_Data,
&CustomEvent::getPersistedCurrentValue, &CustomEvent::setPersistedCurrentValue, Reflection::PropertyDescriptor::STREAMING, Security::None);
Reflection::BoundFuncDesc<CustomEvent, void(float)> CustomEvent::func_SetValue(&CustomEvent::setCurrentValue, "SetValue", "newValue", Security::None);
Reflection::BoundFuncDesc<CustomEvent, shared_ptr<const Instances>()> CustomEvent::func_GetAttachedReceivers(
&CustomEvent::getAttachedReceivers, "GetAttachedReceivers", Security::None);
Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> CustomEvent::event_ReceiverConnected(
&CustomEvent::receiverConnected, "ReceiverConnected", "receiver", Security::None);
Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> CustomEvent::event_ReceiverDisconnected(
&CustomEvent::receiverDisconnected, "ReceiverDisconnected", "receiver", Security::None);
REFLECTION_END();
} // namespace Aya

View File

@@ -0,0 +1,165 @@
#pragma once
#include "signal.hpp"
#include "DataModel/CustomEventReceiver.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/ModelInstance.hpp"
#include "DataModel/CollectionService.hpp"
#include "Tree/Instance.hpp"
namespace Aya
{
extern const char* const sCustomEvent;
class CustomEvent : public DescribedCreatable<CustomEvent, Instance, sCustomEvent>
{
private:
typedef DescribedCreatable<CustomEvent, Instance, sCustomEvent> Super;
typedef std::list<weak_ptr<CustomEventReceiver>> ReceiverList;
// prevent copy and assign: this class does sensitive pointer
// management, which would be complicated by allowing copies/assigns.
CustomEvent(const CustomEvent& other);
CustomEvent& operator=(const CustomEvent& other);
ReceiverList receivers;
float currentValue;
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
if (oldProvider)
oldProvider->create<CollectionService>()->removeInstance(shared_from(this));
Super::onServiceProvider(oldProvider, newProvider);
if (newProvider)
{
newProvider->create<CollectionService>()->addInstance(shared_from(this));
}
else
{
ReceiverList copy = receivers;
for (ReceiverList::iterator itr = copy.begin(); itr != copy.end(); ++itr)
{
shared_ptr<CustomEventReceiver> receiver = itr->lock();
if (receiver)
{
receiver->setSource(NULL);
}
}
}
}
public:
CustomEvent()
: Super(sCustomEvent)
, currentValue(0)
{
}
// The persisted current value is just for storage -- it is set from the
// SetValue callback, and setPersistedCurrentValue _WILL_NOT_ cause
// receivers to get a value update.
static Reflection::PropDescriptor<CustomEvent, float> prop_PersistedCurrentValue;
static Reflection::BoundFuncDesc<CustomEvent, void(float)> func_SetValue;
static Reflection::BoundFuncDesc<CustomEvent, shared_ptr<const Instances>()> func_GetAttachedReceivers;
static Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> event_ReceiverConnected;
static Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> event_ReceiverDisconnected;
// signals are public for testing only
Aya::signal<void(shared_ptr<Instance>)> receiverConnected;
Aya::signal<void(shared_ptr<Instance>)> receiverDisconnected;
/*override*/ virtual bool askSetParent(const Instance* instance) const
{
return Instance::fastDynamicCast<PartInstance>(instance) != NULL;
}
/*override*/ virtual bool askForbidChild(const Instance* instance) const
{
return true;
}
float getPersistedCurrentValue() const
{
return currentValue;
}
// This method is intended for serialization only, it should not be involved
// with triggering valueChanged events on receivers.
void setPersistedCurrentValue(float newValue)
{
currentValue = G3D::clamp(newValue, 0, 1);
}
void setCurrentValue(float newValue)
{
currentValue = G3D::clamp(newValue, 0, 1);
raiseChanged(prop_PersistedCurrentValue);
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end(); ++itr)
{
shared_ptr<CustomEventReceiver> receiver = itr->lock();
if (receiver && receiver->getCurrentValue() != currentValue)
{
receiver->sourceValueChanged(currentValue);
}
}
}
shared_ptr<const Instances> getAttachedReceivers()
{
shared_ptr<Instances> result(new Instances);
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end(); ++itr)
{
shared_ptr<CustomEventReceiver> receiver = itr->lock();
if (receiver)
{
result->push_back(receiver);
}
}
return result;
}
void addReceiver(CustomEventReceiver* receiver)
{
bool found = false;
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end() && !found; ++itr)
{
shared_ptr<CustomEventReceiver> list_receiver = itr->lock();
if (list_receiver.get() == receiver)
{
found = true;
}
}
if (!found)
{
receivers.push_back(weak_ptr<CustomEventReceiver>(shared_from(receiver)));
// send event only after internal state has been updated
receiverConnected(shared_from(receiver));
}
}
void removeReceiver(CustomEventReceiver* receiver)
{
ReceiverList::iterator foundIterator;
bool found = false;
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end() && !found; ++itr)
{
shared_ptr<CustomEventReceiver> list_receiver = itr->lock();
if (list_receiver.get() == receiver)
{
foundIterator = itr;
found = true;
}
}
if (found)
{
receivers.erase(foundIterator);
// send event only after internal state has been updated
receiverDisconnected(shared_from(receiver));
}
}
};
} // namespace Aya

View File

@@ -0,0 +1,100 @@
#include "DataModel/CustomEventReceiver.hpp"
#include "DataModel/CustomEvent.hpp"
#include "DataModel/CollectionService.hpp"
#include "Reflection/Reflection.hpp"
namespace Aya
{
const char* const sCustomEventReceiver = "CustomEventReceiver";
Reflection::RefPropDescriptor<CustomEventReceiver, Instance> CustomEventReceiver::prop_Source("Source", category_Data,
&CustomEventReceiver::getSource, &CustomEventReceiver::setSource, Reflection::PropertyDescriptor::STANDARD, Security::None);
Reflection::EventDesc<CustomEventReceiver, void(float)> CustomEventReceiver::event_SourceValueChanged(
&CustomEventReceiver::sourceValueChanged, "SourceValueChanged", "newValue", Security::None);
Reflection::BoundFuncDesc<CustomEventReceiver, float()> CustomEventReceiver::func_GetCurrentValue(
&CustomEventReceiver::getCurrentValue, "GetCurrentValue", Security::None);
Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> CustomEventReceiver::event_EventConnected(
&CustomEventReceiver::eventConnected, "EventConnected", "event", Security::None);
Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> CustomEventReceiver::event_EventDisconnected(
&CustomEventReceiver::eventDisconnected, "EventDisconnected", "event", Security::None);
REFLECTION_END();
void CustomEventReceiver::setSource(Instance* sourceEvent)
{
shared_ptr<CustomEvent> currentSourceEvent = this->sourceEvent.lock();
if (sourceEvent == currentSourceEvent.get())
{
return;
}
// disconnect from old sourceEvent if this was connected before
if (currentSourceEvent.get() != NULL)
{
currentSourceEvent->removeReceiver(this);
this->sourceEvent.reset();
// send event only after internal state has been updated
eventDisconnected(currentSourceEvent);
}
// connect to the new sourceEvent if it is not NULL
if (sourceEvent != NULL)
{
CustomEvent* castSource = Instance::fastDynamicCast<CustomEvent>(sourceEvent);
debugAssert(castSource != NULL);
if (castSource != NULL)
{
castSource->addReceiver(this);
this->sourceEvent = shared_from(castSource);
if (getCurrentValue() != castSource->getPersistedCurrentValue())
{
sourceValueChanged(castSource->getPersistedCurrentValue());
}
// send event only after internal state is updated
eventConnected(shared_from(castSource));
}
}
if (sourceEvent == NULL && getCurrentValue() != 0)
{
sourceValueChanged(0);
}
raisePropertyChanged(prop_Source);
}
void CustomEventReceiver::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
if (oldProvider)
oldProvider->create<CollectionService>()->removeInstance(shared_from(this));
Super::onServiceProvider(oldProvider, newProvider);
if (newProvider)
{
newProvider->create<CollectionService>()->addInstance(shared_from(this));
}
else
{
setSource(NULL);
}
}
} // namespace Aya
// Randomized Locations for hackflags
namespace Aya
{
namespace Security
{
unsigned int hackFlag4 = 0;
};
}; // namespace Aya

View File

@@ -0,0 +1,84 @@
#pragma once
#include "signal.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/ModelInstance.hpp"
#include "Tree/Instance.hpp"
namespace Aya
{
class CustomEvent;
extern const char* const sCustomEventReceiver;
class CustomEventReceiver : public DescribedCreatable<CustomEventReceiver, Instance, sCustomEventReceiver>
{
private:
typedef DescribedCreatable<CustomEventReceiver, Instance, sCustomEventReceiver> Super;
// prevent copy and assign: this class does sensitive pointer
// management, which would be complicated by allowing copies/assigns.
CustomEventReceiver(const CustomEventReceiver& other);
CustomEventReceiver& operator=(const CustomEventReceiver& other);
weak_ptr<CustomEvent> sourceEvent;
Aya::signals::scoped_connection sourceValueChangedConnection;
float lastReceivedValue;
public:
// public for interoperation with CustomEvent
Aya::signal<void(float)> sourceValueChanged;
// connection signals public for testing
Aya::signal<void(shared_ptr<Instance>)> eventConnected;
Aya::signal<void(shared_ptr<Instance>)> eventDisconnected;
CustomEventReceiver()
: Super(sCustomEventReceiver)
, lastReceivedValue(0)
{
sourceValueChangedConnection = sourceValueChanged.connect(boost::bind(&CustomEventReceiver::setCurrentValue, this, _1));
}
static Reflection::RefPropDescriptor<CustomEventReceiver, Instance> prop_Source;
static Reflection::EventDesc<CustomEventReceiver, void(float)> event_SourceValueChanged;
static Reflection::BoundFuncDesc<CustomEventReceiver, float()> func_GetCurrentValue;
static Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> event_EventConnected;
static Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> event_EventDisconnected;
/*override*/ bool askSetParent(const Instance* instance) const
{
return Instance::fastDynamicCast<PartInstance>(instance) != NULL;
}
/*override*/ bool askForbidChild(const Instance* instance) const
{
return true;
}
// needs to be forward declared because it depends on CustomEvent
/*override*/ // virtual void onAncestorChanged(const AncestorChanged& event);
// should only be used for serialization
Instance* const getSource() const
{
return (Instance*)sourceEvent.lock().get();
}
// should only be used for serialization
void setSource(Instance* sourceEvent);
void setCurrentValue(float newValue)
{
AYAASSERT(newValue != lastReceivedValue);
lastReceivedValue = newValue;
}
float getCurrentValue()
{
return lastReceivedValue;
}
protected:
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
};
} // namespace Aya

View File

@@ -0,0 +1,275 @@
#include "DataModel/CustomParticleEmitter.hpp"
#define CAT_VIS category_Appearance
#define CAT_EMISSION "Emission"
#define CAT_MOTION "Motion"
#define CAT_PARTICLE_BEHAVIOR "Particles"
DYNAMIC_FASTFLAGVARIABLE(CustomEmitterInstanceEnabled, false)
DYNAMIC_FASTFLAGVARIABLE(EnableParticleDrag, false)
namespace Aya
{
const char* const sParticleEmitter = "ParticleEmitter";
CustomParticleEmitter::CustomParticleEmitter()
: DescribedCreatable<CustomParticleEmitter, Instance, sParticleEmitter>("ParticleEmitter")
, texture("ayaasset://textures/particles/sparkles_main.jxl")
, color(Color3(1, 1, 1))
, transparency(0)
, size(1)
, lightEmission(0)
, enabled(true)
, rate(20)
, speed(5, 5)
, spread(0)
, rotation(0, 0)
, rotSpeed(0, 0)
, lifetime(5, 10)
, accel(0, 0, 0)
, zOffset(0)
, lockedToLocalSpace(false)
, dampening(0)
, velocityInheritance(0)
, emissionDirection(Aya::NORM_Y)
{
}
CustomParticleEmitter::~CustomParticleEmitter() {}
// static Reflection::BoundProp<> prop_( "", CAT_VIS, &ParticleEmitter::, &ParticleEmitter:: );
Reflection::PropDescriptor<CustomParticleEmitter, TextureId> CustomParticleEmitter::prop_texture(
"Texture", CAT_VIS, &CustomParticleEmitter::getTexture, &CustomParticleEmitter::setTexture);
Reflection::PropDescriptor<CustomParticleEmitter, ColorSequence> CustomParticleEmitter::prop_color(
"Color", CAT_VIS, &CustomParticleEmitter::getColor, &CustomParticleEmitter::setColor);
Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> CustomParticleEmitter::prop_transp(
"Transparency", CAT_VIS, &CustomParticleEmitter::getTransparency, &CustomParticleEmitter::setTransparency);
Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> CustomParticleEmitter::prop_size(
"Size", CAT_VIS, &CustomParticleEmitter::getSize, &CustomParticleEmitter::setSize);
Reflection::PropDescriptor<CustomParticleEmitter, bool> CustomParticleEmitter::prop_enabled(
"Enabled", CAT_EMISSION, &CustomParticleEmitter::getEnabled, &CustomParticleEmitter::setEnabled);
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_lightEmission(
"LightEmission", CAT_VIS, &CustomParticleEmitter::getLightEmission, &CustomParticleEmitter::setLightEmission);
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_rate(
"Rate", CAT_EMISSION, &CustomParticleEmitter::getRate, &CustomParticleEmitter::setRate);
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_speed(
"Speed", CAT_EMISSION, &CustomParticleEmitter::getSpeed, &CustomParticleEmitter::setSpeed);
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_spread(
"VelocitySpread", CAT_EMISSION, &CustomParticleEmitter::getSpread, &CustomParticleEmitter::setSpread);
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_rotation(
"Rotation", CAT_EMISSION, &CustomParticleEmitter::getRotation, &CustomParticleEmitter::setRotation);
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_rotSpeed(
"RotSpeed", CAT_EMISSION, &CustomParticleEmitter::getRotSpeed, &CustomParticleEmitter::setRotSpeed);
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_lifetime(
"Lifetime", CAT_EMISSION, &CustomParticleEmitter::getLifetime, &CustomParticleEmitter::setLifetime);
Reflection::PropDescriptor<CustomParticleEmitter, Vector3> CustomParticleEmitter::prop_accel(
"Acceleration", CAT_MOTION, &CustomParticleEmitter::getAccel, &CustomParticleEmitter::setAccel);
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_zOffset(
"ZOffset", CAT_VIS, &CustomParticleEmitter::getZOffset, &CustomParticleEmitter::setZOffset);
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_velocityInheritance(
"VelocityInheritance", CAT_PARTICLE_BEHAVIOR, &CustomParticleEmitter::getVelocityInheritance, &CustomParticleEmitter::setVelocityInheritance);
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_dampening(
"Drag", CAT_PARTICLE_BEHAVIOR, &CustomParticleEmitter::getDampening, &CustomParticleEmitter::setDampening);
Reflection::PropDescriptor<CustomParticleEmitter, bool> CustomParticleEmitter::prop_lockedToLocalSpace(
"LockedToPart", CAT_PARTICLE_BEHAVIOR, &CustomParticleEmitter::getLockedToLocalSpace, &CustomParticleEmitter::setLockedToLocalSpace);
Reflection::EnumPropDescriptor<CustomParticleEmitter, NormalId> CustomParticleEmitter::prop_emissionDirection(
"EmissionDirection", CAT_EMISSION, &CustomParticleEmitter::getEmissionDirection, &CustomParticleEmitter::setEmissionDirection);
Reflection::BoundFuncDesc<CustomParticleEmitter, void(int)> CustomParticleEmitter::desc_burst(
&CustomParticleEmitter::requestBurst, "Emit", "particleCount", 16, Security::None);
Reflection::RemoteEventDesc<CustomParticleEmitter, void(int)> CustomParticleEmitter::event_onEmitRequested(&CustomParticleEmitter::onEmitRequested,
"OnEmitRequested", "particleCount", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
REFLECTION_END();
void CustomParticleEmitter::setEnabled(bool v)
{
if (enabled == v)
return;
enabled = v;
raisePropertyChanged(prop_enabled);
}
void CustomParticleEmitter::setLightEmission(float v)
{
if (lightEmission == v)
return;
lightEmission = v;
raisePropertyChanged(prop_lightEmission);
}
void CustomParticleEmitter::setRate(float v)
{
if (rate == v)
return;
rate = v;
raisePropertyChanged(prop_rate);
}
void CustomParticleEmitter::setSpeed(const NumberRange& v)
{
if (speed == v)
return;
speed = v;
raisePropertyChanged(prop_speed);
}
void CustomParticleEmitter::setSpread(float v)
{
if (spread == v)
return;
spread = v;
raisePropertyChanged(prop_spread);
}
void CustomParticleEmitter::setRotation(const NumberRange& v)
{
if (rotation == v)
return;
rotation = v;
raisePropertyChanged(prop_rotation);
}
void CustomParticleEmitter::setRotSpeed(const NumberRange& v)
{
if (rotSpeed == v)
return;
rotSpeed = v;
raisePropertyChanged(prop_rotSpeed);
}
void CustomParticleEmitter::setLifetime(const NumberRange& v)
{
if (lifetime == v)
return;
lifetime = v;
raisePropertyChanged(prop_lifetime);
}
void CustomParticleEmitter::setAccel(const Vector3& v)
{
if (accel == v)
return;
accel = v;
raisePropertyChanged(prop_accel);
}
void CustomParticleEmitter::setZOffset(float v)
{
if (zOffset == v)
return;
zOffset = v;
raisePropertyChanged(prop_zOffset);
}
const TextureId& CustomParticleEmitter::getTexture() const
{
return texture;
}
void CustomParticleEmitter::setTexture(const TextureId& id)
{
if (texture == id)
return;
texture = id;
raisePropertyChanged(prop_texture);
}
const ColorSequence& CustomParticleEmitter::getColor() const
{
return color;
}
void CustomParticleEmitter::setColor(const ColorSequence& val)
{
if (color == val)
return;
color = val;
raisePropertyChanged(prop_color);
}
const NumberSequence& CustomParticleEmitter::getTransparency() const
{
return transparency;
}
void CustomParticleEmitter::setTransparency(const NumberSequence& v)
{
if (transparency == v)
return;
transparency = v;
raisePropertyChanged(prop_transp);
}
const NumberSequence& CustomParticleEmitter::getSize() const
{
return size;
}
void CustomParticleEmitter::setSize(const NumberSequence& val)
{
if (size == val)
return;
size = val;
raisePropertyChanged(prop_size);
}
void CustomParticleEmitter::setVelocityInheritance(float value)
{
if (value != velocityInheritance)
{
velocityInheritance = value;
raisePropertyChanged(prop_velocityInheritance);
}
}
void CustomParticleEmitter::setDampening(float value)
{
if (!DFFlag::EnableParticleDrag)
{
raisePropertyChanged(prop_dampening);
return;
}
if (value != dampening)
{
dampening = value;
raisePropertyChanged(prop_dampening);
}
}
void CustomParticleEmitter::setLockedToLocalSpace(bool value)
{
if (value != lockedToLocalSpace)
{
lockedToLocalSpace = value;
raisePropertyChanged(prop_lockedToLocalSpace);
}
}
void CustomParticleEmitter::setEmissionDirection(NormalId value)
{
if (value != emissionDirection)
{
emissionDirection = value;
raisePropertyChanged(prop_emissionDirection);
}
}
void CustomParticleEmitter::requestBurst(int value)
{
event_onEmitRequested.fireAndReplicateEvent(this, value);
}
void CustomParticleEmitter::onAncestorChanged(const AncestorChanged& ev)
{
Base::onAncestorChanged(ev);
}
} // namespace Aya

View File

@@ -0,0 +1,178 @@
#pragma once
#include "Tree/Instance.hpp"
#include "DataModel/Effect.hpp"
#include "DataModel/PartInstance.hpp"
#include "Utility/TextureId.hpp"
#include "DataModel/NumberSequence.hpp"
#include "DataModel/ColorSequence.hpp"
#include "DataModel/NumberRange.hpp"
namespace Aya
{
extern const char* const sParticleEmitter;
class CustomParticleEmitter
: public DescribedCreatable<CustomParticleEmitter, Instance, sParticleEmitter>
, public Effect
{
typedef DescribedCreatable<CustomParticleEmitter, Instance, sParticleEmitter> Base;
public:
CustomParticleEmitter();
virtual ~CustomParticleEmitter();
static Reflection::PropDescriptor<CustomParticleEmitter, TextureId> prop_texture;
static Reflection::PropDescriptor<CustomParticleEmitter, ColorSequence> prop_color;
static Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> prop_transp;
static Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> prop_size;
static Reflection::PropDescriptor<CustomParticleEmitter, bool> prop_enabled;
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_lightEmission;
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_rate;
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_speed;
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_spread;
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_rotation;
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_rotSpeed;
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_lifetime;
static Reflection::PropDescriptor<CustomParticleEmitter, Vector3> prop_accel;
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_zOffset;
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_velocityInheritance;
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_dampening;
static Reflection::PropDescriptor<CustomParticleEmitter, bool> prop_lockedToLocalSpace;
static Reflection::EnumPropDescriptor<CustomParticleEmitter, NormalId> prop_emissionDirection;
static Reflection::RemoteEventDesc<CustomParticleEmitter, void(int)> event_onEmitRequested;
static Reflection::BoundFuncDesc<CustomParticleEmitter, void(int)> desc_burst;
bool getEnabled() const
{
return enabled;
}
float getLightEmission() const
{
return lightEmission;
}
float getRate() const
{
return rate;
}
const NumberRange& getSpeed() const
{
return speed;
}
float getSpread() const
{
return spread;
}
const NumberRange& getRotation() const
{
return rotation;
}
const NumberRange& getRotSpeed() const
{
return rotSpeed;
}
const NumberRange& getLifetime() const
{
return lifetime;
}
const Vector3& getAccel() const
{
return accel;
}
float getZOffset() const
{
return zOffset;
}
void setEnabled(bool v);
void setLightEmission(float v);
void setRate(float v);
void setSpeed(const NumberRange& v);
void setSpread(float v);
void setRotation(const NumberRange& v);
void setRotSpeed(const NumberRange& v);
void setLifetime(const NumberRange& v);
void setAccel(const Vector3& v);
void setZOffset(float v);
Aya::remote_signal<void(int)> onEmitRequested;
const TextureId& getTexture() const;
void setTexture(const TextureId& id);
const NumberSequence& getTransparency() const;
void setTransparency(const NumberSequence& v);
const ColorSequence& getColor() const;
void setColor(const ColorSequence& val);
const NumberSequence& getSize() const;
void setSize(const NumberSequence& val);
float getVelocityInheritance() const
{
return velocityInheritance;
}
void setVelocityInheritance(float value);
float getDampening() const
{
return dampening;
}
void setDampening(float value);
bool getLockedToLocalSpace() const
{
return lockedToLocalSpace;
}
void setLockedToLocalSpace(bool value);
void requestBurst(int value);
NormalId getEmissionDirection() const
{
return emissionDirection;
}
void setEmissionDirection(NormalId value);
private:
TextureId texture;
ColorSequence color;
NumberSequence transparency;
NumberSequence size;
bool enabled;
float lightEmission;
float rate;
NumberRange speed;
float spread;
NumberRange rotation;
NumberRange rotSpeed;
NumberRange lifetime;
Vector3 accel;
float zOffset;
float velocityInheritance;
float dampening;
bool lockedToLocalSpace;
NormalId emissionDirection;
virtual bool askSetParent(const Instance* parent) const
{
return Instance::fastDynamicCast<PartInstance>(parent) != NULL;
}
virtual bool askAddChild(const Instance* instance) const
{
return false;
}
virtual void onAncestorChanged(const AncestorChanged& event);
};
} // namespace Aya

View File

@@ -0,0 +1,8 @@
#include "DataModel/CylinderMesh.hpp"
using namespace Aya;
const char* const Aya::sCylinderMesh = "CylinderMesh";

View File

@@ -0,0 +1,15 @@
#pragma once
#include "BevelMesh.hpp"
namespace Aya
{
extern const char* const sCylinderMesh;
class CylinderMesh : public DescribedCreatable<CylinderMesh, BevelMesh, sCylinderMesh>
{
public:
CylinderMesh() {}
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,873 @@
#pragma once
#include "GUI/GUI.hpp"
#include "DataModel/DataModelJob.hpp"
#include "DataModel/PhysicsInstructions.hpp"
#include "DataModel/Game.hpp"
#include "DataModel/InputObject.hpp"
#include "Tree/Instance.hpp"
#include "Tree/Service.hpp"
#include "Utility/GameMode.hpp"
#include "Utility/RunStateOwner.hpp"
#include "Utility/InsertMode.hpp"
#include "Utility/Region2.hpp"
#include "Utility/IMetric.hpp"
#include "Utility/HeapValue.hpp"
#include "Security/FuzzyTokens.hpp"
#include "DataModel/DebugMenu.hpp"
#include "DataModel/GameBasicSettings.hpp"
#include <boost/array.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>
#include <boost/unordered_set.hpp>
class XmlElement;
namespace Aya
{
class Fonts;
class GuiRoot;
class GuiItem;
class ContentProvider;
class Hopper;
class PlayerHopper;
class StarterPackService;
class StarterGuiService;
class StarterPlayerService;
class CoreGuiService;
class Workspace;
class Adorn;
class Region2;
class UserInputService;
class ContextActionService;
class GuiObject;
extern const char* const sDataModel;
static inline void robloxScriptModifiedCheck(Aya::Security::Permissions perm)
{
#ifndef AYA_STUDIO
Aya::Security::Context::current().requirePermission(Aya::Security::RobloxScript);
if (perm != Aya::Security::RobloxScript)
{
throw std::runtime_error("");
}
#endif
}
class DataModel
: public IMetric
, public IDataState
, public VerbContainer
, public Diagnostics::Countable<DataModel>
, public DataModelArbiter
, public DescribedNonCreatable<DataModel, ServiceProvider, sDataModel>
{
public:
bool shouldShowLoadingScreen = true;
GameBasicSettings::VirtualVersion initialVirtualVersion = GameBasicSettings::VERSION_2016;
enum CreatorType
{
CREATOR_USER = 0,
CREATOR_GROUP = 1
};
// genre == GENRE_ALL || (allowedGenres & (1 << (genre-1))) != 0;
enum Genre
{
GENRE_ALL = 0,
GENRE_TOWN_AND_CITY = 1,
GENRE_FANTASY = 2,
GENRE_SCI_FI = 3,
GENRE_NINJA = 4,
GENRE_SCARY = 5,
GENRE_PIRATE = 6,
GENRE_ADVENTURE = 7,
GENRE_SPORTS = 8,
GENRE_FUNNY = 9,
GENRE_WILD_WEST = 10,
GENRE_WAR = 11,
GENRE_SKATE_PARK = 12,
GENRE_TUTORIAL = 13,
};
enum GearGenreSetting
{
GEAR_GENRE_ALL = 0,
GEAR_GENRE_MATCH = 1,
};
//(allowedGearTypes & (1 << gearType)) != 0;
enum GearType
{
GEAR_TYPE_MELEE_WEAPONS = 0,
GEAR_TYPE_RANGED_WEAPONS = 1,
GEAR_TYPE_EXPLOSIVES = 2,
GEAR_TYPE_POWER_UPS = 3,
GEAR_TYPE_NAVIGATION_ENHANCERS = 4,
GEAR_TYPE_MUSICAL_INSTRUMENTS = 5,
GEAR_TYPE_SOCIAL_ITEMS = 6,
GEAR_TYPE_BUILDING_TOOLS = 7,
GEAR_TYPE_PERSONAL_TRANSPORT = 8,
};
enum RequestShutdownResult
{
CLOSE_NOT_HANDLED = 0,
CLOSE_REQUEST_HANDLED = 1,
CLOSE_LOCAL_SAVE = 2,
CLOSE_NO_SAVE_NEEDED = 3,
};
void postCreate();
static Aya::atomic<int> count;
std::auto_ptr<Aya::Verb> lockVerb;
Aya::signal<void(bool)> graphicsQualityShortcutSignal;
Aya::signal<void()> allowedGearTypeChanged;
Aya::signal<void(const shared_ptr<InputObject>& event)> InputObjectProcessed;
Aya::signal<void()> workspaceLoadedSignal;
Aya::signal<void()> gameLoadedSignal;
Genre getGenre() const
{
return genre;
}
void setGenre(Genre genre);
GearGenreSetting getGearGenreSetting() const
{
return gearGenreSetting;
}
void setGear(GearGenreSetting gearGenreSetting, int allowedGearTypes);
void setVIPServerId(std::string value);
std::string getVIPServerId() const;
void setVIPServerOwnerId(int value);
int getVIPServerOwnerId() const;
void setRenderGuisActive(bool value)
{
renderGuisActive = value;
}
bool isGearTypeAllowed(GearType gearType);
static void setLoaderFunction(boost::function<void(Aya::DataModel*)> loader)
{
loaderFunc = loader;
}
void loadPlugins();
struct MouseStats
{
RunningAverageTimeInterval<> osMouseMove;
RunningAverageTimeInterval<> mouseMove;
MouseStats()
: osMouseMove(0.5)
, mouseMove(0.5)
{
}
};
MouseStats mouseStats;
bool getSharedSuppressNavKeys() const
{
return (game ? game->getSuppressNavKeys() : suppressNavKeys);
}
void setGame(Aya::Game* newGame)
{
game = newGame;
}
bool getIsShuttingDown()
{
return isShuttingDown;
}
void setIsShuttingDown(bool value)
{
isShuttingDown = value;
}
private:
typedef DescribedNonCreatable<DataModel, ServiceProvider, sDataModel> Super;
ThrottlingHelper savePlaceThrottle;
// TODO: turn some of these into non-essential Services? Or at least, see if we can
// get rid of the direct references here...
shared_ptr<CoreGuiService> coreGuiService;
shared_ptr<ContextActionService> contextActionService;
shared_ptr<StarterPackService> starterPackService;
shared_ptr<StarterGuiService> starterGuiService;
shared_ptr<StarterPlayerService> starterPlayerService;
shared_ptr<RunService> runService;
shared_ptr<UserInputService> userInputService;
shared_ptr<Workspace> workspace;
shared_ptr<GuiRoot> guiRoot;
bool forceArrowCursor;
virtual GuiResponse processAccelerators(const shared_ptr<InputObject>& event);
virtual GuiResponse processCameraCommands(const shared_ptr<InputObject>& event);
std::string uiMessage; // short term hack - will improve
int drawId; // flag to debug extra draws
IMetric* tempMetric; // for output in ctrl-F1 message - only used during render call
IMetric* networkMetric; // for output in ctrl-F1 message - only used during render call
volatile bool dirty; // For IDataState
bool isInitialized; // Block gui events when closing - per crash reports on 12/05/07
bool isContentLoaded;
volatile bool isShuttingDown;
boost::mutex hackFlagSetMutex;
boost::unordered_set<unsigned int> hackFlagSet;
PhysicsInstructions physicsInstructions;
int numPartInstances;
int numInstances;
int shutdownRequestedCount;
int physicsStepID;
int totalWorldSteps;
bool isGameLoaded;
bool forceR15;
bool canRequestUniverseInfo;
bool universeDataRequested;
void requestGameStartInfo();
bool isPersonalServer;
bool networkStatsWindowsOn;
bool renderGuisActive;
Aya::Game* game;
Time dataModelInitTime;
TextureProxyBaseRef renderCursor;
void initializeContents(bool startHeartbeat);
void doDataModelStep(float timeInterval);
bool updatePhysicsInstructions(Network::GameMode gameMode);
bool onlyJobsLeftForThisArbiterAreGenericJobs();
static boost::function<void(Aya::DataModel*)> loaderFunc;
bool areCoreScriptsLoaded;
std::auto_ptr<std::istream> loadAssetIdIntoStream(int assetID);
public:
static bool BlockingDataModelShutdown;
static unsigned int perfStats; // another bitmask used to record detected hacks
std::string jobId;
static unsigned int sendStats; // legacy bitmask used to record detected hacks
std::string getJobId() const
{
return jobId;
};
Time getDataModelInitTime() const
{
return dataModelInitTime;
}
static std::string hash; // set to the hash of the client exe
//-----------------------------------
// Concurrency violation detection
// TODO: Merge with readwrite_concurrency_catcher class
class scoped_write_request
{
DataModel* const dataModel;
public:
// Place this code around tasks that write to a DataModel
scoped_write_request(Instance* context);
~scoped_write_request();
};
class scoped_read_request
{
DataModel* const dataModel;
public:
// Place this code around tasks that read a DataModel
scoped_read_request(Instance* context);
~scoped_read_request();
};
class scoped_write_transfer
{
DataModel* const dataModel;
unsigned int oldWritingThread;
public:
// Place this code around tasks that write to a DataModel and expect some other task to hold the write lock for them
scoped_write_transfer(Instance* context);
~scoped_write_transfer();
};
friend class scoped_write_request;
friend class scoped_read_request;
volatile long write_requested;
volatile long read_requested;
volatile DWORD writeRequestingThread;
bool currentThreadHasWriteLock() const;
static bool currentThreadHasWriteLock(Instance* context);
void setNetworkStatsWindowsOn(bool val)
{
networkStatsWindowsOn = val;
}
friend class Game;
//
//-----------------------------------
// It is important for addHackFlag to be inlined for security reasons.
// If this function is not inlined, it provides a single point of attack.
// Also inlining the code (along with VMProtect) provides more obscurity.
#ifdef WIN32
__forceinline
#else
inline
#endif
void
addHackFlag(unsigned int flag)
{
boost::mutex::scoped_lock l(hackFlagSetMutex);
hackFlagSet.insert(flag);
sendStats |= flag;
}
#ifdef WIN32
__forceinline
#else
inline
#endif
void
removeHackFlag(unsigned int flag)
{
boost::mutex::scoped_lock l(hackFlagSetMutex);
hackFlagSet.erase(flag);
sendStats &= ~flag;
}
#ifdef WIN32
__forceinline
#else
inline
#endif
bool
isHackFlagSet(unsigned int flag)
{
boost::mutex::scoped_lock l(hackFlagSetMutex);
return hackFlagSet.find(flag) != hackFlagSet.end() || (sendStats & flag);
}
unsigned int allHackFlagsOredTogether();
weak_ptr<GuiObject> getMouseOverInteractable() const
{
return mouseOverInteractable;
}
bool getMouseOverGui() const
{
return mouseOverGui;
}
void processWorkspaceEvent(const shared_ptr<InputObject>& event);
bool isClosed() const
{
return !isInitialized;
}
void setSuppressNavKeys(bool value)
{
suppressNavKeys = value;
}
static bool throttleAt30Fps; // for debugging/benchmarking - default is false;
// This event is fired anytime anything in the DataModel changes
Aya::signal<void(shared_ptr<Instance>, const Reflection::PropertyDescriptor*)> itemChangedSignal;
void clearContents(bool resettingSimulation);
void setInitialScreenSize(Aya::Vector2 newScreenSize);
// submits a task to be executed in a thread-safe manner
typedef boost::function<void(DataModel*)> Task;
void submitTask(Task task, DataModelJob::TaskType taskType);
shared_ptr<const Reflection::ValueArray> getJobsInfo();
void setCreatorID(int creatorID, CreatorType creatorType);
int getCreatorID() const
{
return creatorID;
}
CreatorType getCreatorType() const
{
return creatorType;
}
int getPlaceID() const
{
return placeID;
}
int getPlaceIDOrZeroInStudio();
void setPlaceID(int placeID, bool robloxPlace);
Aya::GameBasicSettings::VirtualVersion getInitialVersion() const
{
return initialVirtualVersion;
}
void setInitialVersion(Aya::GameBasicSettings::VirtualVersion vv)
{
this->initialVirtualVersion = vv;
}
const;
bool showLoadingScreen() const
{
return shouldShowLoadingScreen;
}
void setShouldShowLoadingScreen(bool show)
{
this->shouldShowLoadingScreen = show;
}
const;
void setGameInstanceID(std::string gameInstanceID)
{
this->gameInstanceID = gameInstanceID;
}
std::string getGameInstanceID()
{
return this->gameInstanceID;
}
int getUniverseId()
{
return universeId;
}
void setUniverseId(int uId);
bool isStudio() const
{
return runningInStudio;
}
void setIsStudio(bool runningInStudio);
bool isRunMode() const
{
return isStudioRunMode;
}
void setIsRunMode(bool value);
int getPlaceVersion() const
{
return placeVersion;
}
void setPlaceVersion(int placeVersion);
bool getIsPersonalServer() const
{
return isPersonalServer;
}
void setIsPersonalServer(bool value)
{
isPersonalServer = value;
}
bool getIsGameLoaded()
{
return isGameLoaded;
}
void setIsGameLoaded(bool value)
{
isGameLoaded = value;
if (isGameLoaded)
gameLoadedSignal();
}
void gameLoaded();
bool getForceR15() const
{
return forceR15;
}
void setForceR15(bool v);
boost::promise<void> universeDataLoaded;
bool getUniverseDataRequested() const
{
return universeDataRequested;
}
void clearUniverseDataRequested()
{
universeDataRequested = false;
}
void setCanRequestUniverseInfo(bool value);
void loadCoreScripts(const std::string& altStarterScript = "");
int getNumPartInstances() const
{
return numPartInstances;
}
int getNumInstance() const
{
return numInstances;
}
void checkFetchExperimentalFeatures();
bool canSaveLocal() const;
void saveToRoblox(boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void completeShutdown(bool saveLocal);
boost::function<bool()> requestShutdownCallback;
typedef boost::function<void(Reflection::AsyncCallbackDescriptor::ResumeFunction, Reflection::AsyncCallbackDescriptor::ErrorFunction)>
CloseCallback;
CloseCallback onCloseCallback;
void onCloseCallbackChanged(const CloseCallback&);
// Returns true if the shutdown is being handled by Lua, false to use the System Level Prompts
RequestShutdownResult requestShutdown(bool useLuaShutdownForSave = true);
// for perf stats reporting:
// width of window to measure. Set to zero to turn off and release memory.
void setJobsExtendedStatsWindow(double seconds);
shared_ptr<const Reflection::ValueArray> getJobsExtendedStats();
double getJobTimePeakFraction(std::string jobname, double greaterThan);
double getJobIntervalPeakFraction(std::string jobname, double greaterThan);
class GenericJob;
class LegacyLock : boost::noncopyable
{
struct Implementation;
boost::scoped_ptr<Implementation> const implementation;
public:
static int mainThreadId;
class Impersonator : boost::noncopyable
{
// Use this class to override a Lock. This must only be used
// when you know that a different thread is delegating the lock
// to you.
// TODO: For added safety we could write a stack-based Delegator
// object that would be constructed at the source thread. The
// Delegator would issue a token to be used by the thread that
// uses the Impersonator
public:
Impersonator(shared_ptr<DataModel> dataModel, DataModelJob::TaskType taskType);
~Impersonator();
private:
DataModel::GenericJob* previousJob;
};
LegacyLock(shared_ptr<DataModel> dataModel, DataModelJob::TaskType taskType);
LegacyLock(DataModel* dataModel, DataModelJob::TaskType taskType);
~LegacyLock();
static bool hasLegacyLock(DataModel* dataModel);
};
// Please call these to construct and release a datamodel
static shared_ptr<DataModel> createDataModel(bool startHeartbeat, Aya::Verb* lockVerb, bool shouldShowLoadingScreen,
GameBasicSettings::VirtualVersion vv = GameBasicSettings::VERSION_2016);
static void closeDataModel(shared_ptr<DataModel> dataModel);
~DataModel();
DataModel(Aya::Verb* lockVerb);
static DataModel* get(Instance* context);
static const DataModel* get(const Instance* context);
void loadGame(int assetID);
void loadWorld(int assetID);
void loadContent(ContentId contentId);
void processAfterLoad();
Aya::signal<void()> saveFinishedSignal;
class SerializationException : public std::runtime_error
{
public:
SerializationException(const std::string& response)
: std::runtime_error(response)
{
}
};
void save(ContentId contentId);
static bool canSave(const Aya::Instance* instance);
bool getRemoteBuildMode();
void setRemoteBuildMode(bool remoteBuildMode);
void setServerSaveUrl(std::string url);
void serverSave();
bool serverSavePlace(
const SaveFilter saveFilter, boost::function<void(bool)> resumeFunction = NULL, boost::function<void(std::string)> errorFunction = NULL);
shared_ptr<std::stringstream> serializeDataModel(const Instance::SaveFilter saveFilter = Instance::SAVE_ALL);
virtual void savePlaceAsync(
const SaveFilter saveFilter, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
void httpGetAsync(std::string url, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction);
void httpPostAsync(std::string url, std::string data, std::string optionalContentType, boost::function<void(std::string)> resumeFunction,
boost::function<void(std::string)> errorFunction);
std::string httpGet(std::string url, bool synchronous);
std::string httpPost(std::string url, std::string data, bool synchronous, std::string optionalContentType);
void reportMeasurement(std::string id, std::string key1, std::string value1, std::string key2, std::string value2);
void close();
shared_ptr<const Instances> fetchAsset(ContentId contentId);
void raiseClose();
static void ShowMessage(weak_ptr<Aya::DataModel> weakDataModel, int slot, const std::string& message, double duration);
void addCustomStat(std::string name, std::string value);
void removeCustomStat(std::string str);
void writeStatsSettings();
///////////////////////////////////////////////////////////////////////
//
// Command Processing
int numChatOptions()
{
return 4;
} // To Do - make this some kind of dynamic loading
void startCoreScripts(bool buildInGameGui, const std::string& altStarterScript = "");
void setGuiTargetInstance(shared_ptr<Instance> newTargetInstance)
{
guiTargetInstance = newTargetInstance;
}
bool processInputObject(shared_ptr<InputObject> event);
GuiRoot* getGuiRoot()
{
return guiRoot.get();
}
void setForceArrowCursor(bool value)
{
forceArrowCursor = value;
}
///////////////////////////////////////////////////////////////////////
// IDataState
virtual void setDirty(bool dirty)
{
this->dirty = dirty;
}
// Thread-safe:
virtual bool isDirty() const
{
return dirty;
}
virtual std::string arbiterName()
{
return this->getName();
}
////////////////////////////////////////////////////////////////////////////////////
//
// Workspace Stuff / Running Stuff
//
Workspace* getWorkspace() const
{
return workspace.get();
}
float physicsStep(float timeInterval, double dt, double dutyDt, int numThreads);
void renderStep(float timeIntervalSeconds);
////////////////////////////////////////////////////////////////////////////////////
//
// DataModelArbiter
//
/*implement*/ int getNumPlayers() const;
/*implement*/ int getMaxPlayers() const;
///////////////////////////////////////////////////////////////////////////
//
// timing state
//
double getGameTime() const; // i.e. game time
double getSmoothFps() const;
///////////////////////////////////////////////////////////////////////////
//
// IMetric
//
//
/*override*/ virtual std::string getMetric(const std::string& metric) const;
/*override*/ virtual double getMetricValue(const std::string& metric) const;
std::string getUpdatedMessageBoxText();
void setNetworkMetric(IMetric* metric);
///////////////////////////////////////////////////////////////////////////
//
// Rendering
//
virtual void renderPass2d(Adorn* adorn, IMetric* graphicsMetric);
void renderPass3dAdorn(Adorn* adorn);
void setUiMessage(std::string message);
void clearUiMessage();
void setUiMessageBrickCount();
void toggleToolsOff();
virtual void renderMouse(Adorn* adorn);
ContentId getRenderMouseCursor();
void computeGuiInset(Adorn* adorn);
void renderPlayerGui(Adorn* adorn);
void renderDebugMenu();
void renderMessageBox(Adorn* adorn);
static void HttpHelper(std::string* response, std::exception* exception, boost::function<void(std::string)> resumeFunction,
boost::function<void(std::string)> errorFunction);
DebugMenu& getDebugMenu()
{
return *debugMenu;
}
TaskScheduler::Arbiter* getSyncronizationArbiter();
bool uploadPlace(const std::string& uploadUrl, const SaveFilter saveFilter, boost::function<void()> resumeFunction,
boost::function<void(std::string)> errorFunction);
GuiResponse processPlayerGui(const shared_ptr<InputObject>& event);
static void processHttpRequestResponseOnLock(DataModel* dataModel, std::string* response, std::exception* exception,
boost::function<void(shared_ptr<std::string>, shared_ptr<std::exception> exception)> onLockAcquired);
protected:
static void doDataModelSetup(shared_ptr<DataModel> dataModel, bool startHeartbeat, bool shouldShowLoadingScreen);
void internalSave(ContentId contentId);
void internalSaveAsync(ContentId contentId, boost::function<void(bool)> resumeFunction);
/////////////////////////////////////////////////////////////////////
//
// Instance overrides
/*override*/ bool askAddChild(const Instance* instance) const;
/*override*/ void onChildAdded(Instance* child);
/*override*/ void onChildChanged(Instance* instance, const PropertyChanged& event);
/*override*/ void onDescendantAdded(Instance* instance);
/*override*/ void onDescendantRemoving(const shared_ptr<Instance>& instance);
void onRunTransition(RunTransition event);
bool processEvent(const shared_ptr<InputObject>& event);
GuiResponse processProfilerEvent(const shared_ptr<InputObject>& event);
weak_ptr<Instance> guiTargetInstance;
bool mouseOverGui;
weak_ptr<GuiObject> mouseOverInteractable;
bool getSuppressNavKeys() const
{
return suppressNavKeys;
}
private:
Aya::signals::scoped_connection unbindResourceSignal;
void onUnbindResourceSignal();
bool suppressNavKeys;
static std::string doHttpGet(const std::string& url);
static void doHttpGet(
const std::string& url, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction);
static std::string doHttpPost(const std::string& url, const std::string& data, const std::string& contentType);
static void doHttpPost(const std::string& url, const std::string& data, const std::string& contentType,
boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction);
static void doCloseDataModel(shared_ptr<DataModel> dataModel);
void AsyncUploadPlaceResponseHandler(
std::string* response, std::exception* exception, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
bool uploadPlaceReturn(
const bool succeeded, const std::string error, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
bool canRenderMouse();
GuiResponse processDevGamepadEvent(const shared_ptr<InputObject>& event);
GuiResponse processCoreGamepadEvent(const shared_ptr<InputObject>& event);
GuiResponse processGuiTarget(const shared_ptr<InputObject>& event);
typedef boost::array<shared_ptr<GenericJob>, DataModelJob::TaskTypeMax> GenericJobs;
Aya::mutex genericJobsLock;
GenericJobs genericJobs;
Aya::mutex debugLock;
shared_ptr<GenericJob> tryGetGenericJob(DataModelJob::TaskType type);
shared_ptr<GenericJob> getGenericJob(DataModelJob::TaskType type);
std::map<std::string, int> dataModelReportingData;
void traverseDataModelReporting(shared_ptr<Instance> child);
Instance* getLightingDeprecated() const;
static Reflection::RefPropDescriptor<DataModel, Instance> prop_lighting;
bool remoteBuildMode;
std::string serverSaveUrl;
HeapValue<int> placeID;
std::string gameInstanceID;
int universeId;
bool runningInStudio;
bool isStudioRunMode;
bool checkedExperimentalFeatures;
int placeVersion;
HeapValue<int> creatorID;
CreatorType creatorType;
Genre genre;
GearGenreSetting gearGenreSetting;
unsigned allowedGearTypes;
std::string vipServerId;
int vipServerOwnerId;
DebugMenu* debugMenu;
static void buildNetworkStatsOutput(shared_ptr<Instance> instance, std::string* output);
static void buildSimpleStatsOutput(shared_ptr<Instance> instance, std::string* output);
};
} // namespace Aya

View File

@@ -0,0 +1,187 @@
#include "DataModel/DataModelJob.hpp"
#include "Debug.hpp"
#include "boost/cast.hpp"
#include "Reflection/EnumConverter.hpp"
#include "Utility/Object.hpp"
#include "Profiler.hpp"
using namespace Aya;
namespace Aya
{
namespace Reflection
{
template<>
Reflection::EnumDesc<DataModelArbiter::ConcurrencyModel>::EnumDesc()
: Reflection::EnumDescriptor("ConcurrencyModel")
{
addPair(DataModelArbiter::Serial, "Serial");
addPair(DataModelArbiter::Safe, "Safe");
addPair(DataModelArbiter::Logical, "Logical");
addPair(DataModelArbiter::Empirical, "Empirical");
}
} // namespace Reflection
} // namespace Aya
DataModelJob::DataModelJob(const char* name, TaskType taskType, bool isPerPlayer, shared_ptr<DataModelArbiter> arbiter, Time::Interval stepBudget)
: Job(name, arbiter, stepBudget)
, taskType(taskType)
, stepsAccumulated(0)
, isPerPlayer(isPerPlayer)
, profilingToken(0)
{
AYAASSERT(arbiter);
#ifdef AYAPROFILER
profilingToken = Profiler::getToken("Jobs", name);
#endif
}
TaskScheduler::StepResult DataModelJob::step(const Stats& stats)
{
#ifdef AYAPROFILER
Profiler::Scope profilingScope(profilingToken);
#endif
return stepDataModelJob(stats);
}
double DataModelJob::updateStepsRequiredForCyclicExecutive(float stepDt, float desiredHz, float maxStepsPerCycle, float maxStepsAccumulated)
{
float desiredStepsFloat = stepDt * desiredHz + stepsAccumulated;
float desiredStepsFloor = ::floorf(desiredStepsFloat);
desiredStepsFloor = std::min(desiredStepsFloor, maxStepsPerCycle);
stepsAccumulated = desiredStepsFloat - desiredStepsFloor;
stepsAccumulated = std::min((float)stepsAccumulated, maxStepsAccumulated);
if (desiredStepsFloor <= 0.0f)
{
return 0.0f;
}
return desiredStepsFloor;
}
double DataModelJob::getPriorityFactor()
{
// shared_ptr<DataModelArbiter> arbiter(shared_polymorphic_downcast<DataModelArbiter>(getArbiter()));
// if (!arbiter)
// return 1;
return isPerPlayer ? 1 : std::max(1, getArbiter()->getNumPlayers());
}
// The default is "Logical"
DataModelArbiter::ConcurrencyModel DataModelArbiter::concurrencyModel = Logical;
bool DataModelArbiter::areExclusive(TaskScheduler::Job* job1, TaskScheduler::Job* job2)
{
DataModelJob* j1 = boost::polymorphic_downcast<DataModelJob*>(job1);
DataModelJob* j2 = boost::polymorphic_downcast<DataModelJob*>(job2);
return areExclusive(j1->taskType, j2->taskType);
}
bool DataModelArbiter::areExclusive(DataModelJob::TaskType function1, DataModelJob::TaskType function2)
{
return lookup[concurrencyModel][function1][function2];
}
void static copy(
bool src[DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount], bool dst[DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount])
{
for (int j = 0; j < DataModelJob::TaskTypeCount; ++j)
for (int k = 0; k < DataModelJob::TaskTypeCount; ++k)
dst[j][k] = src[j][k];
}
static void setConcurrent(bool table[DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount], DataModelJob::TaskType f1, DataModelJob::TaskType f2)
{
table[f1][f2] = false;
table[f2][f1] = false;
}
DataModelArbiter::DataModelArbiter(void)
{
for (int j = 0; j < DataModelJob::TaskTypeCount; ++j)
for (int k = 0; k < DataModelJob::TaskTypeCount; ++k)
lookup[Serial][j][k] = true; // default all to exclusive
{
copy(lookup[Serial], lookup[Safe]);
// Open up concurrency for Safe:
setConcurrent(lookup[Safe], DataModelJob::PhysicsOut, DataModelJob::PhysicsOut);
setConcurrent(lookup[Safe], DataModelJob::DataOut, DataModelJob::DataOut);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::None);
// RaknetPeer can be concurrent with everything except for Write,Physics,RaknetPeer:
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::PhysicsOut);
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::DataOut);
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::PhysicsIn);
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::DataIn);
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::Read);
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::Render);
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::None);
// PhysicsOutSort can be concurrent with everything except PhysicsOut
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::DataOut);
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::PhysicsIn);
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::DataIn);
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::Read);
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::Render);
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::None);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::PhysicsOut);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::DataOut);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::PhysicsIn);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::DataIn);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Read);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Write);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Render);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Physics);
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::RaknetPeer);
}
{
copy(lookup[Safe], lookup[Logical]);
// Open up concurrency for Logical:
setConcurrent(lookup[Logical], DataModelJob::DataOut, DataModelJob::PhysicsOut);
// Can't do rendering in parallel because it currently makes changes to the DataModel
// setConcurrent(lookup[Logical], DataModelJob::Render, DataModelJob::PhysicsOut);
setConcurrent(lookup[Logical], DataModelJob::Read, DataModelJob::Read);
setConcurrent(lookup[Logical], DataModelJob::Read, DataModelJob::DataOut);
setConcurrent(lookup[Logical], DataModelJob::Read, DataModelJob::PhysicsOut);
// Note: Render and Read are not allowed, because Render can modify the Camera
// Note: Render and DataOut are not allowed, because Render can modify the Camera
}
{
copy(lookup[Logical], lookup[Empirical]);
// Open up concurrency for Imperical:
// setConcurrent(lookup[Empirical], DataModelJob::Render, DataModelJob::PhysicsIn);
// setConcurrent(lookup[Empirical], DataModelJob::Render, DataModelJob::Physics);
}
}
// This does not get called closing a place in Studio.
DataModelArbiter::~DataModelArbiter() {}
void DataModelArbiter::preStep(TaskScheduler::Job* job)
{
activityMeter.increment();
}
void DataModelArbiter::postStep(TaskScheduler::Job* job)
{
activityMeter.decrement();
}

View File

@@ -0,0 +1,73 @@
#pragma once
#include "TaskScheduler.Job.hpp"
LOGGROUP(DataModelJobs)
namespace Aya
{
class DataModelArbiter;
class DataModelJob : public TaskScheduler::Job
{
public:
typedef enum
{
Read = 0, // general read-only access to the DataModel
Write, // general read-writes access to the DataModel
Render,
Physics,
DataOut,
PhysicsOut,
PhysicsOutSort,
DataIn,
PhysicsIn,
RaknetPeer,
None,
TaskTypeMax
} TaskType;
static const int TaskTypeCount = TaskTypeMax;
TaskType taskType;
virtual TaskScheduler::StepResult step(const Stats& stats);
protected:
virtual TaskScheduler::StepResult stepDataModelJob(const Stats& stats) = 0;
virtual double updateStepsRequiredForCyclicExecutive(float stepDt, float desiredHz, float maxStepsPerCycle, float maxStepsAccumulated);
double stepsAccumulated;
const bool isPerPlayer;
unsigned long long profilingToken;
DataModelJob(const char* name, TaskType taskType, bool isPerPlayer, shared_ptr<DataModelArbiter> arbiter, Time::Interval stepBudget);
/*implement*/ double getPriorityFactor();
};
class DataModelArbiter : public SimpleThrottlingArbiter
{
public:
typedef enum
{
Serial = 0,
Safe,
Logical,
Empirical
} ConcurrencyModel;
static const int ConcurrencyModelCount = 4;
static ConcurrencyModel concurrencyModel;
DataModelArbiter();
virtual ~DataModelArbiter();
virtual bool areExclusive(TaskScheduler::Job* job1, TaskScheduler::Job* job2);
virtual int getNumPlayers() const = 0;
bool areExclusive(DataModelJob::TaskType task1, DataModelJob::TaskType task2);
virtual void preStep(TaskScheduler::Job* job);
virtual void postStep(TaskScheduler::Job* job);
private:
bool lookup[ConcurrencyModelCount][DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount];
};
} // namespace Aya

View File

@@ -0,0 +1,122 @@
#include "DataModel/DataModelMesh.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/SpecialMesh.hpp"
#include "DataModel/PartCookie.hpp"
#include "Humanoid/Humanoid.hpp"
namespace Aya
{
const char* const sDataModelMesh = "DataModelMesh";
namespace Reflection
{
template<>
EnumDesc<DataModelMesh::LODType>::EnumDesc()
: EnumDescriptor("LevelOfDetailSetting")
{
addPair(DataModelMesh::HIGH_LOD, "High");
addPair(DataModelMesh::MEDIUM_LOD, "Medium");
addPair(DataModelMesh::LOW_LOD, "Low");
}
} // namespace Reflection
static Reflection::EnumPropDescriptor<DataModelMesh, DataModelMesh::LODType> desc_levelOfDetailX(
"LODX", category_Data, &DataModelMesh::getLevelOfDetailX, &DataModelMesh::setLevelOfDetailX, Aya::Reflection::PropertyDescriptor::STREAMING);
static Reflection::EnumPropDescriptor<DataModelMesh, DataModelMesh::LODType> desc_levelOfDetailY(
"LODY", category_Data, &DataModelMesh::getLevelOfDetailY, &DataModelMesh::setLevelOfDetailY, Aya::Reflection::PropertyDescriptor::STREAMING);
static Reflection::PropDescriptor<DataModelMesh, Vector3> desc_scale("Scale", category_Data, &DataModelMesh::getScale, &DataModelMesh::setScale);
static Reflection::PropDescriptor<DataModelMesh, Vector3> desc_vertColor(
"VertexColor", category_Data, &DataModelMesh::getVertColor, &DataModelMesh::setVertColor);
static Reflection::PropDescriptor<DataModelMesh, Vector3> desc_offset("Offset", category_Data, &DataModelMesh::getOffset, &DataModelMesh::setOffset);
REFLECTION_END();
DataModelMesh::DataModelMesh()
: scale(1.0, 1.0, 1.0)
, vertColor(1.0, 1.0, 1.0)
, offset(0.0, 0.0, 0.0)
, LODx(DataModelMesh::HIGH_LOD)
, LODy(DataModelMesh::HIGH_LOD)
{
setName("Mesh");
}
bool DataModelMesh::askSetParent(const Instance* instance) const
{
return Instance::fastDynamicCast<PartInstance>(instance) != NULL;
}
float DataModelMesh::getAlpha() const
{
// Alpha for a special shape is the alpha of it's parent part
const Instance* i = this->getParent();
if (i)
{
const PartInstance* pi = i->fastDynamicCast<PartInstance>();
if (pi)
return pi->getTransparencyUi();
}
return 0;
}
void DataModelMesh::setScale(const G3D::Vector3& value)
{
// Sanitize the value by replacing non-finite components with 0
// This avoids issues with tesselation while generating geometry
G3D::Vector3 fixedValue = value;
for (int i = 0; i < 3; ++i)
if (Math::isNanInf(fixedValue[i]))
fixedValue[i] = 0;
if (scale != fixedValue)
{
scale = fixedValue;
raisePropertyChanged(desc_scale);
}
}
void DataModelMesh::setVertColor(const G3D::Vector3& value)
{
if (vertColor != value)
{
vertColor = value;
raisePropertyChanged(desc_vertColor);
}
}
void DataModelMesh::setOffset(const G3D::Vector3& value)
{
if (offset != value)
{
offset = value;
raisePropertyChanged(desc_offset);
}
}
void DataModelMesh::setLevelOfDetailX(LODType value)
{
if (LODx != value)
{
LODx = value;
raisePropertyChanged(desc_levelOfDetailX);
}
}
void DataModelMesh::setLevelOfDetailY(LODType value)
{
if (LODy != value)
{
LODy = value;
raisePropertyChanged(desc_levelOfDetailY);
}
}
} // Namespace Aya

View File

@@ -0,0 +1,69 @@
#pragma once
#include "Tree/Instance.hpp"
#include "Utility/G3DCore.hpp"
namespace Aya
{
extern const char* const sDataModelMesh;
class DataModelMesh : public DescribedNonCreatable<DataModelMesh, Instance, sDataModelMesh>
{
public:
// Do not change these integer values, they correlate to the enum on the RBXViewNew side
enum LODType
{
LOW_LOD = 0,
MEDIUM_LOD = 1,
HIGH_LOD = 2
};
protected:
Vector3 scale;
Vector3 vertColor;
Vector3 offset;
LODType LODx;
LODType LODy;
public:
DataModelMesh();
LODType getLevelOfDetailX() const
{
return LODx;
}
void setLevelOfDetailX(LODType val);
LODType getLevelOfDetailY() const
{
return LODy;
}
void setLevelOfDetailY(LODType val);
const G3D::Vector3& getScale() const
{
return scale;
}
void setScale(const G3D::Vector3& value);
const G3D::Vector3& getVertColor() const
{
return vertColor;
}
void setVertColor(const G3D::Vector3& value);
float getAlpha() const;
const G3D::Vector3& getOffset() const
{
return offset;
}
void setOffset(const G3D::Vector3& value);
protected:
bool askSetParent(const Instance* instance) const;
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
#pragma once
#include "Reflection/Reflection.hpp"
#include "Script/ThreadRef.hpp"
#include "Tree/Instance.hpp"
#include "RunningAverage.hpp"
#include "DataModel/DataStoreService.hpp"
#include "Utility/LuaWebService.hpp"
#include "signal.h"
#include <deque>
namespace Aya
{
extern const char* const sGlobalDataStore;
class DataStore : public DescribedNonCreatable<DataStore, Instance, sGlobalDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL>
{
typedef DescribedNonCreatable<DataStore, Instance, sGlobalDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL> Super;
class CachedRecord
{
private:
Reflection::Variant variant;
std::string serialized;
Time accessTimeStamp;
public:
CachedRecord() {};
const Reflection::Variant& getVariant(bool touch = true);
const std::string& getSerialized()
{
return serialized;
}
const Time& getTime()
{
return accessTimeStamp;
}
void update(const Reflection::Variant& variant, const std::string& serialized);
};
struct EventSlot
{
Lua::WeakFunctionRef callback;
EventSlot(Lua::WeakFunctionRef);
void fire(Reflection::Variant value);
};
bool isLegacy;
typedef std::map<std::string, CachedRecord> CachedKeys;
CachedKeys cachedKeys;
typedef std::map<std::string, shared_ptr<Aya::signal<void(Reflection::Variant)>>> OnUpdateKeys;
OnUpdateKeys onUpdateKeys;
typedef std::map<std::string, Time> KeyTimestamps;
KeyTimestamps lastSetByKey;
enum RefetchState
{
RefetchOnUpdateKeys,
RefetchCachedKeys,
RefetchDone
};
RefetchState refetchState;
std::string nextKeyToRefetch;
bool backendProcessing;
std::string constructPostDataForKey(const std::string& key, unsigned index = 0);
std::string constructGetUrl();
std::string constructSetUrl(const std::string& key, unsigned valueLength);
std::string constructSetIfUrl(const std::string& key, unsigned valueLength, unsigned expectedValueLength);
std::string constructIncrementUrl(const std::string& key, int delta);
void processSet(std::string key, std::string* response, std::exception* exception, boost::function<void()> resumeFunction,
boost::function<void(std::string)> errorFunction);
void processFetchSingleKey(std::string* response, std::exception* exception, std::string key, bool expectSubKey, boost::function<void()> callback,
boost::function<void(std::string)> errorFunction);
void lockAcquiredProcessFetchSingleKey(shared_ptr<std::string> response, shared_ptr<std::exception> exception, std::string key, bool expectSubKey,
boost::function<void()> callback, boost::function<void(std::string)> errorFunction);
bool updateCachedKey(const std::string& key, const Reflection::Variant& value);
void createFetchNewKeyRequest(const std::string& key, boost::function<void()> callback, boost::function<void(std::string)> errorFunction,
DataStoreService::HttpRequest& request);
// Has to be const reference to Lua::WeakFunctionRef so boost::bind won't copy it to pass by value
// Lua::WeakFunctionRef needs access to Lua state to copy, so can't be done safely in http threadpool
void processSetIf(std::string key, shared_ptr<Lua::WeakFunctionRef> transform, std::string* response, std::exception* exception,
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction);
void lockAcquiredProcessSetIf(std::string key, shared_ptr<Lua::WeakFunctionRef> transform, shared_ptr<std::string> response,
shared_ptr<std::exception> exception, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void runTransformFunction(std::string key, const shared_ptr<Lua::WeakFunctionRef> transform,
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction);
void processFetchCachedKeys(std::string* response, std::exception* exception);
void lockAcquiredProcessFetchCachedKeys(shared_ptr<std::string> response, shared_ptr<std::exception> exception);
bool checkAccess(const std::string& key, boost::function<void(std::string)>* errorFunction);
bool checkStudioApiAccess(boost::function<void(std::string)> errorFunction);
static std::string serializeVariant(const Reflection::Variant& variant, bool* hasNonJsonType);
static bool deserializeVariant(const std::string& webValue, Reflection::Variant& result);
void sendBatchGet(std::stringstream& keysList);
void accumulateKeyToFetch(const std::string& key, std::stringstream& keysList, int& counter);
protected:
std::string serviceUrl;
std::string name, scope;
std::string scopeUrlEncodedIfNeeded, nameUrlEncodedIfNeeded;
virtual bool checkValueIsAllowed(const Reflection::Variant&)
{
return true;
};
virtual const char* getDataStoreTypeString()
{
return "standard";
}
virtual bool queueOrExecuteSet(DataStoreService::HttpRequest& request);
std::string urlEncodeIfNeeded(const std::string& input);
public:
DataStore(const std::string& name, const std::string& scope, bool legacy);
static const char* urlApiPath()
{
return "persistence";
}
void getAsync(std::string key, boost::function<void(Reflection::Variant)> resumeFunction, boost::function<void(std::string)> errorFunction);
void updateAsync(std::string key, Lua::WeakFunctionRef transformFunc, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void setAsync(
std::string key, Reflection::Variant value, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
void incrementAsync(
std::string key, int delta, boost::function<void(Reflection::Variant)> resumeFunction, boost::function<void(std::string)> errorFunction);
Aya::signals::connection onUpdate(std::string key, Lua::WeakFunctionRef callback);
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
void refetchCachedKeys(int* budget);
void resetRefetch();
bool isKeyThrottled(const std::string& key, Time timestamp);
void setKeySetTimestamp(const std::string& key, Time timestamp);
DataStoreService* getParentDataStoreService();
private:
void runResumeFunction(std::string key, boost::function<void(Reflection::Variant)> resumeFunction);
};
extern const char* const sOrderedDataStore;
class OrderedDataStore : public DescribedNonCreatable<OrderedDataStore, DataStore, sOrderedDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL>
{
typedef DescribedNonCreatable<DataStore, DataStore, sOrderedDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL> Super;
std::string constructGetSortedUrl(bool isAscending, int pagesize, const double* minValue, const double* maxValue);
protected:
virtual bool checkValueIsAllowed(const Reflection::Variant& v);
virtual const char* getDataStoreTypeString()
{
return "sorted";
}
virtual bool queueOrExecuteSet(DataStoreService::HttpRequest& request);
public:
OrderedDataStore(const std::string& name, const std::string& scope);
void getSortedAsync(bool isAscending, int pagesize, Reflection::Variant minValue, Reflection::Variant maxValue,
boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
};
extern const char* const sDataStorePages;
class DataStorePages : public DescribedNonCreatable<DataStorePages, Pages, sDataStorePages, Reflection::ClassDescriptor::RUNTIME_LOCAL>
{
weak_ptr<OrderedDataStore> ds;
std::string requestUrl;
std::string exclusiveStartKey;
void processFetch(
std::string* response, std::exception* exception, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
void lockAcquiredProcessFetch(shared_ptr<std::string> response, shared_ptr<std::exception> exception, boost::function<void()> resumeFunction,
boost::function<void(std::string)> errorFunction);
public:
DataStorePages(weak_ptr<OrderedDataStore> ds, const std::string& requestUrl);
void fetchNextChunk(boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
};
} // namespace Aya

View File

@@ -0,0 +1,536 @@
#include "DataModel/DataStoreService.hpp"
#include "DataModel/DataModel.hpp"
#include "DataModel/DataStore.hpp"
#include "Players.hpp"
LOGGROUP(DataStore);
DYNAMIC_FASTINTVARIABLE(DataStoreJobFrequencyInSeconds, 1);
DYNAMIC_FASTINTVARIABLE(DataStoreFetchFrequenceInSeconds, 30);
DYNAMIC_FASTINTVARIABLE(DataStoreFixedRequestLimit, 60);
DYNAMIC_FASTINTVARIABLE(DataStorePerPlayerRequestLimit, 10);
DYNAMIC_FASTINTVARIABLE(DataStoreInitialBudget, 100);
DYNAMIC_FASTINTVARIABLE(DataStoreOrderedSetFixedRequestLimit, 30);
DYNAMIC_FASTINTVARIABLE(DataStoreOrderedSetPerPlayerRequestLimit, 5);
DYNAMIC_FASTINTVARIABLE(DataStoreOrderedSetInitialBudget, 50);
DYNAMIC_FASTINTVARIABLE(DataStoreRefetchFixedRequestLimit, 30);
DYNAMIC_FASTINTVARIABLE(DataStoreRefetchPerPlayerRequestLimit, 5);
DYNAMIC_FASTINTVARIABLE(DataStoreSortedFixedRequestLimit, 5);
DYNAMIC_FASTINTVARIABLE(DataStoreSortedPerPlayerRequestLimit, 2);
DYNAMIC_FASTINTVARIABLE(DataStoreSortedInitialBudget, 10);
DYNAMIC_FASTINTVARIABLE(DataStoreMaxBudgetMultiplier, 100);
DYNAMIC_FASTINTVARIABLE(DataStoreMaxThrottledQueue, 30);
DYNAMIC_FASTINT(DataStoreKeyLengthLimit);
DYNAMIC_FASTFLAGVARIABLE(GetGlobalDataStorePcallFix, false);
DYNAMIC_FASTFLAGVARIABLE(UseNewDataStoreLogging, true)
DYNAMIC_FASTFLAGVARIABLE(UseNewDataStoreRequestSetTimestampBehaviour, true)
LOGVARIABLE(DataStoreBudget, 0);
namespace Aya
{
const char* const sDataStoreService = "DataStoreService";
static Reflection::BoundFuncDesc<DataStoreService, shared_ptr<Instance>(void)> getGlobalStoreFunction(
&DataStoreService::getGlobalDataStore, "GetGlobalDataStore", Security::None);
static Reflection::BoundFuncDesc<DataStoreService, shared_ptr<Instance>(std::string, std::string)> getDataStoreFunction(
&DataStoreService::getDataStore, "GetDataStore", "name", "scope", "global", Security::None);
static Reflection::BoundFuncDesc<DataStoreService, shared_ptr<Instance>(std::string, std::string)> getOrderedDataStore(
&DataStoreService::getOrderedDataStore, "GetOrderedDataStore", "name", "scope", "global", Security::None);
static Reflection::PropDescriptor<DataStoreService, bool> prop_disableUrlEncoding("LegacyNamingScheme", category_Behavior,
&DataStoreService::isUrlEncodingDisabled, &DataStoreService::setUrlEncodingDisabled, Reflection::PropertyDescriptor::STANDARD,
Security::LocalUser);
REFLECTION_END();
class DataStoreJob : public DataModelJob
{
shared_ptr<DataStoreService> dataStoreService;
int lastJobFrequency;
double desiredHz;
int secondsSinceLastFetch;
public:
DataStoreJob(DataStoreService* owner)
: DataModelJob("DataStoreJob", DataModelJob::Write, false, shared_from(DataModel::get(owner)), Time::Interval(0.01))
, dataStoreService(shared_from(owner))
{
updateHz();
secondsSinceLastFetch = 0;
cyclicExecutive = true;
}
void updateHz()
{
desiredHz = 1.0f / DFInt::DataStoreJobFrequencyInSeconds;
lastJobFrequency = DFInt::DataStoreJobFrequencyInSeconds;
}
/*override*/ Time::Interval sleepTime(const Stats& stats)
{
return computeStandardSleepTime(stats, desiredHz);
}
/*override*/ Job::Error error(const Stats& stats)
{
return computeStandardErrorCyclicExecutiveSleeping(stats, desiredHz);
}
/*override*/ TaskScheduler::StepResult stepDataModelJob(const Stats& stats)
{
if (DFInt::DataStoreJobFrequencyInSeconds != lastJobFrequency)
updateHz();
dataStoreService->addThrottlingBudgets(DFInt::DataStoreJobFrequencyInSeconds / 60.0f);
dataStoreService->executeThrottledRequests();
secondsSinceLastFetch += DFInt::DataStoreJobFrequencyInSeconds;
if (secondsSinceLastFetch >= DFInt::DataStoreFetchFrequenceInSeconds)
{
dataStoreService->refetchCachedKeys();
secondsSinceLastFetch -= DFInt::DataStoreFetchFrequenceInSeconds;
}
return TaskScheduler::Stepped;
}
};
DataStoreService::DataStoreService()
: disableUrlEncoding(false)
, msReadSuccessAverageRequestTime()
, msErrorAverageRequestTime()
, msWriteAverageRequestTime()
, msUpdateAverageRequestTime()
, msBatchAverageRequestTime()
, readSuccessCachedCount(0)
{
setName(sDataStoreService);
throttleCounterGets.addBudget(DFInt::DataStoreInitialBudget, DFInt::DataStoreInitialBudget);
throttleCounterGetSorteds.addBudget(DFInt::DataStoreInitialBudget, DFInt::DataStoreInitialBudget);
throttleCounterSets.addBudget(DFInt::DataStoreSortedInitialBudget, DFInt::DataStoreSortedInitialBudget);
throttleCounterOrderedSets.addBudget(DFInt::DataStoreOrderedSetInitialBudget, DFInt::DataStoreOrderedSetInitialBudget);
}
void DataStoreService::onRequestFinishReport(DataStoreService::HttpRequest* request, bool isError, std::string errorMessage)
{
unsigned int requestDurationMilliseconds =
(unsigned int)((boost::posix_time::microsec_clock::local_time() - request->requestStartTime).total_microseconds() / 1000);
if (isError)
{
msErrorAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
}
else
{
switch (request->requestType)
{
case DataStoreService::HttpRequest::RequestType::GET_ASYNC:
{
msReadSuccessAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
}
break;
case DataStoreService::HttpRequest::RequestType::UPDATE_ASYNC:
{
msUpdateAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
}
break;
case DataStoreService::HttpRequest::RequestType::SET_ASYNC:
{
msWriteAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
}
break;
case DataStoreService::HttpRequest::RequestType::INCREMENT_ASYNC:
{
msWriteAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
}
break;
case DataStoreService::HttpRequest::RequestType::GET_SORTED_ASYNC_PAGE:
{
msBatchAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
}
break;
default:
break;
}
}
}
void reportDataStoreStat(unsigned int& count, unsigned int& average, std::string countReportName, std::string averageReportName)
{
if (count > 0)
{
count = 0;
average = 0;
}
}
shared_ptr<Instance> DataStoreService::getGlobalDataStore()
{
return getDataStoreInternal("", "u", true, false);
}
void DataStoreService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
{
if (oldProvider)
{
FASTLOG1(FLog::DataStore, "Closing down DataStoreService. Backend processing: ", backendProcessing);
visitChildren(boost::bind(&Instance::unlockParent, _1));
if (backendProcessing)
{
TaskScheduler::singleton().removeBlocking(dataStoreJob);
dataStoreJob.reset();
}
backendProcessing = false;
}
Super::onServiceProvider(oldProvider, newProvider);
if (newProvider)
{
backendProcessing = Network::Players::backendProcessing(this);
FASTLOG1(FLog::DataStore, "Backend processing: %u", backendProcessing);
if (backendProcessing)
{
dataStoreJob.reset(new DataStoreJob(this));
TaskScheduler::singleton().add(dataStoreJob);
}
}
}
shared_ptr<Instance> DataStoreService::getDataStoreInternal(std::string name, std::string scope, bool legacy, bool ordered)
{
DataModel* dm = DataModel::get(this);
if (dm->getPlaceID() == 0)
{
if (DFFlag::GetGlobalDataStorePcallFix)
{
throw std::runtime_error("Place has to be opened with Edit button to access DataStores");
}
StandardOut::singleton()->print(MESSAGE_ERROR, "Place has to be opened with Edit button to access DataStores");
return shared_ptr<Instance>();
}
if (!backendProcessing)
throw std::runtime_error("DataStore can't be accessed from client");
AYAASSERT(!(legacy && ordered));
if (legacy)
{
if (!legacyDataStore)
{
FASTLOG(FLog::DataStore, "Creating legacy data store");
legacyDataStore = Creatable<Instance>::create<DataStore>(name, scope, true);
legacyDataStore->setParent(this);
legacyDataStore->lockParent();
}
return legacyDataStore;
}
else if (ordered)
{
StringPair key(name, scope);
DataStores::iterator it = orderedDataStores.find(key);
if (it == orderedDataStores.end())
{
FASTLOGS(FLog::DataStore, "Creating data store, name: %s", name);
shared_ptr<DataStore> ds = Creatable<Instance>::create<OrderedDataStore>(name, scope);
ds->setName(name);
ds->setParent(this);
ds->lockParent();
orderedDataStores[key] = ds;
return ds;
}
return it->second;
}
else
{
StringPair key(name, scope);
DataStores::iterator it = dataStores.find(key);
if (it == dataStores.end())
{
FASTLOGS(FLog::DataStore, "Creating data store, name: %s", name);
shared_ptr<DataStore> ds = Creatable<Instance>::create<DataStore>(name, scope, false);
ds->setName(name);
ds->setParent(this);
ds->lockParent();
dataStores[key] = ds;
return ds;
}
return it->second;
}
}
void checkNameAndScope(const std::string& name, const std::string& scope)
{
if (scope.length() == 0)
throw std::runtime_error("DataStore scope can't be empty string");
if (scope.length() > (unsigned)DFInt::DataStoreKeyLengthLimit)
throw std::runtime_error("DataStore scope is too long");
if (name.length() == 0)
throw std::runtime_error("DataStore name can't be empty string");
if (name.length() > (unsigned)DFInt::DataStoreKeyLengthLimit)
throw std::runtime_error("DataStore name is too long");
}
shared_ptr<Instance> DataStoreService::getDataStore(std::string name, std::string scope)
{
checkNameAndScope(name, scope);
return getDataStoreInternal(name, scope, false, false);
}
shared_ptr<Instance> DataStoreService::getOrderedDataStore(std::string name, std::string scope)
{
checkNameAndScope(name, scope);
return getDataStoreInternal(name, scope, false, true);
}
int DataStoreService::getPlayerNum()
{
const ServiceProvider* serviceProvider = ServiceProvider::findServiceProvider(this);
if (serviceProvider == NULL)
{
AYAASSERT(false);
return 0;
}
Network::Players* players = serviceProvider->find<Network::Players>();
return players ? players->getNumPlayers() : 0;
}
void DataStoreService::addThrottlingBudgets(float timeDeltaMinutes)
{
int numPlayers = getPlayerNum();
{
int sortedPerPlayerPerMinuteBudget = DFInt::DataStoreSortedPerPlayerRequestLimit * numPlayers + DFInt::DataStoreSortedFixedRequestLimit;
float sortedAddedBudget = timeDeltaMinutes * sortedPerPlayerPerMinuteBudget;
throttleCounterGetSorteds.addBudget(sortedAddedBudget, DFInt::DataStoreMaxBudgetMultiplier * sortedPerPlayerPerMinuteBudget);
}
{
int orderedSetsPerPlayerPerMinuteBudget =
DFInt::DataStoreOrderedSetPerPlayerRequestLimit * numPlayers + DFInt::DataStoreOrderedSetFixedRequestLimit;
float orderedSetsAddedBudget = timeDeltaMinutes * orderedSetsPerPlayerPerMinuteBudget;
throttleCounterOrderedSets.addBudget(orderedSetsAddedBudget, DFInt::DataStoreMaxBudgetMultiplier * orderedSetsPerPlayerPerMinuteBudget);
}
{
int perPlayerPerMinuteBudget = DFInt::DataStorePerPlayerRequestLimit * numPlayers + DFInt::DataStoreFixedRequestLimit;
float addedBudget = timeDeltaMinutes * perPlayerPerMinuteBudget;
throttleCounterGets.addBudget(addedBudget, DFInt::DataStoreMaxBudgetMultiplier * perPlayerPerMinuteBudget);
throttleCounterSets.addBudget(addedBudget, DFInt::DataStoreMaxBudgetMultiplier * perPlayerPerMinuteBudget);
FASTLOG4F(FLog::DataStoreBudget, "Adding budget %f, gets budget: %f, sets budget: %f, sorted budget: %f", addedBudget,
throttleCounterGets.getBudget(), throttleCounterSets.getBudget(), throttleCounterGetSorteds.getBudget());
}
}
static void dataStorePostSuccess(std::string response, boost::function<void(std::string*, std::exception*)> handler)
{
handler(&response, 0);
}
static void dataStorePostError(std::string error, boost::function<void(std::string*, std::exception*)> handler)
{
std::runtime_error e(error.c_str());
handler(0, &e);
}
void DataStoreService::HttpRequest::execute(DataStoreService* dataStoreService)
{
if (DFFlag::UseNewDataStoreRequestSetTimestampBehaviour)
{
if (requestType == RequestType::SET_ASYNC || requestType == RequestType::INCREMENT_ASYNC || requestType == RequestType::UPDATE_ASYNC)
{
owner->setKeySetTimestamp(key, Time::nowFast());
}
}
else
{
if (owner)
owner->setKeySetTimestamp(key, Time::nowFast());
}
Aya::Http http(url);
http.additionalHeaders["Cache-Control"] = "no-cache";
http.doNotUseCachedResponse = true;
http.post(postData.size() == 0 ? " " : postData, Http::kContentTypeUrlEncoded, false, handler);
}
bool DataStoreService::HttpRequest::isKeyThrottled(Time timestamp)
{
if (owner == NULL)
return false;
return owner->isKeyThrottled(key, timestamp);
}
bool DataStoreService::queueOrExecuteRequest(HttpRequest& request, std::list<HttpRequest>& queue, BudgetedThrottlingHelper& helper)
{
FASTLOG1(FLog::DataStore, "Queue suze: %u", queue.size());
if (request.isKeyThrottled(Time::nowFast()) || !helper.checkAndReduceBudget())
{
FASTLOG1F(FLog::DataStore, "Throttling, budget: %f", helper.getBudget());
if (queue.size() < (unsigned)DFInt::DataStoreMaxThrottledQueue)
{
queue.push_back(request);
return true;
}
else
return false;
}
FASTLOG1F(FLog::DataStore, "Not throttling, budget after: %f, queue size: %f", helper.getBudget());
request.execute(this);
return true;
}
bool DataStoreService::queueOrExecuteGet(DataStore* source, HttpRequest& request)
{
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
AYAASSERT(service);
if (!service)
return false;
return service->queueOrExecuteRequest(request, service->throttledGets, service->throttleCounterGets);
}
bool DataStoreService::queueOrExecuteGetSorted(DataStore* source, HttpRequest& request)
{
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
AYAASSERT(service);
if (!service)
return false;
return service->queueOrExecuteRequest(request, service->throttledGetSorteds, service->throttleCounterGetSorteds);
}
bool DataStoreService::queueOrExecuteSet(DataStore* source, HttpRequest& request)
{
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
AYAASSERT(service);
if (!service)
return false;
return service->queueOrExecuteRequest(request, service->throttledSets, service->throttleCounterSets);
}
bool DataStoreService::queueOrExecuteOrderedSet(DataStore* source, HttpRequest& request)
{
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
AYAASSERT(service);
if (!service)
return false;
return service->queueOrExecuteRequest(request, service->throttledOrderedSets, service->throttleCounterOrderedSets);
}
void DataStoreService::executeThrottledRequests(std::list<HttpRequest>& queue, BudgetedThrottlingHelper& helper)
{
if (queue.size() > 0)
FASTLOG2F(FLog::DataStore, "Executing throttled requests, size: %f, budget: %f", (float)queue.size(), helper.getBudget());
Time now = Time::nowFast();
for (std::list<HttpRequest>::iterator it = queue.begin(); it != queue.end();)
{
if (it->isKeyThrottled(now))
{
++it;
continue;
}
if (!helper.checkAndReduceBudget())
break;
it->execute(this);
it = queue.erase(it);
}
}
void DataStoreService::executeThrottledRequests()
{
executeThrottledRequests(throttledGets, throttleCounterGets);
executeThrottledRequests(throttledGetSorteds, throttleCounterGetSorteds);
executeThrottledRequests(throttledSets, throttleCounterSets);
executeThrottledRequests(throttledOrderedSets, throttleCounterOrderedSets);
}
void DataStoreService::setUrlEncodingDisabled(bool disabled)
{
if (disabled != disableUrlEncoding)
{
disableUrlEncoding = disabled;
}
}
void DataStoreService::refetchCachedKeys()
{
int budget = (DFInt::DataStoreRefetchFixedRequestLimit + DFInt::DataStoreRefetchPerPlayerRequestLimit * getPlayerNum()) *
DFInt::DataStoreFetchFrequenceInSeconds / 60;
FASTLOG1(FLog::DataStore, "Initial budget for refetch: %i", budget);
if (legacyDataStore)
legacyDataStore->refetchCachedKeys(&budget);
for (DataStores::iterator it = dataStores.begin(); it != dataStores.end(); ++it)
{
if (budget < 0)
break;
it->second->refetchCachedKeys(&budget);
}
for (DataStores::iterator it = orderedDataStores.begin(); it != orderedDataStores.end(); ++it)
{
if (budget < 0)
break;
it->second->refetchCachedKeys(&budget);
}
if (budget >= 0)
{
FASTLOG1(FLog::DataStore, "Refetch budget is %i, so reset refetch on all DataStores", budget);
if (legacyDataStore)
legacyDataStore->resetRefetch();
for (DataStores::iterator it = dataStores.begin(); it != dataStores.end(); ++it)
{
it->second->resetRefetch();
}
for (DataStores::iterator it = orderedDataStores.begin(); it != orderedDataStores.end(); ++it)
{
it->second->resetRefetch();
}
}
}
} // namespace Aya

Some files were not shown because too many files have changed in this diff Show More